Tile Collision & Sliding against tiles

Posted by Devin Rawlek on Game Development See other posts from Game Development or by Devin Rawlek
Published on 2013-07-27T21:47:25Z Indexed on 2013/10/27 10:19 UTC
Read the original article Hit count: 441

Filed under:
|

I have a tile based map with a top down camera. My sprite stops moving when he collides with a wall in any of the four directions however I am trying to get the sprite to slide along the wall if more than one directional key is pressed after being stopped. Tiles are set to 32 x 32.

Here is my code;

        // Gets Tile Player Is Standing On
        var splatterTileX = (int)player.Position.X / Engine.TileWidth;
        var splatterTileY = (int)player.Position.Y / Engine.TileHeight;

        // Foreach Layer In World Splatter Map Layers
        foreach (var layer in WorldSplatterTileMapLayers)
        {
            // If Sprite Is Not On Any Edges
            if (splatterTileX < layer.Width - 1 && splatterTileX > 0 && splatterTileY < layer.Height - 1 && splatterTileY > 0)
            {
                tileN = layer.GetTile(splatterTileX, splatterTileY - 1);      // North
                tileNE = layer.GetTile(splatterTileX + 1, splatterTileY - 1); // North-East
                tileE = layer.GetTile(splatterTileX + 1, splatterTileY);      // East
                tileSE = layer.GetTile(splatterTileX + 1, splatterTileY + 1); // South-East
                tileS = layer.GetTile(splatterTileX, splatterTileY + 1);      // South
                tileSW = layer.GetTile(splatterTileX - 1, splatterTileY + 1); // South-West                    
                tileW = layer.GetTile(splatterTileX - 1, splatterTileY);      // West
                tileNW = layer.GetTile(splatterTileX - 1, splatterTileY - 1); // North-West
            }

            // If Sprite Is Not On Any X Edges And Is On -Y Edge
            if (splatterTileX < layer.Width - 1 && splatterTileX > 0 && splatterTileY == 0)
            {
                tileE = layer.GetTile(splatterTileX + 1, splatterTileY);      // East
                tileSE = layer.GetTile(splatterTileX + 1, splatterTileY + 1); // South-East
                tileS = layer.GetTile(splatterTileX, splatterTileY + 1);      // South
                tileSW = layer.GetTile(splatterTileX - 1, splatterTileY + 1); // South-West                    
                tileW = layer.GetTile(splatterTileX - 1, splatterTileY);      // West
            }
            // If Sprite Is On +X And -Y Edges
            if (splatterTileX == layer.Width - 1 && splatterTileY == 0)
            {
                tileS = layer.GetTile(splatterTileX, splatterTileY + 1);      // South
                tileSW = layer.GetTile(splatterTileX - 1, splatterTileY + 1); // South-West                    
                tileW = layer.GetTile(splatterTileX - 1, splatterTileY);      // West
            }
            // If Sprite Is On +X Edge And Y Is Not On Any Edge
            if (splatterTileX == layer.Width - 1 && splatterTileY < layer.Height - 1 && splatterTileY > 0)
            {
                tileS = layer.GetTile(splatterTileX, splatterTileY + 1);      // South
                tileSW = layer.GetTile(splatterTileX - 1, splatterTileY + 1); // South-West                    
                tileW = layer.GetTile(splatterTileX - 1, splatterTileY);      // West
                tileNW = layer.GetTile(splatterTileX - 1, splatterTileY - 1); // North-West
                tileN = layer.GetTile(splatterTileX, splatterTileY - 1);      // North
            }
            // If Sprite Is On +X And +Y Edges
            if (splatterTileX == layer.Width - 1 && splatterTileY == layer.Height - 1)
            {
                tileW = layer.GetTile(splatterTileX - 1, splatterTileY);      // West
                tileNW = layer.GetTile(splatterTileX - 1, splatterTileY - 1); // North-West
                tileN = layer.GetTile(splatterTileX, splatterTileY - 1);      // North
            }
            // If Sprite Is Not On Any X Edges And Is On +Y Edge
            if (splatterTileX < (layer.Width - 1) && splatterTileX > 0 && splatterTileY == layer.Height - 1)
            {
                tileW = layer.GetTile(splatterTileX - 1, splatterTileY);      // West
                tileNW = layer.GetTile(splatterTileX - 1, splatterTileY - 1); // North-West
                tileN = layer.GetTile(splatterTileX, splatterTileY - 1);      // North
                tileNE = layer.GetTile(splatterTileX + 1, splatterTileY - 1); // North-East
                tileE = layer.GetTile(splatterTileX + 1, splatterTileY);      // East
            }
            // If Sprite Is On -X And +Y Edges
            if (splatterTileX == 0 && splatterTileY == layer.Height - 1)
            {
                tileN = layer.GetTile(splatterTileX, splatterTileY - 1);      // North
                tileNE = layer.GetTile(splatterTileX + 1, splatterTileY - 1); // North-East
                tileE = layer.GetTile(splatterTileX + 1, splatterTileY);      // East
            }
            // If Sprite Is On -X Edge And Y Is Not On Any Edges
            if (splatterTileX == 0 && splatterTileY < (layer.Height - 1) && splatterTileY > 0)
            {
                tileN = layer.GetTile(splatterTileX, splatterTileY - 1);      // North
                tileNE = layer.GetTile(splatterTileX + 1, splatterTileY - 1); // North-East
                tileE = layer.GetTile(splatterTileX + 1, splatterTileY);      // East
                tileSE = layer.GetTile(splatterTileX + 1, splatterTileY + 1); // South-East
                tileS = layer.GetTile(splatterTileX, splatterTileY + 1);      // South
            }
            // If Sprite Is In The Top Left Corner
            if (splatterTileX == 0 && splatterTileY == 0)
            {
                tileE = layer.GetTile(splatterTileX + 1, splatterTileY);      // East
                tileSE = layer.GetTile(splatterTileX + 1, splatterTileY + 1); // South-East
                tileS = layer.GetTile(splatterTileX, splatterTileY + 1);      // South
            }
            // Creates A New Rectangle For TileN
            tileN.TileRectangle = new Rectangle(splatterTileX * Engine.TileWidth,
                                              (splatterTileY - 1) * Engine.TileHeight,
                                              Engine.TileWidth,
                                              Engine.TileHeight);

            // Tile Collision Detection Between Player Rectangle And N Tile
            var tileNCollision = player.Rectangle.Intersects(tileN.TileRectangle);


            // Creates A New Rectangle For TileNE
            tileNE.TileRectangle = new Rectangle((splatterTileX + 1) * Engine.TileWidth,
                                              (splatterTileY - 1) * Engine.TileHeight,
                                              Engine.TileWidth,
                                              Engine.TileHeight);

            // Tile Collision Detection Between Player Rectangle And NE Tile
            var tileNECollision = player.Rectangle.Intersects(tileNE.TileRectangle);

            // Creates A New Rectangle For TileE
            tileE.TileRectangle = new Rectangle((splatterTileX + 1) * Engine.TileWidth,
                                              splatterTileY * Engine.TileHeight,
                                              Engine.TileWidth,
                                              Engine.TileHeight);

            // Tile Collision Detection Between Player Rectangle And E Tile
            var tileECollision = player.Rectangle.Intersects(tileE.TileRectangle);

            // Creates A New Rectangle For TileSE
            tileSE.TileRectangle = new Rectangle((splatterTileX + 1) * Engine.TileWidth,
                                              (splatterTileY + 1) * Engine.TileHeight,
                                              Engine.TileWidth,
                                              Engine.TileHeight);

            // Tile Collision Detection Between Player Rectangle And SE Tile
            var tileSECollision = player.Rectangle.Intersects(tileSE.TileRectangle);

            // Creates A New Rectangle For TileS
            tileS.TileRectangle = new Rectangle(splatterTileX * Engine.TileWidth,
                                              (splatterTileY + 1) * Engine.TileHeight,
                                              Engine.TileWidth,
                                              Engine.TileHeight);

            // Tile Collision Detection Between Player Rectangle And S Tile
            var tileSCollision = player.Rectangle.Intersects(tileS.TileRectangle);

            // Creates A New Rectangle For TileSW
            tileSW.TileRectangle = new Rectangle((splatterTileX - 1) * Engine.TileWidth,
                                              (splatterTileY + 1) * Engine.TileHeight,
                                              Engine.TileWidth,
                                              Engine.TileHeight);

            // Tile Collision Detection Between Player Rectangle And SW Tile
            var tileSWCollision = player.Rectangle.Intersects(tileSW.TileRectangle);

            // Creates A New Rectangle For TileW
            tileW.TileRectangle = new Rectangle((splatterTileX - 1) * Engine.TileWidth,
                                              splatterTileY * Engine.TileHeight,
                                              Engine.TileWidth,
                                              Engine.TileHeight);

            // Tile Collision Detection Between Player Rectangle And Current Tile
            var tileWCollision = player.Rectangle.Intersects(tileW.TileRectangle);

            // Creates A New Rectangle For TileNW
            tileNW.TileRectangle = new Rectangle((splatterTileX - 1) * Engine.TileWidth,
                                              (splatterTileY - 1) * Engine.TileHeight,
                                              Engine.TileWidth,
                                              Engine.TileHeight);

            // Tile Collision Detection Between Player Rectangle And Current Tile
            var tileNWCollision = player.Rectangle.Intersects(tileNW.TileRectangle);

            // Allow Sprite To Occupy More Than One Tile
            if (tileNCollision && tileN.TileBlocked == false)
            {
                tileN.TileOccupied = true;
            }
            if (tileECollision && tileE.TileBlocked == false)
            {
                tileE.TileOccupied = true;
            }
            if (tileSCollision && tileS.TileBlocked == false)
            {
                tileS.TileOccupied = true;
            }
            if (tileWCollision && tileW.TileBlocked == false)
            {
                tileW.TileOccupied = true;
            }

            // Player Up
            if (keyState.IsKeyDown(Keys.W) ||
                (gamePadOneState.DPad.Up == ButtonState.Pressed))
            {
                player.CurrentAnimation = AnimationKey.Up;

                if (tileN.TileOccupied == false)
                {
                    if (tileNWCollision && tileNW.TileBlocked || tileNCollision && tileN.TileBlocked || tileNECollision && tileNE.TileBlocked)
                    {
                        playerMotion.Y = 0;
                    }
                    else
                        playerMotion.Y = -1;
                }
                else if (tileN.TileOccupied)
                {
                    if (tileNWCollision && tileNW.TileBlocked || tileNECollision && tileNE.TileBlocked)
                    {
                        playerMotion.Y = 0;
                    }
                    else
                        playerMotion.Y = -1;
                }
            }

            // Player Down
            if (keyState.IsKeyDown(Keys.S) ||
                (gamePadOneState.DPad.Down == ButtonState.Pressed))
            {
                player.CurrentAnimation = AnimationKey.Down;

                // Check Collision With Tiles
                if (tileS.TileOccupied == false)
                {
                    if (tileSWCollision && tileSW.TileBlocked || tileSCollision && tileS.TileBlocked || tileSECollision && tileSE.TileBlocked)
                    {
                        playerMotion.Y = 0;
                    }
                    else
                        playerMotion.Y = 1;
                }
                else if (tileS.TileOccupied)
                {
                    if (tileSWCollision && tileSW.TileBlocked || tileSECollision && tileSE.TileBlocked)
                    {
                        playerMotion.Y = 0;
                    }
                    else
                        playerMotion.Y = 1;
                }
            }

            // Player Left
            if (keyState.IsKeyDown(Keys.A) ||
                (gamePadOneState.DPad.Left == ButtonState.Pressed))
            {
                player.CurrentAnimation = AnimationKey.Left;

                if (tileW.TileOccupied == false)
                {
                    if (tileNWCollision && tileNW.TileBlocked || tileWCollision && tileW.TileBlocked || tileSWCollision && tileSW.TileBlocked)
                    {
                        playerMotion.X = 0;
                    }
                    else
                        playerMotion.X = -1;
                }
                else if (tileW.TileOccupied)
                {
                    if (tileNWCollision && tileNW.TileBlocked || tileSWCollision && tileSW.TileBlocked)
                    {
                        playerMotion.X = 0;
                    }
                    else
                        playerMotion.X = -1;
                }
            }

            // Player Right
            if (keyState.IsKeyDown(Keys.D) ||
                (gamePadOneState.DPad.Right == ButtonState.Pressed))
            {
                player.CurrentAnimation = AnimationKey.Right;

                if (tileE.TileOccupied == false)
                {
                    if (tileNECollision && tileNE.TileBlocked || tileECollision && tileE.TileBlocked || tileSECollision && tileSE.TileBlocked)
                    {
                        playerMotion.X = 0;
                    }
                    else
                        playerMotion.X = 1;
                }
                else if (tileE.TileOccupied)
                {
                    if (tileNECollision && tileNE.TileBlocked || tileSECollision && tileSE.TileBlocked)
                    {
                        playerMotion.X = 0;
                    }
                    else
                        playerMotion.X = 1;
                }
            }

I have my tile detection setup so the 8 tiles around the sprite are the only ones detected. The collision variable is true if the sprites rectangle intersects with one of the detected tiles. The sprites origin is centered at 16, 16 on the image so whenever this point goes over to the next tile it calls the surrounding tiles.

I am trying to have collision detection like in the game Secret of Mana.

If I remove the diagonal checks the sprite will pass through thoses tiles because whichever tile the sprites origin is on will be the detection center. So if the sprite is near the edge of the tile and then goes up it looks like half the sprite is walking through the wall.

Is there a way for the detection to occur for each tile the sprite's rectangle touches?

© Game Development or respective owner

Related posts about XNA

Related posts about c#