Problem:
Problem http://img684.imageshack.us/img684/5486/prob1b.png
Problem http://img810.imageshack.us/img810/3157/prob3.png
Green block is the player and the gray block is the static brick
When jumping up against a tile the player is pushed to the left/right when it stands near the edge of the colliding tile. This probably happens because the jump velocity up is bigger than the x-movement (which may even be 0) and thus the player is corrected over the x-axis...
The pseudo-code goes like this:
1: get colliding tiles around player
2: foreach colliding tile:
{
2a: get intersection depth
2b: correct player location with the smallest correction-axis
(example: if (Math.Abs(X) > Math.Abs(Y) Then correct Y value. Else
correct X value)
// Below is some other pseudo-code which should not be part of the problem:
2c: if collision is with the top side of the player: cancel any jumps
2d: if collision is with the bottom side of the player:
IsStandingOnGround = true;
}
Some Code:
foreach (Stone stone in collidingStones)
{
#region Collision Response
if (DrawRect.Intersects(stone.CollisionRect)) // DrawRect is the players collision rectangle
{
//warning bug is in this if-statement. causes the player to be adjusted and go trough neighboar tiles and such
Vector2 correction = Collision.CalculateMinimumTranslationDistance(DrawRect, stone.CollisionRect);
if (Math.Abs(correction.X) > Math.Abs(correction.Y))
{
Location += new Vector2(correction.X, 0);
}
else
{
Location += new Vector2(0, correction.Y);
}
Interesting question, I was developing a game in Delphi some years ago and I ran into the same problems. Couple of points:
You should correct both X and Y on collision. Correcting only X or Y will look unnatural with high velocities (more than ~3px/update).
The kind of collision detection that you are doing would work with rectangularly-filled tiles, but if you had irregular tiles (like a triangle for a slope-section) then it would be impossible to calculate the correction values for collision.
In the end I made an engine in which each tile had the bitmap (the actual visible graphics) and something I called a freemap, which was a black&white bitmap which represented map of free space within a tile. Then I wrote collision functions that operated first in the tile-space (to determine the tiles that collide with the player) then in the bitmap of the tile.
When moving the player you calculate the next point based on the player velocity, and then you test each point along the line between the current position and the calculated next position. If on that line you run into an obstacle then you stop the player at that point.
Related
I'm currently working on a 2D Game Engine in C# using GDI+ and have gotten to the point where I want to add simple collision detection.
So far, I can check whether or not my player has intersected with another game object using the code below:
public static bool IntersectGameObject(GameObject a, GameObject b)
{
Rectangle rect1 = new Rectangle((int)a.Position.X, (int)a.Position.Y, a.Sprite.Bitmap.Width, a.Sprite.Bitmap.Height);
Rectangle rect2 = new Rectangle((int)b.Position.X, (int)b.Position.Y, b.Sprite.Bitmap.Width, b.Sprite.Bitmap.Height);
return rect1.IntersectsWith(rect2);
}
That's fantastic and I'm happy that I've gotten this far, however I wish to find out whether or not my player has intersected with the top, bottom, left or right side of a game object so that I can stop my player from moving in that direction if say... he collides with a wall. How would I go about doing this? Can some one please help me :)
By the way, game objects have Bitmaps, that are all 32 * 32 pixels, so i don't need per pixel collision
Thank you in advance :)
Computing whether two rectangles intersect is just a matter of a few comparisons (I know you're already using IntersectsWith):
if (Left > other.Right) return false;
if (Right < other.Left) return false;
if (Top > other.Bottom) return false;
if (Bottom < other.Top) return false;
return true;
To determine the direction of the collision, you'll have to take into account the possibility that:
one rectangle completely contains the other (collides in no direction)
it might create a "plus" figure (collides in all directions)
one corner of the rectangle could intersect with a corner of other corner (collides in two directions)
Anyway, to determine whether it has collided on the left side:
if (Right > other.Left && Right < other.Right && Left < other.Left)
{
// the right side of "this" rectangle is INSIDE the other
// and the left side of "this" rectangle is to the left of it
}
Last but not least, if you're computing this frame by frame, there's the possibility that an object might "jump over" the other. For more realistic physics you'd have to keep track of time and compute when it would intersect with (each edge of) the rectangle.
If you're processing frame by frame, a good choice could be storing the previous position P0x,P0y and the current position P1x,P1y.
Then, you can calculate a vector Vx,Vy and you can calculate the intersection point Ix,Iy. So you can get a more precise information about the collision.
If you need collision detection you need a rigidbody, you normally move rigidbodies with physics. As far as I saw, it's possible to live problem with above code snippet. If your moving them with transform move you should mark as is kinematic. That means it will not be moved by physics but by transform.
A kinematic rigidbody which you want to move should be done during FixedUpdate to apply collisions properly.
if you would like to know which side you hit you may raycast to the object
This code snippet gives an example that shows a raycast you could perform on collision. Ultimately, which game engine have you been using?
//this ray will generate a vector which points from the center of
//the falling object TO object hit. You subtract where you want to go from where you are
Ray MyRay = ObjectHit.position - ObjectFalling.position;
//this will declare a variable which will store information about the object hit
raycasthit MyRayHit;
//this is the actual raycast
raycast(MyRay, out MyRayHit);
//this will get the normal of the point hit, if you dont understand what a normal is
//wikipedia is your friend, its a simple idea, its a line which is tangent to a plane
vector3 MyNormal = MyRayHit.normal;
//this will convert that normal from being relative to global axis to relative to an
//objects local axis
MyNormal = MyRayHit.transform.transformdirection(MyNormal);
//this next line will compare the normal hit to the normals of each plane to find the
//side hit
if(MyNormal == MyRayHit.transform.up)
{
you hit the top plane, act accordingly
}
//important note the use of the '-' sign this inverts the direction, -up == down. Down doesn't exist as a stored direction, you invert up to get it.
if(MyNormal == -MyRayHit.transform.up)
{
you hit the bottom plane act accordingly
}
if(MyNormal == MyRayHit.transform.right)
{
hit right
}
//note the '-' sign converting right to left
if(MyNormal == -MyRayHit.transform.right)
{
hit left
}
Wouldn't it be an option to have 4 different objects each one on 1 side on your player object so depending on which side intersects with game object you could know which movement direction to prevent ?
I am trying to make a basic pong game using only drawn objects, so no picture boxes.
Here is my code: http://pastebin.com/pFNYa0HK
(I can pasted specific sections of the code if needed). What I need the code to do is detect the collision of the ball with the paddle and then reflect the ball off the paddle towards the other side of the game screen. pbCanvas is a picture box that covers the full form and is where all objects are drawn.
This is off the top of my head and written to test for collisions with the right paddle only:
There are three trivial possible collisions of a ball (at x,y with a radius r) with a paddle (Rectangle p):
Head on : bool CollisionFrontal = p.Contains(new Point(x+r, y));
Top : bool CollisionTop = p.Contains(new Point(x+r, y+r));
Bottom : bool CollisionBottom = p.Contains(new Point(x+r, y-r));
And then there are the corners:
After creating a GraphicsPath for the ball
GraphicsPath ballPath = new GraphicsPath();
ballPath.AddEllipse(p);
you can test for collisions with the two front corners of the paddle:
CornerTop : bool CollisionCornerTop = ballPath.IsVisible(p.Location);
CornerBot : bool CollisionCornerBot = ballPath.IsVisible(new Point(p.X, p.Bottom));
You still will need to check for the right angle to reflect and try to keep track of the balls spin as well as combining the relative speeds in the case of non-frontal collisions
Without spin the direction is reflected on either the front or side lines or, when colliding with a corner from the tangent of the corner on the ball's circle.
And you will usually need to move the ball back to the exact collision point as the collision will often be detected 'too late'..
Note that all coordinates and other variables ought to be using floats! I didn't in the code above but you should..
A new problem i have at the moment is sometimes when the ball is bouncing around the screen, it may all of a sudden get stuck colliding with a rectangle, so it may hit a spot between the rectangle and the side of the screen. This sometimes results in either: a) the ball looks like it's shaking or something, which i am assuming as it's still colliding with the rectangle, it's velocity (as i have defined it) is constantly inversing... but it's still stuck... (that's what i cant solve... lol :( ), or b) when the ball collides with the rectangle, it starts to move along the edge of the rectangle and goes back and forth but it continues to do this... (so it's basically stuck moving just along the edge).
I have uploaded a video to youtube showing this in action (btw... the ball gets stuck and goes up and down the right side of the screen and then starts going along the top edge of the white rectangle and then eventually bounces off into oblivion... well basically frees itself, which is kind of ok but then if you wait 5 more seconds, it repeats this process)
http://www.youtube.com/watch?v=3qfpgtoWbIU&feature=youtu.be
the code i am using for collision detection with the rectangle is:
if (theBall.GetRectangle.Bottom >= cornerSquare.GetRectangle.Top && theBall.GetRectangle.Bottom <= cornerSquare.GetRectangle.Bottom)
{
theBall.pVelocity.Y = -theBall.pVelocity.Y;
}
thats for detection if the ball hit's the top of the rectangle.
detection with the right side of the screen is:
if (pPosition.X + pTexture.Width >= screenWidth)
{
pVelocity.X = -pVelocity.X;
}
I hope someone has a simple and effective solution to this as i just want to 'be done' with all this collision detection nonsense lol... as it's taking up most of my time whilst i could be alot more productive doing other parts of the game.
Thanks for reading, taking the time to read, taking the time to help.... ETC.... :D
Try to check for collision first using a calculated test-rectangle.
For example the ball moves for Vector2(1, 1) per Update. Find out where the Rectangle for collision WOULD be at the next step BEFORE moving.
int nextPosX = currRect.X + (int)movementVector.X;
int nextPosY = currRect.Y + (int)movementVector.Y;
// find out where the rectangle would be after this update:
Rectangle nextStepsCollisionRect = new Rectangle(nextPosX, nextPosY, width, height);
// check if the test rectangle would collide:
if( collisionTest(nextStepsCollisionRect, sideWall) )
{
// do not move the intended way!
// invert X and recalc with pre-calculated rectangle again...
} else {
// no collision. now move the caculated way:
ball.CollisionRectangle = nextStepsCollisionRect;
}
This is a quite simple way. Use "ray tracing" if the ball is a very fast bullet (just to be mentioned).
If your ball is stuck along the edge you can simply bring the ball position to its previuos position before the collision (the one it had in the previous frame), in this way you change its velocity and ensure that the ball is not colliding anymore, this should do the trick.
You can achieve this by subtracting ball speed to ball position.
I have been working on and off of a certain game I'm making (trying to get it finished!) and a problem that has been unsolved for a while in it is the collision detection between a ball and a square.
Basically what I want to happen eventually is depending on the angle/way the rectangle is facing, I want the ball to bounce of it accordingly (I know I could just inverse the ball direction before/as it hit the square).
At the moment though, my current problem is trying to inverse the correct X and Y components depending on the side/face that the ball collides with the square, e.g. if the ball hits the right side of the square, then I need to inverse the ball's X component.
This doesn't seem to work and I was wondering if I could somehow label each side of the rectangle, in terms of for the top of it label that 'face 1' or something, then for the right side of it 'face 2' or 'side 2', etc...
I have provided some code below (this is the code I'm using now):
//(collision with right side of square)
if (theBall.GetRectangle.Left <= thePaddle.GetRectangle.Right)
{
theBall.pVelocity.X = -theBall.pVelocity.X;
}
//(collision with bottom of square)
if (theBall.GetRectangle.Top <= thePaddle.GetRectangle.Bottom)
{
theBall.pVelocity.Y = -theBall.pVelocity.Y;
}
I have written the code for the other 2 sides of the rectangle but they are just the opposite of the two above, i.e. for collision with top of rectangle = opposite of bottom, etc.
EDIT: the object I am checking against if the ball has collided with DOES NOT move, I mean it only rotates... so I don't know if this is important (it probably is, therefore I apologise for missing this info out at the start).
EDIT # 23:36: ok, I have tried something.... and it hasn't worked... :(
public Vector2 DistBetweenBallAndBlock(Paddle thePaddle, Ball theBall)
{
Vector2 centreOfBall = new Vector2(theBall.Texture.Width / 2, theBall.Texture.Height / 2);
float distX = thePaddle.Position.X - centreOfBall.X;
float distY = thePaddle.Position.Y - centreOfBall.Y;
if (distX < 0)
{
distX = -distX;
}
if (distY < 0)
{
distY = -distY;
}
return new Vector2(distX, distY);
}
I have then tried to just print the result just to get an idea of what's going on and what sort of values are being output:
Vector2 a = ball.DistBetweenBallAndBlock(paddle1, ball);
angleOfPaddle = Math.Atan2(a.Y, a.X);
I then just print this value to screen, however I am getting the same result of 0.63...
To detect a collision between Rectangles you could use Rectangle.Intersect method, instead of checking the objects' sides.
And to detect which side of the rectangle is hit, you can compute the Vector2 between the ball center and the rectangle center. Getting its angle with Math.Atan2 you can easily know which face of the rectangle has been hit.
Looked up some Vector stuff, based on my comment.
Collision Style
The optimal way of colliding with a circular object is to collide using a vector between it and the nearest point of the object you're checking against. If the distance is less than or equal to the radius of the circle, there is a collision. The advantages of this method are that you don't have to keep track of a rectangle, you get circular collision, and you get the angle of the collision from the vector.
How You Do It
I'll assume you have some strategy to keep from considering every object every frame, and keep to the basic problem. Your paddle has 4 vertices, one for each corner. Because your ball is essentially a vertex with a picture drawn over it, you can easily check the distance between the ball and each corner of your paddle. Two will be nearest. From there, it's just a matter of finding out if that edge collides. I found a solution to that here, which includes a nice formula.
Does that help?
in my XNA game(im fairly new to XNA by the way) i would like to have my player sprite land on top of a platform. i have a player sprite class that inherits from my regular sprite class, and the regular sprite class for
basic non playable sprite stuff such as boxes, background stuff, and platforms. However, i am unsure how to implement a way to make my player sprite land on a platform.
My player Sprite can jump and move around, but i dont know where and how to check to see if it is on top of my platform sprite.
My player sprites jump method is here
private void Jump()
{
if (mCurrentState != State.Jumping)
{
mCurrentState = State.Jumping;
mStartingPosition = Position;
mDirection.Y = MOVE_UP;
mSpeed = new Vector2(jumpSpeed, jumpSpeed);
}
}
mStartingPosition is player sprites starting position of the jump, and Position is the player sprites current position. I would think that my code for checking to see whether my player sprite is on top of my platform sprite. I am unsure how to reference my platform sprite inside of the playersprite class and inside of the jump method.
i think it should be something like this
//platformSprite.CollisonBox would be the rectangle around the platform, but im not
//sure how to check to see if player.Position is touching any point
//on platformSprite.CollisionBox
if(player.Position == platformSprite.CollisionBox)
{
player.mDirection = 0;
}
Again im pretty new to programming and XNA, and some of this logic i dont quite understand so any help on any of it would be greatly appreciated:D
Thanks
If player.Position is a Point and CollisionBox is a Rectangle, you could use
if (platformSprite.CollisionBox.Contains (player.Position))
I was playing around with something like this just a few days ago.
What you're calling a CollisionBox I called a BoundingBox. A BoundingBox is a Rectangle which represents the area occupied by the sprite.
You'll probably find it helpful to define a BoundingBox for your sprites instead of just using their position.
You can easily test for the collision of Rectangles using the following code:
if (player.BoundingBox.Intersects(platform.BoundingBox)
{
// handle collision here...
}
For this to work, make sure that the X and Y coordinates of your BoundingBox are correctly reflecting your sprite's position.
You may want to look at the Platformer Starter Kit. It includes this as well as detecting when the player is touching an enemy, collecting gems, etc.
The answer about BoundingBox is right, but IMHO it is easier to use Rectangle if you are making a 2D game rather than BoundingBox, wich is designed for a 3D one.
Anyway both objects have intersection / collision test methods that takes Vector2 or Point as parameter.
Now you have to have a box for your player, and a box for your platform. If one collide with the other, you have to check from where (the player can hit the platform from up, down, left, right, or maybe up & left on the left up corner).
If the player is on top of the platform, then just stop his fall, probably by setting his Y-speed component to 0.
Maybe you will need the player to go through the platform when jumping (player hit from bottom) but not when falling (player hit from top).