If I set my player's "hspeed" variable (horizontal speed) to a slow enough speed,
then when he goes over a gap of 16x16 blocks, he will fall through as planned.
However if I set the "hspeed" variable high enough, he will go so fast that he hovers over it.
All though it works when I set it to a slow speed, it's too slow.
Here's an example:
http://img199.imageshack.us/img199/2685/demondj.png
Here's my collision code (loops through a list of blocks):
for(unsigned int i = 0; i<blocks.size(); i++){
Block &b = blocks.at(i);
if(!b.passable){
//Check if we are on the sides
if(y + height + vspeed >= b.worldY+2 && y + vspeed <= b.worldY+b.height)
{
//Right side
if(x + hspeed <= b.worldX+b.width+1 && x + hspeed > b.worldX+b.width + hspeed-1)
{
x = b.worldX + b.width; hspeed = 0;
}
//Left side
if(x + width + hspeed >= b.worldX +1 && x + width + hspeed <= b.worldX + hspeed + 1)
{
x = b.worldX - width; hspeed = 0;
}
}
//Check if we are on the top or the bottom
if(x + width + hspeed >= b.worldX+1 && x + hspeed <= b.worldX+b.width-1)
{
if(y + height + vspeed >= b.worldY && y + height + vspeed <= b.worldY + vspeed + 1 && jumpstate=="falling")
{
y = b.worldY - height; jumpstate.assign("ground"); vspeed = 0;
}
if(y + vspeed <= b.worldY + b.height && y + vspeed >= b.worldY + b.height + vspeed + 1 && jumpstate=="jumping")
{
y = b.worldY + b.height; jumpstate.assign("falling"); vspeed = 0;
}
}
}
}
Do I have a problem in my collision code?
Another problem is when I hit the bottom of a block, it's a bit glitchy.
He's supposed to bounce right off it, and he does, but he jitters if he's moving and he hits the bottom of a block.
Ok, a number of things here:
1) Too many magic numbers. Not sure exactly what the +2, +1, etc. truely mean. I'd like to see those as constants. Also, I'm paranoid about order of operations so I would surround everything in parenthesis, just in case :)
2) Unsure of what types your vars are. Assuming floats, but they feel like ints.
3) The bigger problem, your code as designed will not properly handle "large" velocity well. Something traveling faster than the width/height of your collidable area won't be reliably detected. Nor can you easily with this type of collision code. This is one side effect you are already seeing and my guess is the fudge factor adds you have contribute to the rest.
Consider using a vector based collision system instead. Treat each edge of the box as a line vector. You can then do very simple math to determine if a point starts one one side of the line and ends on the other (sign of cross product) and sign of dot will tell whether the point was within the line segment. Also, it allows your collision objects to be any shape and even makes the math extendable to 3D rather easily. This sort of system also is parallel processing friendlier as well.
Related
Hi I am trying to make 2d collision but I dont get it to work
i am doing :
if (collider.posX + collider.sizeX >= obstacle.posX && obstacle.posX + obstacle.sizeX >= collider.posX && collider.posY + collider.sizeY >= obstacle.posY)
the obstacle cannot move on the y axis so this is why i only check for
collider.posY + 50 >= obstacle.posY
the collision works but they are only start when the obstacle is half in the collider
What about:
if( obstacle.posX >= collider.posX &&
obstacle.posX <= collider.posX + collider.sizeX &&
obstacle.posY >= collider.posY &&
obstacle.posY <= collider.posY + collider.sizeY
)
This code should check if obstacle point intersects collider rectangle.
This is better solution because it detects overlapping between 2 rectangles:
if ( obstacle.posX <= collider.posX + collider.sizeX &&
obstacle.posX + obstacle.sizeX >= collider.posX &&
obstacle.posY <= collider.posY + collider.sizeY &&
obstacle.posY + obstacle.sizeY >= collider.posY )
You're running into logical errors, seen by the following code:
collider.posX + collider.sizeX >= obstacle.posX
Suppose the two colliding objects are circles with their positions being their center points. The first object is moving in the positive X direction while the second is static (stationary). You need to check if the first object's X position plus its radius (not diameter) touches the second object's X position minus its radius.
Example code:
collider.posX + (collider.sizeX / 2) >= obstacle.posX - (obstacle.sizeX / 2)
Note: The above inequality assumes that collider.sizeX represents the diameter of collider.
Presumably later on you'll also need to check if the first object is passed the second object. Since the above inequality only accounts for the first object having an increasing X position and its X position being less than the second object's X position.
I'm currently using this collision code:
( // tx = other x, tex = other end x etc.
( // horizontal collision
(xpos >= tx && xpos <= tex)
||
((xpos + w) <= tex && (xpos + w) >= tx)
)
&&
( // vertical collision
(ypos >= ty && ypos <= tey)
||
((ypos + h) <= tey && (ypos + h) >= ty)
)
)
However, it only detects if the top left pixel, the top right pixel, the bottom left pixel, or the bottom right pixel is within the rectangle to test. I don't know what to do, it already took forever to get to the current collision testing method I'm using, what can I change to fix this problem?
Do I understand correctly that one rectangle extends horizontally from xpos to xpos + w and vertically from ypos to ypos + h, and the other extends horizontally from tx to tex and vertically from ty to tey? With w, h, tex − tx, and tey − ty all being positive?
If so, then you can write:
xpos <= tex && xpos + w >= tx // horizontal collision
&& ypos <= tey && ypos + h >= ty // vertical collision
Alternatively, you may find it easier to reason about if you calculate the conditions whereby they don't collide, and just add a !:
!(
xpos > tex // first rectangle entirely to right of second
|| xpos + w < tx // first rectangle entirely to left of second
|| ypos > tey // first rectangle entirely above second
|| ypos + h < ty // first rectangle entirely below second
)
(The two versions are equivalent, due to one of De Morgan's laws.)
I'm trying to make obstacles that stops the player after colliding. Simply, the walls.
I've created collision detection, like this:
if (x > (wall.x1 - wall.boundX) && x < (wall.x + wall.boundX) &&
y >(wall.y1 - wall.boundY) && y < (wall.y + wall.boundY))
{
x = 0;
y = 0;
}
but, as you can see, it just returns my player object to (0,0) on map (by the way, I'm using mouse to control my character). I want it to stop right where he collides and to be unable to move through this wall.
How can I do that?
You will need to separate your if statement to handle each wall separately, and then set the x or y position to the bounds of the wall:
if (x > wall.x1 - wall.boundX)
x = wall.x1 - wall.boundX;
if (x < (wall.x + wall.boundX))
x = wall.x + wall.boundX;
// etc
No matter how far the mouse attempts to move past the bounds of the wall, it will just remain at that position.
Well I've got a 2D box collision code that basically loops through every block in a list called "Blocks" and it checks if I'm near the sides and whatnot.
It works very well except for the bottom of the block. When I'm jumping up towards the bottom I want my player to simply "bounce" off. It does this, but it is very glitchy. It's hard to explain so I was hoping you guys could possibly spot out what's wrong with my bottom collision code.
Here's the entire thing (this is ran in a loop):
for(unsigned int i = 0; i<blocks.size(); i++){
Block &b = blocks.at(i);
if(!b.passable==true){
//Check if we are on the sides
if(y + height + vspeed >= b.worldY+2 && y + vspeed <= b.worldY+b.height)
{
//Right side
if(x + hspeed <= b.worldX+b.width-1 && x + hspeed > b.worldX+b.width + hspeed-2)
{
x = b.worldX + b.width; hspeed = 0;
}
//Left side
if(x + width + hspeed >= b.worldX +1 && x + width + hspeed <= b.worldX + hspeed + 2)
{
x = b.worldX - width; hspeed = 0;
}
}
//Check if we are on the top or the bottom
if(x + width + hspeed >= b.worldX+2 && x + hspeed <= b.worldX+b.width-2)
{
if(y + height + vspeed >= b.worldY && y + height + vspeed <= b.worldY + vspeed + 1 && jumpstate=="falling")
{
y = b.worldY - height; jumpstate.assign("ground"); vspeed = 0;
}
if(y + vspeed <= b.worldY + b.height && y + vspeed >= b.worldY + b.height + vspeed - 1 && jumpstate=="jumping")
{
y = b.worldY + b.height; jumpstate.assign("falling"); vspeed = 0;
}
}
}
}
I'm not sure you'll get it to work like this. I'll explain a solution that I thought of for some collision detection and bouncing that works smoothly. Record the time interval from when you last checked collisions and adjusted position. If Xplayer+Vplayer*deltaT>Xtarget (if the player would overlap the target) then compute the actual time when it would be touching the target deltaTtouch=(Xtarget-Xplayer)/Vplayer. Now bounce the player back Xplayer=Xtarget-Vplayer*(deltaT-deltaTtouch). You'll have to figure out all the cases when moving forward, backward, up, down.
LE: You can also implement gravity, this involves solving a quadratic equation for finding out the deltaTtouch.
Well I can see a few little details:
1) In this line the hspeed when checking the old position is quite redundant, best to remove it for clarity.
if(x + hspeed <= b.worldX+b.width-1 && x + hspeed > b.worldX+b.width + hspeed-2)
Becomes:
if(x + hspeed <= b.worldX + b.width - 1 && x > b.worldX + b.width - 2)
Same goes for the other similar lines.
2) The small offsets of -2 and -1 are a bit confusing I presume you are trying to achieve a small buffer such that a slight amount of overlap is required. Especially this line where you have used a < instead of the <= you have used every else:
if(x + hspeed <= b.worldX+b.width-1 && x + hspeed > b.worldX+b.width + hspeed-2)
To keep consistancy with the rest of the program I would probably write:
if(x + hspeed <= b.worldX + b.width - 1 && x >= b.worldX + b.width - 1)
3) You seem to be missing a few of your small offsets in the vert checks:
So this check first:
if(y + height + vspeed >= b.worldY && y + height + vspeed <= b.worldY + vspeed + 1 && jumpstate=="falling")
You seem to have forgotten your small offset:
if(y + height + vspeed >= b.worldY + 1 && y + height <= b.worldY + 1 && jumpstate=="falling")
Then the second check:
if(y + vspeed <= b.worldY + b.height && y + vspeed >= b.worldY + b.height + vspeed - 1 && jumpstate=="jumping")
Offset again:
if(y + vspeed <= b.worldY + b.height - 1 && y >= b.worldY + b.height - 1 && jumpstate=="jumping")
4) You will need to be very careful that vspeed and jumpstate always remain in sync as the sign of vspeed needs to always match jumpstate or you will miss collisions. I imagine this might be where your problem is coming from.
5) In both directions if speed exceeds the block size you will skip collision detection and fire through the block.
if(y + height + vspeed >= b.worldY+2 && y + vspeed <= b.worldY+b.height)
The second check in these lines will be false if speed is higher than b.height and the y coords are similar.
Hope that helps some.
Well I've got a 2D box collision code that basically loops through every block in a list called "Blocks" and it checks if I'm near the sides and whatnot.
It works very well except for the bottom of the block. When I'm jumping up towards the bottom I want my player to simply "bounce" off. It does this, but it is very glitchy.
It's hard to explain so I was hoping you guys could possibly spot out what's wrong with my bottom collision code.
Here's the entire thing:
for(unsigned int i = 0; i<blocks.size(); i++){
Block &b = blocks.at(i);
if(!b.passable==true){
//Check if we are on the sides
if(y + height + vspeed >= b.worldY+2 && y + vspeed <= b.worldY+b.height)
{
//Right side
if(x + hspeed <= b.worldX+b.width-1 && x + hspeed > b.worldX+b.width + hspeed-2)
{
x = b.worldX + b.width; hspeed = 0;
}
//Left side
if(x + width + hspeed >= b.worldX +1 && x + width + hspeed <= b.worldX + hspeed + 2)
{
x = b.worldX - width; hspeed = 0;
}
}
//Check if we are on the top or the bottom
if(x + width + hspeed >= b.worldX+2 && x + hspeed <= b.worldX+b.width-2)
{
if(y + height + vspeed >= b.worldY && y + height + vspeed <= b.worldY + vspeed + 1 && jumpstate=="falling")
{
y = b.worldY - height; jumpstate.assign("ground"); vspeed = 0;
}
if(y + vspeed <= b.worldY + b.height && y + vspeed >= b.worldY + b.height + vspeed - 1 && jumpstate=="jumping")
{
y = b.worldY + b.height; jumpstate.assign("falling"); vspeed = 0;
}
}
}
}
My guess is that you should set vspeed = -vspeed; instead of vspeed = 0. Fully elastic bouncing means that the velocity gets mirrored in the box's side.
If you set it to zero, depending on the order in which you perform your updates, you might not move during a frame. And since you use <= and >= for bounds checking, you'll still be inside the box on the next frame, invoke the bouncing behaviour again, and get stuck with your head glued to the block.
My guess at your problem would be negative numbers in your inequality checks. For example, when going down, I guess vspeed will be negative. Depending on where you have set the origin of your box, this could mean that the condition
y + vspeed <= b.worldY + b.height
is always true.
As an aside, by adding a distance to a speed, you have assumed a magic dimension for the unit time. This makes your code brittle because if you change this magic number, then your equations will be wrong.
Try,
y + vspeed*unit_time <= b.worldY + b.height
If you want to handle the units at compile-time (and not pay for that multiplication), then use boost.units.
Additionally,
x + hspeed > b.worldX+b.width + hspeed-2
can be simplified to
x > b.worldX+b.width-2
And the significance of this magic number 2 is anyones guess. (why for instance do you have
//Check if we are on the sides
if(y + vspeed <= b.worldY+b.height)
but have a -2 in
//Check if we are on the top or the bottom
if( x + hspeed <= b.worldX+b.width-2)
)