Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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;
                }
            }
        }
    }
share|improve this question
add comment

2 Answers 2

up vote 3 down vote accepted

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.

share|improve this answer
    
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. –  user26564 Feb 21 '13 at 13:30
add comment

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.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.