[SOLVED] How would I go about "sliding" a collision while moving diagonally?

Hey everyone,

Currently I am in the process of improving collisions in my top-down game. I want to make it so that if you move diagonally and you are facing, let’s say, left, you can still slide up and down, according to which keys you are holding. I’m bad at explaining, so maybe the below code that detects my collisions will do it for me:

            Rectangle newCollisionBox = new Rectangle(CollisionBox.X + (int)Velocity.X, CollisionBox.Y + (int)Velocity.Y,
                Sprite.SpriteWidth * (int)Game1.Scale / 2, Sprite.SpriteHeight * (int)Game1.Scale / 4);


            for (int index = 0; index < Game1.CurrentLocation.TileCollisions.Count; ++index)
            {
                if (newCollisionBox.Intersects(Game1.CurrentLocation.TileCollisions[index]))
                {
                    if (newCollisionBox.Right > Game1.CurrentLocation.TileCollisions[index].Center.X)
                    {
                        Console.WriteLine("Coming from the right");
                        Velocity = new Vector2(0f, Velocity.Y);
                    }

                    if (newCollisionBox.Left < Game1.CurrentLocation.TileCollisions[index].Center.X)
                    {
                        Console.WriteLine("Coming from the left");
                        Velocity = new Vector2(0f, Velocity.Y);
                    }


                    if (newCollisionBox.Bottom > Game1.CurrentLocation.TileCollisions[index].Center.Y)
                    {
                        Console.WriteLine("Coming from the bottom");
                        Velocity = new Vector2(Velocity.X, 0f);
                    }

                    if (newCollisionBox.Top < Game1.CurrentLocation.TileCollisions[index].Center.Y)
                    {
                        Console.WriteLine("Coming from the top");
                        Velocity = new Vector2(Velocity.X, 0f);
                    }
                }
            }

So how would I go about this? I already did research to AABB collisions, but I am not sure how to implement this. My current problem with the code above is that if I move diagonally, my character does not move at all. Please provide me with some example code!

Thanks in advance. If you still have any questions regarding what I exactly mean, please let me know.

EDIT: (Solution)

With your help I managed to implement the collision detection! Check out my new code below:

`

        if (CanMove)
        {
            if (Velocity.Y != 0f)
            {
                Vector2 oldPosition = Sprite.Position;
                Sprite.Position = new Vector2(Sprite.Position.X, Sprite.Position.Y + Velocity.Y);

                UpdateCollisionBox();

                for (int index = 0; index < Game1.CurrentLocation.TileCollisions.Count; ++index)
                {
                    if (CollisionBox.Intersects(Game1.CurrentLocation.TileCollisions[index]))
                    {
                        if (CollisionBox.Bottom > Game1.CurrentLocation.TileCollisions[index].Center.Y)
                        {
                            Sprite.Position = new Vector2(Sprite.Position.X, oldPosition.Y);
                            UpdateCollisionBox();
                        }
                        else if (CollisionBox.Top < Game1.CurrentLocation.TileCollisions[index].Center.Y)
                        {
                            Sprite.Position = new Vector2(Sprite.Position.X, oldPosition.Y);
                            UpdateCollisionBox();
                        }
                    }
                }
            }

            if (Velocity.X != 0f)
            {
                Vector2 oldPosition = Sprite.Position;
                Sprite.Position = new Vector2(Sprite.Position.X + Velocity.X, Sprite.Position.Y);

                UpdateCollisionBox();

                for (int index = 0; index < Game1.CurrentLocation.TileCollisions.Count; ++index)
                {
                    if (CollisionBox.Intersects(Game1.CurrentLocation.TileCollisions[index]))
                    {
                        if (CollisionBox.Right > Game1.CurrentLocation.TileCollisions[index].Center.X)
                        {
                            Sprite.Position = new Vector2(oldPosition.X, Sprite.Position.Y);
                            UpdateCollisionBox();
                        }

                        if (CollisionBox.Left < Game1.CurrentLocation.TileCollisions[index].Center.X)
                        {
                            Sprite.Position = new Vector2(oldPosition.X, Sprite.Position.Y);
                            UpdateCollisionBox();
                        }
                    }
                }
            }
        }
        UpdateCollisionBox();

`
Once again, thank you to everybody who took their time to reply to this thread!

One simple way to do this is to separately check collisions horizontally and vertically. For example, if you collide with something horizontally but not vertically, only the vertical movement should be unhindered, which would allow your character to move.

You can go further by applying the same concept and aligning your character to the edge of the collision if it detects a collision from a direction. This would naturally result in ejecting the character if they somehow got stuck, which is generally favorable to keeping them unable to move.

You could try this: https://gamedev.stackexchange.com/questions/22118/how-can-i-use-rectangle-intersect-to-resolve-collisions-in-xna

David has done a great job :slight_smile: I use this code too and it’s great!

public static class RectangleExtensions
{
   public static float GetHorizontalIntersectionDepth(this Rectangle rectA, Rectangle rectB)
   {
        // Calculate half sizes.
        float halfWidthA = rectA.Width / 2.0f;
        float halfWidthB = rectB.Width / 2.0f;

        // Calculate centers.
        float centerA = rectA.Left + halfWidthA;
        float centerB = rectB.Left + halfWidthB;

        // Calculate current and minimum-non-intersecting distances between centers.
        float distanceX = centerA - centerB;
        float minDistanceX = halfWidthA + halfWidthB;

        // If we are not intersecting at all, return (0, 0).
        if (Math.Abs(distanceX) >= minDistanceX)
            return 0f;

        // Calculate and return intersection depths.
        return distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
   }

   public static float GetVerticalIntersectionDepth(this Rectangle rectA, Rectangle rectB)
   {
        // Calculate half sizes.
        float halfHeightA = rectA.Height / 2.0f;
        float halfHeightB = rectB.Height / 2.0f;

        // Calculate centers.
        float centerA = rectA.Top + halfHeightA;
        float centerB = rectB.Top + halfHeightB;

        // Calculate current and minimum-non-intersecting distances between centers.
        float distanceY = centerA - centerB;
        float minDistanceY = halfHeightA + halfHeightB;

        // If we are not intersecting at all, return (0, 0).
        if (Math.Abs(distanceY) >= minDistanceY)
            return 0f;

        // Calculate and return intersection depths.
        return distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
   }
}

public enum Direction
{
   Horizontal,
   Vertical
}

public void HandleInput(float elapsed)
{
   KeyboardState state = Keyboard.GetState();

   // Handle Keyboard Input
   int horizontal = state.IsKeyDown(Keys.Left) ? -1 : (state.IsKeyDown(Keys.Right) ? 1 : 0);
   int vertical = state.IsKeyDown(Keys.Up) ? -1 : (state.IsKeyDown(Keys.Down) ? 1 : 2);

   // Handle Horizontal Movement
   if (horizontal != 0)
   {
        _playerPosition += Vector2.UnitX * Math.Min(PlayerSpeed * elapsed, Math.Min(_playerSize.X, TileSize)) * horizontal;
        _playerPosition = new Vector2((float)Math.Round(_playerPosition.X), _playerPosition.Y);
        HandleCollisions(Direction.Horizontal);
   }

   // Handle Vertical Movement
   if(vertical != 0)
   {
        _playerPosition += Vector2.UnitY * Math.Min(PlayerSpeed * elapsed, Math.Min(_playerSize.Y, TileSize)) * vertical;
        _playerPosition = new Vector2(_playerPosition.X, (float)Math.Round(_playerPosition.Y));
        HandleCollisions(Direction.Vertical);
   }
}

private void HandleCollisions(Direction direction)
{
   Rectangle playerBounds = GetPlayerBounds();
   int leftTile = playerBounds.Left / TileSize;
   int topTile = playerBounds.Top / TileSize;
   int rightTile = (int)Math.Ceiling((float)playerBounds.Right / TileSize) - 1;
   int bottomTile = (int)Math.Ceiling(((float)playerBounds.Bottom / TileSize)) - 1;

   for (int y = topTile; y <= bottomTile; ++y)
   {
        for (int x = leftTile; x <= rightTile; ++x)
        {
            Vector2 depth;
            if (TileIsObstacle(x, y) && TileIntersectsPlayer(playerBounds, GetTileBounds(y, x), direction, out depth))
            {
                _playerPosition += depth;
                playerBounds = GetPlayerBounds();
            }
        }
   }
}

private static Rectangle GetTileBounds(int y, int x)
{
   return new Rectangle(x * TileSize, y * TileSize, TileSize, TileSize);
}

public Rectangle GetPlayerBounds()
{
   return new Rectangle((int)Math.Round(_playerPosition.X), (int)Math.Round(_playerPosition.Y), (int)Math.Round(_playerSize.X), (int)Math.Round(_playerSize.Y));
}

private static bool TileIntersectsPlayer(Rectangle player, Rectangle block, Direction direction, out Vector2 depth)
{
   depth = direction == Direction.Vertical ?  new Vector2(0, player.GetVerticalIntersectionDepth(block)) :  new Vector2(player.GetHorizontalIntersectionDepth(block), 0);
   return depth.Y != 0 || depth.X != 0;
}

private bool TileIsObstacle(int x, int y)
{
   if (x < 0 || y < 0 || x >= _width || y >= _height) return true;
   return _blocks[x, y] != ' ';
}

Thanks for your reply! Do you have any example code for this? Also, how would I pull this off with multiple collisions? I have tried this method and that would result in the player being able to slide past collisions from the bottom while they were facing either left or right.

Thanks! But that looks like more code than needed… Beside that, I’m not using just square tiles, but also other rectangles to check for collisions. Could you show me how I would apply something like this to my own code?

EnemyArea posted all the code you need. It’s already using rectangles and is not suqare-only.
Of course you have to alter it slightly to fit your needs.
If you can’t then you might consider using a higher-level framework to do the work for you or take some more time trying to understand the provided code and where to put it.

EnemyArea’s code is more complete and will work, but I’ll post a simplified version of what I have.

public static bool CheckCollision(in Vector2 speed, in Rectangle collisionRect, List<Rectangle> collision)
{
    Rectangle rect = collisionRect;
    rect.X += (int)speed.X;
    rect.Y += (int)speed.Y;

    for (int i = 0; i < collision.Count; i++)
    {
        if (rect.Intersects(collision[i]) == true)
        {
            return true;
        }
    }

    return false;
}

private void HandleMove(in Vector2 moveAmt)
{
    Vector2 newMove = Vector2.Zero;
    
    //Check collisions in the X
    if (moveAmt.X != 0f)
    {
        newMove.Y = 0f;
        newMove.X = moveAmt.X;
    
        bool collided = CheckCollision(newMove, PlayerRect, Collisions);
        if (collided == false)
        {
            PlayerPosition += newMove;
        }
    }
    
    //Check collisions in the Y
    if (moveAmt.Y != 0f)
    {
        newMove.Y = moveAmt.Y;
        newMove.X = 0f;
    
        bool collided = CheckCollision(newMove, PlayerRect, Collisions);
        if (collided == false)
        {
            PlayerPosition += newMove;
        }
    }
}

To put it simply, if there’s no collision in the X direction, move in the X direction, and if there’s no collision in the Y direction, move in the Y direction.