2
votes

I have decided to have a go at making a dungeon crawler game with the Xna framework. I am a computer science student and am quite familiar with c# and .net framework. I have some questions about different parts of the development for my engine.

  1. Loading Maps

I have a tile class that stores the vector2 position, 2dtexture and dimensions of the tile. I have another class called tilemap that has a list of tiles that are indexed by position. I am reading from a text file which is in the number format above that matches the number to the index in the tile list and creates a new tile with the correct texture and position, storing it into another list of tiles.

public List<Tile> tiles = new List<Tile>(); // List of tiles that I have added to the game
public List<TileRow> testTiles = new List<TileRow>(); // Tilerow contains a list of tiles along the x axis along with there vector2 position.

Reading and storing the map tiles.

    using (StreamReader stream = new StreamReader("TextFile1.txt"))
                {
                    while (stream.EndOfStream != true)
                    {
                        line = stream.ReadLine().Trim(' ');
                        lineArray = line.Split(' ');
                        TileRow tileRow = new TileRow();
                        for (int x = 0; x < lineArray.Length; x++)
                        {  
        tileXCo = x * tiles[int.Parse(lineArray[x])].width;
        tileYCo =  yCo * tiles[int.Parse(lineArray[x])].height;
        tileRow.tileList.Add(new Tile(tiles[int.Parse(lineArray[x])].titleTexture, new Vector2(tileXCo,tileYCo)));
                        }
                        testTiles.Add(tileRow);
                        yCo++;
                    }
                }

For drawing the map.

 public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            foreach (TileRow tes in testTiles)
            {
                foreach (Tile t in tes.tileList)
                {
                    spriteBatch.Draw(t.titleTexture, t.position, Color.White);
                }
            }
        }

Questions: Is this the correct way I should be doing it, or should I just be storing a list referencing my tiles list?

How would I deal with Multi Layered Maps?

  1. Collision Detection

At the moment I have a method that is looping through every tile that is stored in my testTiles list and checking to see if its dimensions are intersecting with the players dimensions and then return a list of all the tiles that are. I have a derived class of my tile class called CollisionTile that triggers a collision when the player and that rectangle intersect. (public class CollisionTile : Tile)

public List<Tile> playerArrayPosition(TileMap tileMap)
        {  
            List<Tile> list = new List<Tile>();
            foreach (TileRow test in tileMap.testTiles)
            {
                foreach (Tile t in test.tileList)
                {
                    Rectangle rectangle = new Rectangle((int)tempPosition.X, (int)tempPosition.Y, (int)playerImage.Width / 4, (int)playerImage.Height / 4);
                    Rectangle rectangle2 = new Rectangle((int)t.position.X, (int)t.position.Y, t.width, t.height);
                    if (rectangle.Intersects(rectangle2))
                    {
                        list.Add(t);
                    }
                }
            }
            return list;
        }

Yeah, I am pretty sure this is not the right way to check for tile collision. Any help would be great.

Sorry for the long post, any help would be much appreciated.

2
You should only draw tiles that are in the player's view. As for the Multilayering you can do many approaches such as creating another class TileLayer which contains List<TileRow> with a ZIndex which you can use to draw or incorporate layers directly on the tiles by having each tile contain Entities that have ZIndex.Dustin Kingen
Please don't use tags (ex. RPG) unless you understand its meaning here on this site. RPG is a programming language used primarily by professional programmers, to build software used to run a large percentage of businesses we all take for granted.WarrenT

2 Answers

3
votes

You are right. This is a very inefficient way to draw and check for collision on your tiles. What you should be looking into is a Quadtree data structure.

A quadtree will store your tiles in a manner that will allow you to query your world using a Rectangle, and your quadtree will return all tiles that are contained inside of that Rectangle.

List<Tiles> tiles = Quadtree.GetObjects(rectangle);

This allows you to select only the tiles that need to be processed. For example, when drawing your tiles, you could specify a Rectangle the size of your viewport, and only those tiles would be drawn (culling).

Another example, is you can query the world with your player's Rectangle and only check for collisions on the tiles that are returned for that portion of your world.

For loading your tiles, you may want to consider loading into a two dimensional array, instead of a List. This would allow you to fetch a tile based on its position, instead of cross referencing it between two lists.

Tile[,] tiles = new Tile[,]
Tile tile = tiles[x,y];

Also, in this case, an array data structure would be a lot more efficient than using a List.

3
votes

For uniform sets of tiles with standard widths and heights, it is quite easy to calculate which tiles are visible on the screen, and to determine which tile(s) your character is overlapping with. Even though I wrote the QuadTree in Jon's answer, I think it's overkill for this. Generally, the formula is:

tileX = someXCoordinate % tileWidth;
tileY = someYCoordinate % tileHeight;

Then you can just look that up in a 2D array tiles[tileX, tileY]. For drawing, this can be used to figure out which tile is in the upper left corner of the screen, then either do the same again for the bottom right (+1), or add tiles to the upper left to fill the screen. Then your loop will look more like:

leftmostTile = screenX % tileWidth;    // screenX is the left edge of the screen in world coords
topmostTile = screenY % tileHeight;
rightmostTile = (screenX + screenWidth) % tileWidth;
bottommostTile = (screenY + screenHeight) % tileHeight;
for(int tileX = leftmostTile; tileX <= rightmostTile; tileX++)
{
    for(int tileY = topmostTile; tileY <= bottommostTile; tileY++)
    {
        Tile t = tiles[tileX][tileY];
        // ... more stuff
    }
}

The same simple formula can be used to quickly figure out which tile(s) are under rectangular areas.

IF however, your tiles are non-uniform, or you have an isometric view, or you want the additional functionality that a QuadTree provides, I would consider Jon's answer and make use of a QuadTree. I would try to keep tiles out of the QuadTree if you can though.