Time for action - accounting for walls

  1. Add the checkTileObstacles() method to the Movement Limitations region of the Player class:
    private static Vector2 checkTileObstacles(
    float elapsedTime,
    Vector2 moveAngle)
    {
    Vector2 newHorizontalLocation = BaseSprite.WorldLocation +
    (new Vector2(moveAngle.X, 0) * (playerSpeed * elapsedTime));
    Vector2 newVerticalLocation = BaseSprite.WorldLocation +
    (new Vector2(0, moveAngle.Y) * (playerSpeed * elapsedTime));
    Rectangle newHorizontalRect = new Rectangle(
    (int)newHorizontalLocation.X,
    (int)BaseSprite.WorldLocation.Y,
    BaseSprite.FrameWidth,
    BaseSprite.FrameHeight);
    Rectangle newVerticalRect = new Rectangle(
    (int)BaseSprite.WorldLocation.X,
    (int)newVerticalLocation.Y,
    BaseSprite.FrameWidth,
    BaseSprite.FrameHeight);
    int horizLeftPixel = 0;
    int horizRightPixel = 0;
    int vertTopPixel = 0;
    int vertBottomPixel = 0;
    if (moveAngle.X < 0)
    {
    horizLeftPixel = (int)newHorizontalRect.Left;
    horizRightPixel = (int)BaseSprite.WorldRectangle.Left;
    }
    if (moveAngle.X > 0)
    {
    horizLeftPixel = (int)BaseSprite.WorldRectangle.Right;
    horizRightPixel = (int)newHorizontalRect.Right;
    }
    if (moveAngle.Y < 0)
    {
    vertTopPixel = (int)newVerticalRect.Top;
    vertBottomPixel = (int)BaseSprite.WorldRectangle.Top;
    }
    if (moveAngle.Y > 0)
    {
    vertTopPixel = (int)BaseSprite.WorldRectangle.Bottom;
    vertBottomPixel = (int)newVerticalRect.Bottom;
    }
    if (moveAngle.X != 0)
    {
    for (int x = horizLeftPixel; x < horizRightPixel; x++)
    {
    for (int y = 0; y < BaseSprite.FrameHeight; y++)
    {
    if (TileMap.IsWallTileByPixel(
    new Vector2(x, newHorizontalLocation.Y + y)))
    {
    moveAngle.X = 0;
    break;
    }
    }
    if (moveAngle.X == 0)
    {
    break;
    }
    }
    }
    playerwalls, accounting forif (moveAngle.Y != 0)
    {
    for (int y = vertTopPixel; y < vertBottomPixel; y++)
    {
    for (int x = 0; x < BaseSprite.FrameWidth; x++)
    {
    if (TileMap.IsWallTileByPixel(
    new Vector2(newVerticalLocation.X + x, y)))
    {
    moveAngle.Y = 0;
    break;
    }
    }
    if (moveAngle.Y == 0)
    {
    break;
    }
    }
    }
    return moveAngle;
    }
    
  2. Return to the handleInput() method and once again update the if (moveAngle != Vector2.Zero) statement by adding a call to checkTileObstacles(). The whole statement should now read:
    if (moveAngle != Vector2.Zero)
    {
    moveAngle.Normalize();
    baseAngle = moveAngle;
    moveAngle = checkTileObstacles(elapsed, moveAngle);
    }
    
  3. Execute the game again and drive around.

What just happened?

In order to check for tile-based collisions, we break the process into two steps. First we will check for collisions due to horizontal movement, and then due to vertical movement.

To facilitate this, we begin by establishing two new vectors representing the new locations of the sprite, if only the horizontal or vertical movement was applied.

From these new vectors, we create two rectangles, representing the world location of the sprite's two possible movements. In other words, newHorizontalRect specifies where the player's sprite would be located after moving, if only the horizontal component of the movement is considered, while newVerticalRect represents the new location, if movement occurred only along the vertical axis:

What just happened?

Next, we need to determine a range of pixels to check for tile obstacles. We could simply check all of the pixels inside the two new rectangles, calling TileMap IsWallTile() for each pixel in both rectangles. This would return the results we are looking for, but it would cause unnecessary processing, as only the portions of the new rectangles that are not already covered by the current position's rectangle need to be checked for collisions.

Instead, we can use the direction in which we are moving to determine a smaller range of pixels that need to be checked. In the horizontal movement portion of the previous diagram, the new position rectangle mostly overlaps the old position rectangle. We only need to check the pixels between the right edge of the old position and the right edge of the new position, because the old position is already known to be an area the sprite can exist in.

If we are moving to the right, we use the right edges of the old and new positions to define the limits of the area we will check. Similarly, if we are moving left, we use the left edges of the two position rectangles to define the area.

We can then check each pixel in the new area against TileMap.IsWallTile() to determine if moving in this direction would cause the sprite to overlap a wall tile. If any of them do, we set the X component of the moveAngle vector to zero, eliminating the movement in that direction. As soon as any pixel tests true, we break out of the loop, since there is no need to continue testing.

After we have dealt with horizontal movement, the process is repeated for vertical movement, eliminating the Y component of the moveAngle vector if the new Y position would result in a wall collision.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.111.9