Subscribe to Six's Game Programming Adventures        RSS Feed
-----

Creating a tile engine in XNA Part 1

Icon 4 Comments
In this blog entry I will go over the basics of making a tile engine with C# and XNA. I used Microsoft Visual C# Express 2008 and XNA Game Studio 3.0 for this tutorial. I don’t see any reason why you couldn’t use XNA Game Studio 3.1 or even Microsoft Visual C# Express 2005 and XNA Game Studio 2.0 for this tutorial. To get started go a head and create a new XNA Windows game call it something other than TileEngine. I used SimpleTileEngine for my project.

Tiling is the process of making a large map using many small images called tiles. For this tutorial I will be using a 2 dimensional array of integers for the map. In later tutorials I will expand this tutorial to use custom maps. So you will need a few tiles to get started. I have used a few tiles from a tile set that I found on the Internet. You do not have to use these, you can use your own if you have them but you will have to change the asset names when you add them to your Content folder. You can find the tiles I used, in zip format, on my web site: There are 4 tiles: a grass tile, a dirt tile and a water tile.

Once you have the tiles you will be working with you will have to add them to the Content folder. You do that by right clicking the Content folder and choosing Add Existing Item and navigating to the tiles. You can select all of the tiles at once by holding down the Ctrl key and clicking them.

You need some way to index which tile you are going to be drawing. For this I will be using a List<Texture2D>. Why am I doing this instead of an array of Texture2D? The reason is using a generic List will allow the size grow and shrink with out having to change a lot of constants. Add the following variable at the class level. Put them near the SpriteBatch object.

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



You will have to load the tiles into this list. If you are familiar with XNA you know that you will do that in the LoadContent method. I named the tiles: grasstile.png, dirttile.png, snowtile.png and watertile.png. By default, the names of the assets you add to your Content folder are the file names with out the extensions. So the names of the assets are grasstile, dirttile, snowtile and watertile. I used a little short cut to add them to the List<Texture2D>. This is the code for the LoadContent method:

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            tiles.Add(Content.Load<Texture2D>("grasstile"));
            tiles.Add(Content.Load<Texture2D>("dirttile"));
            tiles.Add(Content.Load<Texture2D>("snowtile"));
            tiles.Add(Content.Load<Texture2D>("watertile"));
        }



It is really simple enough. I used the Add method of the List class and added the Texture2D returned by the Content.Load method to add the tiles to the list.

Before you can do any tiling you will need two more things. You will need values for the height and width of the tiles as well as a 2 dimensional array for the map. It is important to note that the height and width variables are not the actual width and height of the tiles. They are the height and width of the tiles on the screen. I chose 64 for the height and width of the tiles. That was just an arbitrary choice and you do not have to use these value. I also created a 2 dimensional array with a map.

        static int tileWidth = 64;
        static int tileHeight = 64;

        int[,] map = {
              {0, 1, 1, 2, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 1, 1, 2, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 1, 1, 2, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
              {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
                     };



If you look at the array of integers it is mostly zeros with six ones, twos and threes. It is important to note that the numbers are important. Since the first tile loaded into the List was grass 0 will represent a grass tile. 1 will represent and dirt tile, 2 a snow tile and 3 a water tile.

Now we are ready to tile our map. All of this will take place in the Draw method. The Draw method, as many of you probably know, is where rendering is done. 2D rendering is done between calls to the Begin and End methods of a SpriteBatch object. I will give you the Draw method and then explain why I did what I did.

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

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

            for (int y = 0; y < map.GetLength(0); y++)
            {
                for (int x = 0; x < map.GetLength(1); x++)
                {
                    spriteBatch.Draw(tiles[map[y, x]],
                        new Rectangle(
                            x * tileWidht,
                            y * tileHeight,
                            tileWidht,
                            tileHeight),
                        Color.White);
                }
            }

            base.Draw(gameTime);

            spriteBatch.End();
        }



The rendering takes place in nested for loops. The first loop will loop through each row in the array and the second will loop through each column. You might be screaming that I used 0 to get the row and 1 to get the column. This is a peculiarity when it comes to tiling. Usually arrays are written [x,y] but in tiling the values are reversed to [y,x]. This is just a quirk that I can’t explain. If you try and do it the other way around your map would come out rotated 90 degrees. You would even get an exception if the map wasn’t square.

To find out what tile I need to draw I use value of map[y, x] as the index of the tile in the list of tiles. To find out where to draw the tile you take the x value and multiply it by the width of the tiles to find the x coordinate and the y value and multiply it by the height of the tiles. To draw the tile I use an over load of the draw method that takes a destination rectangle. The destination rectangle will either stretch or shrink the tile to fill the rectangle if it is greater or less than the tile.

If you run the game now you will get the following screen. (It may take a while for the image to load.)
Posted Image

That is all fine and good but the map doesn’t scroll. This would be fine for a platform game where the map doesn't have to scroll but for a strategy game, role playing game or side scrolling game you want the map to be able to scroll. I will get to scrolling the map in my next entry.

4 Comments On This Entry

Page 1 of 1

Hellbroth 

11 November 2009 - 09:49 AM
Thanx awesome tutorial at last :D.

P.S in the

static int tileWidht = 64;

you have spelled the Width wrong fixit when you can so if others use this tutorial they don't get the error.

And something last can you put where you define the variables cause in the begging i wasn't sure if i put them in the right place.
0

SixOfEleven 

11 November 2009 - 11:52 AM
Thanks, I've made those changes to the post.
0

Moshambi 

23 January 2010 - 11:17 PM
Hi,

I am getting this error:

InvalidOperationException was unhandled.
Begin cannot be called again until End has been successfully called.


I am pretty sure I have everything typed in the way you have it in the tutorial.

Thanks for any help.


EDIT: Found the error, I was not calling spriteBatch.End();
0

Garnaal 

29 July 2010 - 01:56 PM
Well, someone pointed me to this and i have to say it's very good!

But there's one problem i've got!

I've created a class for a player with some simple stuff in it, position texture etc.

And i've added it to the tiles, but i don't seem to move it..
Any idea how to move the player from one tile to the other?

*edit*

I guess i need some kind of function to update the board?
If you want, i can post my code!
0
Page 1 of 1

February 2022

S M T W T F S
  12345
6 7 89101112
13141516171819
20212223242526
2728     

Recent Entries

Recent Comments

Search My Blog

0 user(s) viewing

0 Guests
0 member(s)
0 anonymous member(s)