Friday, June 1, 2018

xna - Platformer Starter Kit - Collision Issues


I'm having trouble with my game that is based off the XNA Platformer starter kit. My game uses smaller tiles (16x16) then the original (32x40) which I'm thinking may be having an effect on collision (Being it needs to be more precise).


Standing on the edge of a tile and jumping causes the player to move off the the tile when he lands. And 80% of the time, when the player lands, he goes flying though SOLID tiles in a diagonal fashion. This is very annoying as it is almost impossible to test other features, when spawning and jumping will result in the player landing in another part of the level or falling off the edge completely.


The code is as follows:


/// 
/// Updates the player's velocity and position based on input, gravity, etc.

///

public void ApplyPhysics(GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

Vector2 previousPosition = Position;

// Base velocity is a combination of horizontal movement control and
// acceleration downward due to gravity.
velocity.X += movement * MoveAcceleration * elapsed;

velocity.Y = MathHelper.Clamp(velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);

velocity.Y = DoJump(velocity.Y, gameTime);

// Apply pseudo-drag horizontally.
if (IsOnGround)
velocity.X *= GroundDragFactor;
else
velocity.X *= AirDragFactor;


// Prevent the player from running faster than his top speed.
velocity.X = MathHelper.Clamp(velocity.X, -MaxMoveSpeed, MaxMoveSpeed);

// Apply velocity.
Position += velocity * elapsed;
Position = new Vector2((float)Math.Round(Position.X), (float)Math.Round(Position.Y));

// If the player is now colliding with the level, separate them.
HandleCollisions();


// If the collision stopped us from moving, reset the velocity to zero.
if (Position.X == previousPosition.X)
velocity.X = 0;

if (Position.Y == previousPosition.Y)
velocity.Y = 0;
}


///

/// Detects and resolves all collisions between the player and his neighboring
/// tiles. When a collision is detected, the player is pushed away along one
/// axis to prevent overlapping. There is some special logic for the Y axis to
/// handle platforms which behave differently depending on direction of movement.


///

private void HandleCollisions()
{
// Get the player's bounding rectangle and find neighboring tiles.

Rectangle bounds = BoundingRectangle;
int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

// Reset flag to search for ground collision.
isOnGround = false;

// For each potentially colliding tile,

for (int y = topTile; y <= bottomTile; ++y)
{
for (int x = leftTile; x <= rightTile; ++x)
{
// If this tile is collidable,
ItemCollision collision = Level.GetCollision(x, y);
if (collision != ItemCollision.Passable)
{
// Determine collision depth (with direction) and magnitude.
Rectangle tileBounds = Level.GetBounds(x, y);

Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
if (depth != Vector2.Zero)
{
float absDepthX = Math.Abs(depth.X);
float absDepthY = Math.Abs(depth.Y);

// Resolve the collision along the shallow axis.
if (absDepthY < absDepthX || collision == ItemCollision.Platform)
{
// If we crossed the top of a tile, we are on the ground.

if (previousBottom <= tileBounds.Top)
isOnGround = true;

// Ignore platforms, unless we are on the ground.
if (collision == ItemCollision.Impassable || IsOnGround)
{
// Resolve the collision along the Y axis.
Position = new Vector2(Position.X, Position.Y + depth.Y);

// Perform further collisions with the new bounds.

bounds = BoundingRectangle;
}
}
else if (collision == ItemCollision.Impassable) // Ignore platforms.
{
// Resolve the collision along the X axis.
Position = new Vector2(Position.X + depth.X, Position.Y);

// Perform further collisions with the new bounds.
bounds = BoundingRectangle;

}
}
}
}
}

// Save the new bounds bottom.
previousBottom = bounds.Bottom;
}


It also tends to jitter a little bit sometimes, I'm solved some of this with some fixes I found here on stackexchange, But Ive only seen one other case of the flying though blocks problem. This question seems to have a similar problem in the video, but mine is more crazy.


Again this is a very annoying bug! Any help would be greatly appreciated!


EDIT: Speed stuff


    // Constants for controling horizontal movement
private const float MoveAcceleration = 13000.0f;
private const float MaxMoveSpeed = 1750.0f;
private const float GroundDragFactor = 0.48f;
private const float AirDragFactor = 0.58f;

// Constants for controlling vertical movement

private const float MaxJumpTime = 0.35f;
private const float JumpLaunchVelocity = -3500.0f;
private const float GravityAcceleration = 3400.0f;
private const float MaxFallSpeed = 550.0f;
private const float JumpControlPower = 0.14f;

Answer



I believe you're on the right track. Take a look at my amazing artistic skills here ;) Platform collision visualization


Let's say that the player is the red block with the solid being their current position and the open square being their previous position. Now say that the blue tile is the platform you expect to land on and the green one is some other "impassable" tile beneath it.


The collision resolution assumes the you can only intersect one solid tile at a time and checks against it. Specifically, these lines


if (previousBottom <= tileBounds.Top)

isOnGround = true;

So if we look at the right side of the image we can see that technically your previous bottom position would be less than the green tiles top. Since we've determined that, we assume it's a piece of ground and try to move on top of it. Then on the next go around since we've "corrected" the vertical collision, any other collisions will be treated as horizontal collisions and attempt to "push you away" from the other block, thus giving you a diagonal descent in to the pits of no-clip zone.


You've got a couple different solutions you could try:



  • Change the maximum falling speed to be no greater than the height of your tiles.

  • Use a more advanced collision structure and try to predict collisions before they occur.

  • Check not only tiles you're directly colliding with but also one (or more) in a given direction. For example, if you're colliding with an "Impassable" check the tile just above it to see if it's a platform. It'd be pretty safe bet to say you passed through it if that's the case.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...