简体   繁体   中英

Tilemap collision detection C# XNA

I am trying to make a very simple terraria-like game in C# XNA for a school project. I have very limited time otherwise I would probably have spent more time trying to figure this out myself. I created a tilemap but I just can't figure out how to make the tiles "solid" and not passable.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content; 
using Microsoft.Xna.Framework.GamerServices; 
using Microsoft.Xna.Framework.Graphics; 
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace TileEngine {
     public class Game1 : Microsoft.Xna.Framework.Game
     {
         GraphicsDeviceManager graphics;
         SpriteBatch spriteBatch;

         Texture2D character;
         Vector2 cPosition;
         Rectangle cBounds, t1Bounds;

         List<Texture2D> tileTextures = new List<Texture2D>();

         int[,] tileMap = new int[,]
         {
             { 0, 1, 1, 0, 2, 1, 1, 1, 0, 1, },
             { 0, 1, 1, 0, 2, 1, 1, 1, 0, 1, },
             { 0, 1, 1, 0, 2, 1, 1, 1, 0, 1, },
             { 0, 1, 1, 0, 2, 1, 1, 1, 0, 1, },
         };

         int tileWidth = 64;
         int tileHeight = 36;

         int cameraPositionX = 0;
         int cameraPositionY = 0;

         int vSpeed = 0;

         public Game1()
         {
             graphics = new GraphicsDeviceManager(this);
             Content.RootDirectory = "Content";
         }

         protected override void Initialize()
         {
             IsMouseVisible = true;

             graphics.IsFullScreen = false;
             graphics.PreferredBackBufferWidth = 1280;
             graphics.PreferredBackBufferHeight = 720;
             graphics.ApplyChanges();

             cPosition = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2 - 15, graphics.GraphicsDevice.Viewport.Height / 2 - 20);

             cBounds = new Rectangle((int)(cPosition.X), (int)(cPosition.Y), character.Width, character.Height);

             base.Initialize();
         }
         protected override void LoadContent()
         {
             // Create a new SpriteBatch, which can be used to draw textures.
             spriteBatch = new SpriteBatch(GraphicsDevice);

             Texture2D texture;

             character = Content.Load<Texture2D>("Tiles/character");

             texture = Content.Load<Texture2D>("Tiles/green");
             tileTextures.Add(texture);

             texture = Content.Load<Texture2D>("Tiles/red");
             tileTextures.Add(texture);

             texture = Content.Load<Texture2D>("Tiles/blue");
             tileTextures.Add(texture);

             cBounds = new Rectangle((int)(cPosition.X), (int)(cPosition.Y),  
 character.Width, character.Height);

             // TODO: use this.Content to load your game content here
         }
         protected override void UnloadContent()
         {
             // TODO: Unload any non ContentManager content here
         }


         protected override void Update(GameTime gameTime)
         {

             if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                 this.Exit();

             KeyboardState keyState = Keyboard.GetState();

             vSpeed += 1;
             cameraPositionY += vSpeed;

             if (keyState.IsKeyDown(Keys.Right))
                 cameraPositionX += 5;
             if (keyState.IsKeyDown(Keys.Left))
                cameraPositionX -= 5;
             if (keyState.IsKeyDown(Keys.Space))
                 vSpeed = -15;

             if (cBounds.Intersects(t1Bounds))
             {
                 cameraPositionY = 0;
                 vSpeed = 0;
             }    

             base.Update(gameTime);
         }


         protected override void Draw(GameTime gameTime)
         {
             GraphicsDevice.Clear(Color.CornflowerBlue);

             spriteBatch.Begin();

             int tileMapWidth = tileMap.GetLength(1);
              int tileMapHeight = tileMap.GetLength(0);

             spriteBatch.Draw(character, cPosition, Color.White);

             for (int x = 0; x < tileMapWidth; x++)
             {
                 for (int y = 0; y < tileMapHeight; y++)
                 {
                     int textureIndex = tileMap[y, x];
                     Texture2D texture = tileTextures[textureIndex];

                     spriteBatch.Draw(
                         texture, t1Bounds =
                         new Rectangle(
                             320 + x * tileWidth - cameraPositionX,
                             540 + y * tileHeight - cameraPositionY,
                             tileWidth,
                             tileHeight),
                         Color.White);
                 }
             }

             spriteBatch.End();

             base.Draw(gameTime);
         }
     } }

As you can see I tried to make Rectangles around all the sprites and detect when they intersect, but it doesn't seem to work. And even if I get the Rectangle-thing to work I just don't know what to do if they intersect. If I set the velocity to 0 then it will still slowly "fall" through the blocks as there is a default vertical acceleration.

First, you need to create a simple class for your tiles like this:

Class Tile
{
public int type; //Holds the ID to the specific texture.
public bool collision; //If true you should check for collision if the player is close.
public int health; //You probably need this since rock is harder to break then dirt.
}

Then create an array with Tiles. Now you have to check if the player is close to a collidable tile. You need a function that converts world space to tile space and put your player coordinates in it, check each frame for a couple of tiles around the player. If you check the complete map for collision your FPS will drop to .001, likewise for drawing all the tiles. Some pseudo code (already on tile level):

for (int y = player.y-4;y <= player.y+4;y++) 
{ //If your player is just the size of a single tile then just -2/+2 will do. 9*9 is already an expensive 81 rectangles to check.
    for (int x = player.x-4;x <= player.x+4;x++)
    {
        if (map[x,y].collision){
        if (new Rectangle(x*tilewidth,y*tileheight,tilewidth,tileheight).intersects(player.rectangle)){
        //Check farthest valid position and put player there
        }}

    }
}

The best thing to do is add in a newPosition property and before moving the player to this newPosition you have to check if the position is valid.

Other then that, if you do not have the time then the best advice is to not create a terraria like game. Even the simplest of terraria like game will be time consuming. Since you do not know the basics of collision i suggest making a pong or arkanoid clone that is pretty much how we all started out.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM