I have a sprite, Player
. I update Player
's position the following way:
_position += (_direction * _velocity) * (float)gameTime.ElapsedGameTime.TotalSeconds;
Where _position
, _direction
and _velocity
are Vector2
s.
I have a simple collider(BoxCollider
) which simply generates a rectangle (BoundingBox
) given the position and dimensions of the collider.
In Initialize()
, I create a new List<BoxCollider>
and fill it with colliders for the level.
On Update()
, I pass the list to Player
to check for collisions.
The collision check method:
public void CheckPlatformCollision(List<BoxCollider> colliders, GameTime gameTime)
{
var nextPosition = _position + (_direction * _velocity) * (float)gameTime.ElapsedGameTime.TotalSeconds;
Rectangle playerCollider = new Rectangle((int)nextPosition.X, (int)nextPosition.Y, BoundingBox.Width, BoundingBox.Height);
foreach(BoxCollider collider in colliders)
{
if(playerCollider.Intersects(collider.BoundingBox))
{
nextPosition = _position;
}
}
Position = nextPosition;
}
Right now, every way I've tried to implement gravity has failed. If Player
is dropped from too high, nextPosition
becomes too far away from Player
and leaves it stuck in mid-air.
I'm also having problems with horizontal collisions as well, the issue being similar: Player
stops too soon, leaving a space between. Sometimes I've had Player
stick to the side of the collider.
What I would like to know is:
How do I properly implement gravity & jumping with _position
, _direction
, and _velocity
? How do I properly handle collisions both horizontally and vertically?
For gravity, add this just before you update _position
:
_velocity += gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;
Where gravity
is something like new Vector2(0, 10)
.
For jumping, you need to set the vertical component of the velocity when the player presses the jump button:
if (jumpPressed && jumpAvailable)
{
_velocity.Y = -10; // Numbers are example. You need to adjust this
jumpAvailable = false;
}
And you need to reset jumpAvailable
when the player touches the floor.
Colliding is a much complicated thing. But if you look for "XNA implement collision" on the internet you will find a lot of answers.
There are many ways. One of them is pushing back the player to the border of the boxcollider, instead of not letting him move, like you did in your code. The code would be:
if (IntersectsFromTop(playerCollider, collider.BoundingBox))
{
_position.Y = collider.BoundingBox.Y - BoundingBox.Height;
}
else if (IntersectsFromRight(playerCollider, collider.BoundingBox))
{
_position.X = collider.BoundingBox.X + collider.BoundingBox.Width;
}
// And so on...
The helper methods can implemented like:
private static bool IntersectsFromTop(Rectange player, Rectangle target)
{
var intersection = Rectangle.Intersect(player, target);
return player.Intersects(target) && intersection.Y == target.Y && intersection.Width >= intersection.Height;
}
private static bool IntersectsFromRight(Rectange player, Rectangle target)
{
var intersection = Rectangle.Intersect(player, target);
return player.Intersects(target) && intersection.X + intersection.Width == target.X + target.Width && intersection.Width <= intersection.Height;
}
// And so on...
The rationnale behind that code can be explained with a picture:
In that picture width
and height
correspond to the intersection, in purple.