5
\$\begingroup\$

I am writing a 2D tile based game engine in XNA, and I've recently fine-tuned my collision detection using the answers provided here and more specifically here.

The tile based collision detection now works flawlessly and fixed all the small problems I had with my previous code. However, implementing this forced me to rewrite how movement was handled. Instead of moving the player all at once and then resolving each collision along the smallest axis of intersection depth, it now updates and resolves on the X and Y axes separately.

This is fine, but now my diagonal movement is much faster than my horizontal and vertical movement and I am unsure of how to normalize it.

I'm sure I'm just overlooking something simple, is there a straight forward way of doing this?

    public void HandleInput()
    {
        //Set horizontal and vertical values accordingly based on direction of key presses
        int horizontal = InputHelper.IsKeyDown(Keys.A) ? -1 : (InputHelper.IsKeyDown(Keys.D) ? 1 : 0);
        int vertical = InputHelper.IsKeyDown(Keys.W) ? -1 : (InputHelper.IsKeyDown(Keys.S) ? 1 : 0);

        //If there is movement update sprite position, round the position to nearest pixel and then check for collision
        if (vertical != 0)
        {
            sprite.Position += Vector2.UnitY * sprite.Speed * vertical;
            sprite.Position = new Vector2(sprite.Position.X, (float)Math.Round(sprite.Position.Y));
            HandleCollisions(Direction.Vertical);
            UpdateSpriteAnimation(Direction.Vertical, vertical);
        }
        if (horizontal != 0)
        {
            sprite.Position += Vector2.UnitX * sprite.Speed * horizontal;
            sprite.Position = new Vector2((float)Math.Round(sprite.Position.X), sprite.Position.Y);
            HandleCollisions(Direction.Horizontal);
            UpdateSpriteAnimation(Direction.Horizontal, horizontal);
        }
    }

    private void HandleCollisions(Direction direction)
    {
        // Get the player's bounding rectangle and find neighboring tiles.
        Rectangle playerBounds = sprite.spriteBounds;
        int leftTile = playerBounds.Left / Engine.TileSize;
        int topTile = playerBounds.Top / Engine.TileSize;
        int rightTile = (int)Math.Ceiling((float)playerBounds.Right / Engine.TileSize) - 1;
        int bottomTile = (int)Math.Ceiling(((float)playerBounds.Bottom / Engine.TileSize)) - 1;

        // For each potentially colliding tile,
        for (int y = topTile; y <= bottomTile; ++y)
        {
            for (int x = leftTile; x <= rightTile; ++x)
            {
                Vector2 depth;
                //If this tile is collidable, and it intersects sprite bounds
                if (tileMap.CollisionLayer.GetCellIndex(x, y) == 0 && TileIntersectsPlayer(playerBounds, GetTileBounds(y, x), direction, out depth))
                {
                    //Resolve the collision along the given axis
                    sprite.Position += depth;

                    // Perform further collisions with the new bounds.
                    playerBounds = sprite.spriteBounds;
                }
            }
        }
    }
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

To expand on what Trevor was saying, when you hold down just a vertical or horizontal button then your net movement is only 1 * speed, but if you hold down a vertical plus a horizontal key then your net movement is sqrt(1+1) * speed. So what you need to do is to normalize the horizontal and vertical values by dividing them by the sqrt of the sum of the squares of them. Or you can just put the values into a Vector2 and normalize it.

So change your lines where you retrieve vertical and horizontal and do this instead:

Vector2 move = new Vector2(InputHelper.IsKeyDown(Keys.A) ? -1 : (InputHelper.IsKeyDown(Keys.D) ? 1 : 0) , InputHelper.IsKeyDown(Keys.W) ? -1 : (InputHelper.IsKeyDown(Keys.S) ? 1 : 0));
Vector2.Normalize(move);

Then use move.X wherever you would have horizontal and move.Y wherever you had vertical.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks, this helped alot. I first put the values into a Vector2 and normalized it as per your code, but the speed seemed unchanged, maybe I over looked something. So I tried the other way you mentioned; dividing both the horizontal and vertical values by the sqrt of the sum of the squares and it now works perfectly. Thanks. \$\endgroup\$
    – user26564
    Commented Feb 21, 2013 at 13:30
1
\$\begingroup\$

Math doesn't care how you store your variables; you normalise vectors in exactly the same way, regardless of whether you put their values into a single object, or separate variables.

Length = sqrt( axisX * axisX + axisY * axisY (... + axisZ * axisZ, etc) ).

Then divide each axis by the length you've calculated.

\$\endgroup\$
2
  • \$\begingroup\$ @KyleBaran Please post answers as answers, not as comments. \$\endgroup\$ Commented Nov 12, 2015 at 22:28
  • \$\begingroup\$ (With that said, I can't make your amended formula produce reasonable results, so either it needs more explanation about how to use it, or there may simply be fundamental problems with the formula. But either way, the place to wrangle with those is in a separate answer, not in the comments on this one.) \$\endgroup\$ Commented Nov 12, 2015 at 22:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .