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).
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 = new int[,]
{
{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:
public void 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:
public int Width
{
get { return map.GetLength(1); }
}
public int Height
{
get { return map.GetLength(0); }
}
Now add a new method called “Draw” at the end of the class:
public void 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();
graphics.PreferredBackBufferWidth = level.Width * 32;
graphics.PreferredBackBufferHeight = level.Height * 32;
graphics.ApplyChanges();
IsMouseVisible = true;
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 addspriteBatch.Begin();level.Draw(spriteBatch);
spriteBatch.End();
The source code for this tutorial can be downloaded here.
XNA? WOW play the TD on the XBOX will be fantasy !
ReplyDeletewaiting for the continue post !!!
WOOH! THANK YOU
ReplyDeletePlease carry on
kitty, kitty. On the wall, where are you now?
ReplyDeleteError 1 'TowerDefense.Level' does not contain a constructor that takes '0' arguments
ReplyDeleteIs there any reason why I would get this error, I followed the tutorial step by step but I can't seem to fix this error.
The level class should either contain no constructor or at least have one like this :
ReplyDeletepublic Level()
{
}
Hope that helps!
I keep getting an error saying "Cannot find file grass" or something like that. Don't understand why because the png is in the project
ReplyDelete"batch.Draw(Texture" "new Rectangle"
ReplyDeleteare giving me errors.
in Game1.cs --
"Level.Width" "Level.Height"
are giving me errors.
Hi, i need some help. When i take out these lines:
ReplyDeletegraphics.PreferredBackBufferWidth = level.Width * 32;
graphics.PreferredBackBufferHeight = level.Height * 32;
graphics.ApplyChanges();
it works fine, but the actual tiles only take up about 1/4 of my screen. Maybe i am putting those lines of code in the wrong spot or something? can you help me please?
Hi,
ReplyDeleteWhat happens when when you don't take out those lines?
Those lines should be in the Game1 constructor:
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// 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.
Hope that helps!
Thanks Firefly08,
ReplyDeleteuseful stuff.
This comment has been removed by the author.
ReplyDeleteNevermind, I found it! Great tutorial.
ReplyDeleteawesome tutorial
ReplyDeleteHow 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
ReplyDeleteNevermind, 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
ReplyDeletepublic void ChangeMap()
{
map = new int[,]
{
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0},
{0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,3,0,0,0,0},
{0,0,0,0,4,0,0,0,0,2,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
{0,0,3,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
{0,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,1,1,1,1,1,1,0,0,0,4,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,4,0,0,0,0,0,0,0,4,0,0,1,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,1,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
waypoints = new Queue();
waypoints.Enqueue(new Vector2(0, 19) * 32);
waypoints.Enqueue(new Vector2(3, 0) * 32);
waypoints.Enqueue(new Vector2(3, 3) * 32);
waypoints.Enqueue(new Vector2(21, 3) * 32);
waypoints.Enqueue(new Vector2(21, 9) * 32);
waypoints.Enqueue(new Vector2(16, 9) * 32);
waypoints.Enqueue(new Vector2(16, 19) * 32);
}
Now if you wanted to take that a step further, having 3, or 4, or really as many maps as you want, just modify the method to accept a paramater, such as
Press N : Fires
ChangeMap(2)
back in level
public void ChangeMap(int battlefield)
{
if (battlefield == 1)
code for map 1
if (battlefield == 2)
code for map 2
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.
Deletehey 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?
ReplyDeletegraphics.PreferredBackBufferWidth = level.Width * 32;
ReplyDeletegraphics.PreferredBackBufferHeight = level.Height * 32;
graphics.ApplyChanges();
Error is showing near 32.....and its telling NullHandlingReference,
Wat to do???
hi firefly,
ReplyDeleteim 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)
Level level = new Level(); gives the folowing error to me:
ReplyDeleteError 1 The type or namespace name 'Level' could not be found (are you missing a using directive or an assembly reference?) \WindowsGame1\WindowsGame1\WindowsGame1\Game1.cs
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....
ReplyDeleteHey, Don't worry all comments get sent to my email so I still know if people ask questions!
ReplyDeleteIf you want to make the actual block textures bigger, then you would need to change 32 to the new size of the image you're using (At the moment the tutorial uses images that are 32x32).
If you want to add more blocks to the level then you need to change the level array at the top i.e either add more columns or more rows!
I hope that helped! :)
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?
ReplyDeleteI 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!
I have deleted some previous comments because I found and corrected my typo.
ReplyDeleteThe problem I am having now is that when the application launches it does not draw the tiles (grass and path) I just see the background color.
The Draw() method says "outside the bounds of the array"
ReplyDeleteI'm getting the same problem as Brock Laster, any help would be greatly appreciated.
ReplyDeleteHmm that's odd, which line is causing the error?
ReplyDeleteIf it is the line:
Texture2D texture = tileTextures[textureIndex];
Then you need to make sure that you are calling the AddTexture methods in LoadContent.
If it is:
int textureIndex = map[y, x];
Then make sure that Width and Height look like the code I posted above.
If neither of those suggestions fix your problem, I just uploaded some source code for this tutorial that you can compare your code with!
Let me know if this helps!
@Firefly
ReplyDeleteThanks for replying! In an act of frustration I copy/pasted your game1.cs source code over my own code and the solution will not build.
protected override void Draw(GameTime gameTime) causes error: Error 1 Expected class, delegate, enum, interface, or struct
I found my error thanks to your source code, I place brackets containing code for an if statement by habit, it was locking out access to the class. Thanks for the help! =)
ReplyDelete@Richard I'm glad you got it sorted!
ReplyDelete@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.
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteHmm, are you sure that the Width and Height properties are exactly the same as mine? With Width returning GetLength(1) and Height returning GetLength(0).
ReplyDeleteAlso have you checked that in your for loop you are doing y < Height and x < Width?
Ah yeah I see your problem was using <= instead of <. I guess you have seen that yourself now? :)
ReplyDeleteSorry 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.
ReplyDeleteThanks 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!
Thanks you!
It's no problem man, we all had to learn at some point! I think the problem was that in the draw method your for loop looked like:
ReplyDeletefor (int x = 0; x <= Width; x++)
when it should have looked like
for (int x = 0; x < Width; x++)
Not the difference between < and <=, < means that x will never be equal to or be greater than Width, whereas <= means that x could be equal to but will never be greater than Width!
Hopefully that makes some sense.
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!!!
ReplyDeleteFirst of all, thank you for those great tutorials!
ReplyDeleteI 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 :
00000000
00000000
11000111
01101101
00111001
00000001
00000001
00000001
it solved this error by changing the x and y in the Draw-Method :
public void Draw(SpriteBatch batch)
{
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
//Some Code here//
batch.Draw(texture, new Rectangle(
y * 32, x * 32, 32, 32), Color.White);
}
}
}
Is this the right way or am i missing something?
I missed something :-)
DeleteI accidentally swapped the x and y here :
int textureIndex = map[y, x]; (tutorial version)
int textureIndex = map[x, y]; (my version)
i got an error on
ReplyDeleteLevel level = new Level();
Level could not be found
Hi,
ReplyDeleteFirst, thanks, it's a very useful tutorial.
I have a problem, SpriteBatch can't be resolved, even if I added the two references needed. Can you help me with this? It seems that microsft.xna.framework.graphics does not contains that class.
Thnaks !
Hmm that is strange, it definitely should be contained in Microsoft.Xna.Framework.Graphics... If you download the source at the end of the tutorial does that run ok?
ReplyDeleteThanks for your answer! I took the wrong dll to add in my project. I needed those in Windows/Micxrosoft .Net/XNA. Works like a charm now.
ReplyDeleteHi,
ReplyDeleteFirst 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?
Awaiting your swift reply,
Eran Bodokh.
Hey Firefly, can you give some tips on handling multiple levels?
ReplyDeleteOne 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>