Steven B.
Steven B.

Reputation: 45

Collision Detection between player and tiles

I am trying to implement basic (for now) collision detection into my platformer. I have tiles that are each 16 x 16 in size. The character is 32 x 32 pixels in size and has its own bounding box. Now, in my Tile class, I have a bool, isSolid. Each of these tiles in my array also have a rect for their respective bounding boxes.

I am checking to see if there's an intersection between the player and tiles by doing:

if (player.GetBoundingBox().Intersects(map.tiles[(int)player.position.Y / 16,
(int)player.position.X / 16].bounds) && map.tiles[(int)player.position.Y / 16,  
(int)player.position.X / 16].isSolid) 
{
...
}

Now, my problem is that this is extremely inaccurate as I'm rounding off the position. I'm tired as heck right now and for the life of me I can't figure out how to properly do this. What is the best way to approach this issue?

Upvotes: 1

Views: 812

Answers (1)

Cyral
Cyral

Reputation: 14153

Well this might not be exactly "basic", It works very nicely and dosen't have any problems because it calculates the X axis and Y axis seperatley, this collision structure will help you later on. (I switched to this from the old Platformer Starter kit code, which was very glitchy)

Assuming you already have methods for gravity, lets get started.

This should be after your falling and velocity logic, It will see what axises need to be checked.

            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds; //If you havent already, get the elapsed time
            if (velocity.X != 0f)
            {
                Position += velocity.X * Vector2.UnitX * elapsed;
                HandleCollisions(CollisionDirection.Horizontal);
            }
            if (velocity.Y != 0f)
            {
                Position += velocity.Y * Vector2.UnitY * elapsed;
                HandleCollisions(CollisionDirection.Vertical);
            }

Now for the very important HandleCollisons method

        private void HandleCollisions(CollisionDirection direction)
        {
            // Get the player's bounding rectangle and find neighboring tiles.
            Rectangle bounds = player.GetBoundingBox();
            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)
                {
                   Rectangle tileBounds = Level.GetBounds(x, y);
                    // If this tile is collidable,
                    bool IsSolid = map.tiles[x,y].IsSolid;
                    Vector2 depth;
                    if (isSolid && TileIntersectsPlayer(BoundingRectangle, tileBounds, direction, out depth))
                    {

                            if ((collision == ItemCollision.Platform && movement.Y > 0))
                                continue;
                            isOnGround = true;
                            if (isSolid || isOnGround)
                            {
                                if (direction == CollisionDirection.Horizontal)
                                {
                                    position.X += depth.X;
                                }
                                else
                                {

                                    isOnGround = true;
                                    position.Y += depth.Y;
                                }
                            }


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

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

Thats it for that! It will detect collisons, but we need to allow it to figure out how much to push the player back up once it collides! You will need these two extension methods.

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;
        }

Note you may need to modify this a bit, as the players position is the BOTTOM left. Also a collision enum is needed, for vertical and horizontal. Please tell me if anything seems missing in this.

Upvotes: 1

Related Questions