Saturday, 28 February 2009

Tutorial 1: Levels

Ok, so the first part of our tower defence that we are going to make is a simple terrain class. Please note that most of this code is going to be from Nick Gravelyn’s Tile Engine Series. I’m assuming you know how to create a new project so I’ll skip that bit.

Ok, so to start off with create a new class called “Level.cs”. This is going to be what holds all the information about our level (which textures go where basically).

First add the following using statements to the top of “Level.cs”

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Graphics;

Now, go ahead and add a new 2-Dimensional array to the top of your “Level.cs” class.

int[,] map = newint[,]

{

{0,0,1,0,0,0,0,0,},

{0,0,1,1,0,0,0,0,},

{0,0,0,1,1,0,0,0,},

{0,0,0,0,1,0,0,0,},

{0,0,0,1,1,0,0,0,},

{0,0,1,1,0,0,0,0,},

{0,0,1,0,0,0,0,0,},

{0,0,1,1,1,1,1,1,},

};

This array is going to be used to map out our level. Imagine that each one of these numbers is a texture. In this data a zero means draw a grass texture whereas a one means draw a path. So the above array would translate into something like this:

So, how do we turn this array of numbers into the above image? Well that’s simple. First off add a new list underneath the array.

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

This list is going to contain our two textures: grass and path. Now because our level class does not have access to the ContentManager we will pass in the textures using a new method called “AddTexture” which looks like the following:

publicvoid AddTexture(Texture2D texture)

{

tileTextures.Add(texture);

}

All this method does is add a texture to our texture list. So, now we have a list of textures, but how do we make them be drawn and how do we make them be drawn so they correspond to our array? Well this is all done in our draw method. But before we move on to the draw method we need to add to more variables to the top of our class:

publicint Width

{

get { return map.GetLength(1); }

}

publicint Height

{

get { return map.GetLength(0); }

}

What these two variables do is get the width and height of our level by retreiving our array’s row length(how many numbers are in a row) and column length(how many numbers are there in a column).

Now add a new method called “Draw” at the end of the class:

publicvoid Draw(SpriteBatch batch)

{

for (int x = 0; x < Width; x++)

{

for (int y = 0; y < Height; y++)

{

int textureIndex = map[y, x];

if (textureIndex == -1)

continue;

Texture2D texture = tileTextures[textureIndex];

batch.Draw(texture, new Rectangle(

x * 32, y * 32, 32, 32), Color.White);

}

}

}

This is where the magic happens.

First of all we itterate through each of the numbers in our array. Then we find out if it’s one or zero and store this value in an integer named textureIndex.

Next we find the texture that needs to be drawn. We do this by getting the texture from tileTextures which is at the texture index. Then all we do is draw the texture. All the textures we are going to be using are going to be 32 by 32 pixels.

So, how do we know where to draw the texture? Well we know the x and y coordinates of the texture in the array. So all we need to do is transform the array coordiantes into screen coordinates by multiplying the x and y coordinates by the texture width or height which is 32.

So, we now have a basic level class which we can expand upon later. So let’s incorporate this into our “Game1.cs” class.

First off save these two images into your projects Content folder and then add them into your project:

Then at the top of “Game1.cs” add

Level level = new Level();

Next add these lines of code to your "Game1()" constructor to make sure you game window is an appropriate size:

graphics.PreferredBackBufferWidth = level.Width * 32;

graphics.PreferredBackBufferHeight = level.Height * 32;

graphics.ApplyChanges();

IsMouseVisible = true;

Remember the level’s width and height needs to be multiplied by 32 to bring the value out of array space and into screen space.

Then in the “LoadContent()” method add the following code :

Texture2D grass = Content.Load<Texture2D>("grass");

Texture2D path = Content.Load<Texture2D>("path");

level.AddTexture(grass);

level.AddTexture(path);

Note: the order you add your textures is important. The order you add your textures in corresponds to the numbers in our level array.

Now, in the “Draw” method add

spriteBatch.Begin();

level.Draw(spriteBatch);

spriteBatch.End();

And there we have it. Next time we will be creating a level editor to create new levels and edit existing ones. Thanks for reading. Feel free to comment or ask any questions.

// The width of the level in pixels graphics.PreferredBackBufferWidth = level.Width * 32; // The height of the toolbar + the height of the level in pixels graphics.PreferredBackBufferHeight = 32 + level.Height * 32;

graphics.ApplyChanges();

IsMouseVisible = true;}

Are you using the images from the tutorial? If not you will need to replace 32 with the size of your textures.

How can I make it so there are more then just 1 map available? Ive got it so that I can change an int in Visual Studio, then when it fires the constructor for Level(), it picks a diffrent map and set of waypoints for it, but that wouldnt work in game, is there a way for it to happen at like a title screen sort of menue? Id think the whole logic of the game constuction would have to be modded though

Nevermind, I figured it out, It was alot simpler then I thought it would be, All you need to do is set level.map to something new, and clean out the waypoints and add in new ones. This can also happen on the fly as I figured out, For example whenever my game is running, you can press N and it fires this method in level

Something even more flexible and more 'best practice' I guess is to actually have it read the maps from say an XML or even a CSV file. That way you won't have to even touch code or recompile in order to change the maps.

hey i'd like help with this. i'm using xna 4.0 and i keep getting weird errors like missing ) or level does not exist in this current context. i've double and triple checked the code and its exact. what am i doing wrong?

hi firefly, im interested in TD games, but not so much in the programing as in the "strategy" and level aspect - how do you coordinate the difficulty levels of the game? keeping the game playable? not to easy, not to hard? finding that equilibrium between the defense option s and the enemy? is the flw of enemies random? whats the mathematical system behind the vary levels?

do you know where i can read about that? thanks, nadav (nadi.bar@gmail.com)

I doubt Firefly is still checking this - but I'll give it a shot in case anyone else is. Currently the levels are "small" (my playing window is only about 4-5in height/width - if I wanted to make this "larger" and add more blocks to the level, how would I do that? I assume it would be something to do with the *32, but that part kind of loses me....

Ok - For some reason I guess I just wasn't understanding that the texture of the image was 32 pixels - if I created a larger one, lets say a 50x50 pixel texture, I could just * by 50 instead of 32 then? That would work out the same?

I may have a few more questions on the other tutorials since I know you see this, I ran them all up to the point of shooting (I am on the waves tutorial) but wanted to go back and really try to expand on each one to learn a bit more, so thank you again!

@Brock Hmm when you copied the code over a } must have been missed somewhere. I would suggest making sure every { has a corresponding }. If that is the case then there must me an extra } somewhere, my guess would be just above the draw method.

Sorry for blowing up your blog :/ feel like a real nag. I'm still not sure exactly what the problem was but apparently there was an issue with my Draw() method. I copy/pasted just the Draw() from your source code and now the tiles are drawn when the application is compiled.

Thanks so much for your help! Its really appreciated. I only started trying to learn programming about a month ago and I'm "sort of" starting to get the hang of it. Your tutorials really are a godsend!

I'm not sure if u read these anymore but im making a TD game with my friend. He has made me some textures but the forest around the grid is going to be hard to tile he said and won't look as good. I was wondering if it was possible for me to have the border be an image and the center be textures? Thanks a lot!!!

I encountered one error though :In the first picture of this tutorial you showed how the map will look like with this map-array.But if i start the game, my level looks like i would have filled the array like this :0000000000000000110001110110110100111001000000010000000100000001

Hi,First of all i want to really thank you for taking your time to make this tutorial, i actually have a project due for in a couple of months, and i seem to be getting the same error all the time which is: "The type or namespace 'Level' could not be found", I tried searching for mistypes but haven't found anything. What do you suggest i do?

Hey Firefly, can you give some tips on handling multiple levels?One of the above comments does suggest an easy method, like using different numbers in the same map to denote different levels, but is there an easier approach>