new to Raylib and trying to create a Super Mario clone using Go. I am using rl.CheckCollisionRecs to detect collision between the player and a pipe object, which is using the AABB collision method to detect if a player is hitting the X or Y axis first. The problem I am facing is that when the player collides with the Y, the player's Y position is set to match the pipes y position plus its height as expected. However, the player does not fall back to the ground when they leave the collider. How can I code an action when the rectangles are now longer colliding?
When they player leaves the collider, I want them to fall back to the ground.
func drawColliders() {
for _, current_Pipe := range pipes {
rl.DrawRectangle(current_Pipe.posX, current_Pipe.posY, current_Pipe.width, current_Pipe.height, current_Pipe.Color)
if rl.CheckCollisionRecs(playerDest, rl.NewRectangle(float32(current_Pipe.posX), float32(current_Pipe.posY), float32(current_Pipe.width), float32(current_Pipe.height))) {
var xDistance float32
var yDistance float32
var dx float32
var dy float32
if playerDest.X < float32(current_Pipe.posX) {
dx = float32(current_Pipe.posX) - playerDest.Width
isColliding = true
} else if playerDest.X > float32(current_Pipe.posX-current_Pipe.width) {
dx = float32(current_Pipe.posX) + float32(current_Pipe.width)
isColliding = true
}
if playerDest.Y < float32(current_Pipe.posY) {
dy = float32(current_Pipe.posY) - (playerDest.Y + playerDest.Height)
isColliding = true
} else if playerDest.Y > float32(current_Pipe.posY) {
dy = float32(current_Pipe.posY) + (float32(current_Pipe.posY) + float32(current_Pipe.height))
isColliding = true
}
xDistance = dx
yDistance = dy
// fmt.Println(xDistance, yDistance)
var xAxisTimeToCollide float32 = float32(math.Abs(float64(xDistance) / float64(velocityX)))
var yAxisTimeToCollide float32 = float32(math.Abs(float64(yDistance) / float64(velocityY)))
// fmt.Println("X Time: ", xAxisTimeToCollide, " | Y Time: ", yAxisTimeToCollide)
if xAxisTimeToCollide < yAxisTimeToCollide {
// fmt.Println("Collision on the X axis")
if playerDest.X < float32(current_Pipe.posX) {
playerDest.X = float32(current_Pipe.posX) - playerDest.Width
} else if playerDest.X > float32(current_Pipe.posX-current_Pipe.width) {
playerDest.X = float32(current_Pipe.posX) + float32(current_Pipe.width)
}
} else {
// fmt.Println("Collsion on the Y axis")
playerGrounded = true
playerJumping = false
velocityY = 0
playerDest.Y = float32(current_Pipe.posY) - playerDest.Height
}
}
}
Problem solved. CheckCollisionRecs is checking if there is a collision, but does not check when a collision ends.
My solution was that when a collision was detected, I stored the static objects starting coordinate and ending coordinate (startPoint = obj.positionX, endPoint = obj.positionX + obj.width). Then in my update function, I have an if statement that checks if the players current X position is more than or less than the static objects start or end point.
n= -1;
Color = IIf((High < Ref(High,n) & Low > Ref(Low,n)), colorRed , colorWhite);
Plot( Close, "Colored Price", Color, styleBar );
Red arrow pointing bar is mother bar and blue arrow pointing white bar is inside bar within motherbar range.
so i'm trying to compare current bar with mother bar, if current bar is inside the range of mother bar is compare immediate previous bar but i tried to add decrements n value if code condition true but its not working don't know why. Please refer image too for better clarity
n= -1;
Color = IIf((High < Ref(High,n) & Low > Ref(Low,n)), colorRed && n= n--, colorWhite);
Plot( Close, "Colored Price", Color, styleBar );
I found it easier to write out the for loops
function InsideBarColor() {
local state;
local isInsideBar;
local index;
local insideHigh;
local insideLow;
local priceColor;
state = 0;
isInsideBar = Inside();
priceColor = colorDefault;
for(index = 1; index < BarCount; index += 1) {
if (state == 0 && isInsideBar[index] == True) {
// Record the High/Low of the first inside bar.
insideHigh = High[index-1];
insideLow = Low[index-1];
state = 1;
priceColor[index] = colorRed;
} else if(state == 1) {
if (High[index] > insideHigh || Low[index] < insideLow) {
// Price broke out of range
priceColor[index] = colorWhite;
state = 0;
} else {
// Price still inside range
priceColor[index] = colorBlue;
}
} else {
priceColor[index] = priceColor[index -1];
}
}
return priceColor;
}
colors = InsideBarColor();
Given the structure:
structure box_dimensions:
int? left
int? right
int? top
int? bottom
point? top_left
point? top_right
point? bottom_left
point? bottom_right
point? top_center
point? bottom_center
point? center_left
point? center_right
point? center
int? width
int? height
rectangle? bounds
where each field can be defined or not.
How would you implement the function check_and_complete(box_dimensions) ?
That function should return an error if there is not enough fields defined to describe a box, or too many.
If input is consistent, it should compute the undefined fields.
You can describe a box by its center, width and height, or top_left and bottom_right corners, etc
The only solution I can think of contains way to many if-elses. I'm sure there's a smart way to do it.
EDIT
If you wonder how I end up with a structure like that, here is why :
I'm toying with the idea of a "layout by constraints" system:
User define a bunch of boxes, and for each box define a set of constraints like "box_a.top_left = box_b.bottom_right", "box_a.width = box_b.width / 2".
The real structure fields are actually expression AST, not values.
So I need to check if a box is "underconstrained" or "overconstrained", and if it's ok, create the missing expression AST from the given ones.
Yes, certainly there will be too many if-elses.
Here's my attempt to keep them reasonably organized:
howManyLefts = 0
if (left is set) { realLeft = left; howManyLefts++; }
if (top_left is set) { realLeft = top_left.left; howManyLefts++; }
if (bottom_left is set) { realLeft = bottom_left.left; howManyLefts++; }
if (center_left is set) { realLeft = center_left.left; howManyLefts++; }
if (bounds is set) { realLeft = bounds.left; howManyLefts++; }
if (howManyLefts > 1) return error;
Now, repeat that code block for center, right and width.
Now you end up with howManyLefts, howManyCenters, howManyRights and howManyWidths, all of them being either zero or one, depending on whether the value was provided or not. You need exactly two values set and two unset, so:
if (howManyLefts + howManyRights + howManyCenters + howManyWidths != 2) return error
if (howManyWidths == 0)
{
// howManyWidths is 0, so we look for the remaining 0 and assume the rest is 1s
if (howManyCenters == 0)
{ realWidth = realRight - realLeft; realCenter = (realRight + realLeft) / 2; }
else if (howManyLefts == 0)
{ realWidth = 2 * (realRight - realCenter); realLeft = realRight - realWidth; }
else
{ realWidth = 2 * (realCenter - realLeft); realRight = realLeft + realWidth; }
}
else
{
// howManyWidths is 1, so we look for the remaining 1 and assume the rest is 0s
if (howManyCenters == 1)
{ realLeft = realCenter - realWidth / 2; realRight = realCenter + realWidth / 2; }
else if (howManyLefts == 1)
{ realRight = realLeft + realWidth; realCenter = (realRight + realLeft) / 2; }
else
{ realLeft = realRight - realWidth; realCenter = (realRight + realLeft) / 2; }
}
Now, repeat everything for the vertical axis (i.e. replacing { left, center, right, width } with { top, center, bottom, height }).
I am trying to load in a .obj-File and draw it with the help of glDrawElements.
Now, with glDrawArrays everything works perfectly, but it is - of course - inefficient.
The problem I have right now is, that an .obj-file uses multiple index-buffers (for each attribute) while OpenGL may only use one. So I need to map them accordingly.
There are a lot of pseudo-algorithms out there and I even found a C++ implementation. I do know quite a bit of C++ but strangely neither helped me with my implementation in Scala.
Let's see:
private def parseObj(path: String): Model =
{
val objSource: List[String] = Source.fromFile(path).getLines.toList
val positions: List[Vector3] = objSource.filter(_.startsWith("v ")).map(_.split(" ")).map(v => new Vector3(v(1).toFloat,v(2).toFloat,v(3).toFloat))//, 1.0f))
val normals: List[Vector4] = objSource.filter(_.startsWith("vn ")).map(_.split(" ")).map(v => new Vector4(v(1)toFloat,v(2).toFloat, v(3).toFloat, 0.0f))
val textureCoordinates: List[Vector2] = objSource.filter(_.startsWith("vt ")).map(_.split(" ")).map(v => new Vector2(v(1).toFloat, 1-v(2).toFloat)) // TODO 1-y because of blender
val faces: List[(Int, Int, Int)] = objSource.filter(_.startsWith("f ")).map(_.split(" ")).flatten.filterNot(_ == "f").map(_.split("/")).map(a => ((a(0).toInt, a(1).toInt, a(2).toInt)))
val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))
val f: List[(Vector3, Vector2, Vector4)] = for(face <- faces) yield((positions(face._1-1), textureCoordinates(face._2-1), normals(face._3-1)))
println(f.mkString("\n"))
val indices: List[Int] = faces.map(f => f._1-1) // Wrong!
new Model(vertices.toArray, indices.toArray)
}
The val indices: List[Int] was my first naive approach and of course is wrong. But let's start at the top:
I load in the file and go through it. (I assume you know how an .obj-file is made up)
I read in the vertices, texture-coordinates and normals. Then I come to the faces.
Now, each face in my example has 3 values v_x, t_y, n_z defining the vertexAtIndexX, textureCoordAtIndexY, normalAtIndexZ. So each of these define one Vertex while a triple of these (or one line in the file) defines a Face/Polygon/Triangle.
in val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1))) I actually try to create Vertices (a case-class that currently only holds positions and texture-coordinates and neglects normals for now)
The real problem is this line:
val indices: List[Int] = faces.map(f => f._1-1) // Wrong!
To get the real indices I basically need to do this instead of
val vertices: List[Vertex] = for(face <- faces) yield(new Vertex(positions(face._1-1), textureCoordinates(face._2-1)))
and
val indices: List[Int] = faces.map(f => f._1-1) // Wrong!
Pseudo-Code:
Iterate over all faces
Iterate over all vertices in a face
Check if we already have that combination of(position, texturecoordinate, normal) in our newVertices
if(true)
indices.put(indexOfCurrentVertex)
else
create a new Vertex from the face
store the new vertex in the vertex list
indices.put(indexOfNewVertex)
Yet I'm totally stuck. I've tried different things, but can't come up with a nice and clean solution that actually works.
Things like:
val f: List[(Vector3, Vector2, Vector4)] = for(face <- faces) yield((positions(face._1-1), textureCoordinates(face._2-1), normals(face._3-1)))
and trying to f.distinct are not working, because there is nothing to distinct, all the entries there are unique, which totally makes sense if I look at the file and yet that's what the pseudo-code tells me to check.
Of course then I would need to fill the indices accordingly (preferably in a one-liner and with a lot of functional beauty)
But I should try to find duplicates, so... I'm kind of baffled. I guess I mix up the different "vertices" and "positions" too much, with all the referencing.
So, am I thinking wrong, or is the algorithm/thinking right and I just need to implement this in nice, clean (and actually working) Scala code?
Please, enlighten me!
As per comments, I made a little update:
var index: Int = 0
val map: mutable.HashMap[(Int, Int, Int), Int] = new mutable.HashMap[(Int, Int, Int), Int].empty
val combinedIndices: ListBuffer[Int] = new ListBuffer[Int]
for(face <- faces)
{
val vID: Int = face._1-1
val nID: Int = face._2-1
val tID: Int = face._3-1
var combinedIndex: Int = -1
if(map.contains((vID, nID, tID)))
{
println("We have a duplicate, wow!")
combinedIndex = map.get((vID, nID, tID)).get
}
else
{
combinedIndex = index
map.put((vID, nID, tID), combinedIndex)
index += 1
}
combinedIndices += combinedIndex
}
where faces still is:
val faces: List[(Int, Int, Int)] = objSource.filter(_.startsWith("f ")).map(_.split(" ")).flatten.filterNot(_ == "f").map(_.split("/")).map(a => ((a(0).toInt, a(1).toInt, a(2).toInt)))
Fun fact I'm still not understanding it obviously, because that way I never ever get a duplicate!
Meaning that combinedIndices at the end just holds the natural numbers like:
ListBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...)
This is javascript (sorry not scala) but it's commented and shoul be fairly easy to convert.
// bow-tie
var objString = "v 0 0 0\nv 1 1 0\nv 1 -1 0\nv -1 1 0\nv -1 -1 0\n" +
"vt 0 .5\nvt 1 1\nvt 1 0\n" +
"vn 0 0 1\n" +
"f 1/1/1 2/2/1 3/3/1\nf 1/1/1 4/2/1 5/3/1";
// output indices should be [0, 1, 2, 0, 3, 4]
// parse the file
var lines = objString.split("\n");
var data = lines.map(function(line) { return line.split(" "); });
var v = [];
var t = [];
var n = [];
var f = [];
var indexMap = new Map(); // HashMap<face:string, index:integer>
var nextIndex = 0;
var vertices = [];
var indices = [];
// fill vertex, texture and normal arrays
data.filter(function(d) { return d[0] == "v"; }).forEach(function(d) { v.push([parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]); });
data.filter(function(d) { return d[0] == "vt"; }).forEach(function(d) { t.push([parseFloat(d[1]), parseFloat(d[2])]); });
data.filter(function(d) { return d[0] == "vn"; }).forEach(function(d) { n.push([parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])]); });
//
console.log("V", v.toString());
console.log("T", t.toString());
console.log("N", n.toString());
// create vertices and indices arrays by parsing faces
data.filter(function(d) { return d[0] == "f"; }).forEach(function(d) {
var f1 = d[1].split("/").map(function(d) { return parseInt(d)-1; });
var f2 = d[2].split("/").map(function(d) { return parseInt(d)-1; });
var f3 = d[3].split("/").map(function(d) { return parseInt(d)-1; });
// 1
if(indexMap.has(d[1].toString())) {
indices.push(indexMap.get(d[1].toString()));
} else {
vertices = vertices.concat(v[f1[0]]).concat(t[f1[1]]).concat(n[f1[2]]);
indexMap.set(d[1].toString(), nextIndex);
indices.push(nextIndex++);
}
// 2
if(indexMap.has(d[2].toString())) {
indices.push(indexMap.get(d[2].toString()));
} else {
vertices = vertices.concat(v[f2[0]]).concat(t[f2[1]]).concat(n[f2[2]]);
indexMap.set(d[2].toString(), nextIndex);
indices.push(nextIndex++);
}
// 3
if(indexMap.has(d[3].toString())) {
indices.push(indexMap.get(d[3].toString()));
} else {
vertices = vertices.concat(v[f3[0]]).concat(t[f3[1]]).concat(n[f3[2]]);
indexMap.set(d[3].toString(), nextIndex);
indices.push(nextIndex++);
}
});
//
console.log("Vertices", vertices.toString());
console.log("Indices", indices.toString());
The output
V 0,0,0,1,1,0,1,-1,0,-1,1,0,-1,-1,0
T 0,0.5,1,1,1,0
N 0,0,1
Vertices 0,0,0,0,0.5,0,0,1,1,1,0,1,1,0,0,1,1,-1,0,1,0,0,0,1,-1,1,0,1,1,0,0,1,-1,-1,0,1,0,0,0,1
Indices 0,1,2,0,3,4
The JSFiddle http://jsfiddle.net/8q7jLvsq/2
The only thing I'm doing ~differently is using the string hat represents one of the parts of a face as the key into my indexMap (ex: "25/32/5").
EDIT JSFiddle http://jsfiddle.net/8q7jLvsq/2/ this version combines repeated values for vertex, texture and normal. This optimizes OBJ files that repeat the same values values making every face unique.
// bow-tie
var objString = "v 0 0 0\nv 1 1 0\nv 1 -1 0\nv 0 0 0\nv -1 1 0\nv -1 -1 0\n" +
"vt 0 .5\nvt 1 1\nvt 1 0\nvt 0 .5\nvt 1 1\nvt 1 0\n" +
"vn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\nvn 0 0 1\n" +
"f 1/1/1 2/2/2 3/3/3\nf 4/4/4 5/5/5 6/6/6";
// output indices should be [0, 1, 2, 0, 3, 4]
// parse the file
var lines = objString.split("\n");
var data = lines.map(function(line) { return line.split(" "); });
var v = [];
var t = [];
var n = [];
var f = [];
var vIndexMap = new Map(); // map to earliest index in the list
var vtIndexMap = new Map();
var vnIndexMap = new Map();
var indexMap = new Map(); // HashMap<face:string, index:integer>
var nextIndex = 0;
var vertices = [];
var indices = [];
// fill vertex, texture and normal arrays
data.filter(function(d) { return d[0] == "v"; }).forEach(function(d, i) {
v[i] = [parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])];
var key = [d[1], d[2], d[3]].toString();
if(!vIndexMap.has(key)) {
vIndexMap.set(key, i);
}
});
data.filter(function(d) { return d[0] == "vt"; }).forEach(function(d, i) {
t[i] = [parseFloat(d[1]), parseFloat(d[2])];
var key = [d[1], d[2]].toString();
if(!vtIndexMap.has(key)) {
vtIndexMap.set(key, i);
}
});
data.filter(function(d) { return d[0] == "vn"; }).forEach(function(d, i) {
n[i] = [parseFloat(d[1]), parseFloat(d[2]), parseFloat(d[3])];
var key = [d[1], d[2], d[3]].toString();
if(!vnIndexMap.has(key)) {
vnIndexMap.set(key, i);
}
});
//
console.log("V", v.toString());
console.log("T", t.toString());
console.log("N", n.toString());
// create vertices and indices arrays by parsing faces
data.filter(function(d) { return d[0] == "f"; }).forEach(function(d) {
var f1 = d[1].split("/").map(function(d, i) {
var index = parseInt(d)-1;
if(i == 0) index = vIndexMap.get(v[index].toString());
else if(i == 1) index = vtIndexMap.get(t[index].toString());
else if(i == 2) index = vnIndexMap.get(n[index].toString());
return index;
});
var f2 = d[2].split("/").map(function(d, i) {
var index = parseInt(d)-1;
if(i == 0) index = vIndexMap.get(v[index].toString());
else if(i == 1) index = vtIndexMap.get(t[index].toString());
else if(i == 2) index = vnIndexMap.get(n[index].toString());
return index;
});
var f3 = d[3].split("/").map(function(d, i) {
var index = parseInt(d)-1;
if(i == 0) index = vIndexMap.get(v[index].toString());
else if(i == 1) index = vtIndexMap.get(t[index].toString());
else if(i == 2) index = vnIndexMap.get(n[index].toString());
return index;
});
// 1
if(indexMap.has(f1.toString())) {
indices.push(indexMap.get(f1.toString()));
} else {
vertices = vertices.concat(v[f1[0]]).concat(t[f1[1]]).concat(n[f1[2]]);
indexMap.set(f1.toString(), nextIndex);
indices.push(nextIndex++);
}
// 2
if(indexMap.has(f2.toString())) {
indices.push(indexMap.get(f2.toString()));
} else {
vertices = vertices.concat(v[f2[0]]).concat(t[f2[1]]).concat(n[f2[2]]);
indexMap.set(f2.toString(), nextIndex);
indices.push(nextIndex++);
}
// 3
if(indexMap.has(f3.toString())) {
indices.push(indexMap.get(f3.toString()));
} else {
vertices = vertices.concat(v[f3[0]]).concat(t[f3[1]]).concat(n[f3[2]]);
indexMap.set(f3.toString(), nextIndex);
indices.push(nextIndex++);
}
});
//
console.log("Vertices", vertices.toString());
console.log("Indices", indices.toString());