This tutorial will mainly focus on how the player will add in new towers through the addition of a new class called “Player.cs”.
The Player class will hold all the information about a specific player, such as how much money he has, what stage he is on etc. The reason we are putting all this information in it’s own class and not just in “Game1.cs” is that if we do decide to make this game multiplayer, it will make it much easier to store information about each person playing.
So let’s get started, the first thing we will do as I'm sure you have guessed is add a new class called “Player.cs”. To start with we are going to add a few fields and properties :
private int money = 50;
private int lives = 30;
private List<Tower> towers = new List<Tower>();
private MouseState mouseState; // Mouse state for the current frame
private MouseState oldState; // Mouse state for the previous frame
public int Money
{
get { return money; }
}
public int Lives
{
get { return lives; }
}
Next we are going to add in one more field and a constructor for the class :
private Level level;
public Player(Level level)
{
this.level = level;
}
Now we are going to add in an Update method for our Player, this is where we will handle the creation of new towers, and also the updating of existing towers.
private int cellX;
private int cellY;
private int tileX;
private int tileY;
public void Update(GameTime gameTime, List<Enemy> enemies)
{
mouseState = Mouse.GetState();
cellX = (int)(mouseState.X / 32); // Convert the position of the mouse
cellY = (int)(mouseState.Y / 32); // from array space to level space
tileX = cellX * 32; // Convert from array space to level space
tileY = cellY * 32; // Convert from array space to level space
oldState = mouseState; // Set the oldState so it becomes the state of the previous frame.
}
Right, I know this method looks completely pointless but I assure you it’s not!! The first and last lines are quite straight forward, we just update the mouseState so it is correct for the current frame, and update the oldState so it is correct for the previous frame. Now I’m sure your asking your self why are we dividing the mouse position by 32 only to multiply it by 32 again.
The reason if quite simple really, if you think about what happens when you divide a floating point number by another floating point number, and then cast it to and integer, you will get the integer part number of the number. So lets take a look at an example :
In the above example if the mouse was at position (77, 114) and we use the above equation to calculate where that is in array space we get the following :
CellX = (int) (77 / 32)
= (int) (2.40625)
= 2
Which is correct, as we can see in the image the point is in the third square along. Now we know what cell the pointer is in we can work out where that cell is in level space by multiplying it by 32 (The the widow of our tiles)
TileX = 2 * 32 = 64
Which is the level space position of the top right corner of the 3rd tile along, so hopefully now you can see why this works.
public int GetIndex(int cellX, int cellY)
{
if (cellX < 0 || cellX > Width || cellY < 0 || cellY > Height)
return 0;
return map[cellY, cellX];
}
All this code does is return the index of the requested cell. We can use this index to check if we are on a path or not. Right, now we are ready to add the following method to “Player.cs” :
private bool IsCellClear()
{
bool inBounds = cellX >= 0 && cellY >= 0 && // Make sure tower is within limits
cellX < level.Width && cellY < level.Height;
bool spaceClear = true;
foreach (Tower tower in towers) // Check that there is no tower here
{
spaceClear = (tower.Position != new Vector2(tileX, tileY));
if (!spaceClear)
break;
}
bool onPath = (level.GetIndex(cellX, cellY) != 1);
return inBounds && spaceClear && onPath; // If both checks are true return true
}
Now, as you may have noticed in this method we are trying to access tower.Position, but we never actually created that property in the sprite class… my bad. So lets go to “Sprite.cs” and add the following property :
public Vector2 Position
{
get { return position; }
}
private Texture2D towerTexture;
Now we need to modify the constructor slightly to allow for a tower texture to be passed in :
public Player(Level level, Texture2D towerTexture)
{
this.level = level;
this.towerTexture = towerTexture;
}
And that’s all the fields we will add for now. Next, in the Update method add the following just before where we set oldState :
if (mouseState.LeftButton == ButtonState.Released
&& oldState.LeftButton == ButtonState.Pressed)
{
if (IsCellClear())
{
Tower tower = new Tower(towerTexture, new Vector2(tileX, tileY));
towers.Add(tower);
}
}
We are almost finished with the Player class, but there is one big thing that we are missing, have you spotted it yet? At no point are our towers updated, we will handle this now, just under the code we just added, add the following :
foreach (Tower tower in towers)
{
if (tower.Target == null)
{
tower.GetClosestEnemy(enemies);
}
tower.Update(gameTime);
}
public void Draw(SpriteBatch spriteBatch)
{
foreach (Tower tower in towers)
{
tower.Draw(spriteBatch);
}
}
//Tower tower;
Player player;
Texture2D towerTexture = Content.Load<Texture2D>("arrow tower");
//tower = new Tower(towerTexture, Vector2.Zero);
player = new Player(level, towerTexture);
Next replace the update method with this one :
protected override void Update(GameTime gameTime)
{
enemy1.Update(gameTime);
List<Enemy> enemies = new List<Enemy>();
enemies.Add(enemy1);
player.Update(gameTime, enemies);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
level.Draw(spriteBatch);
enemy1.Draw(spriteBatch);
player.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
Nice tut. Keep up the good work.
ReplyDeleteYou're back!
ReplyDeleteDoes the player class need to include:
ReplyDeleteusing Microsoft.Xna.Framework.Input;
Also, when I click on the window border of my program, it gives me an error that says:
An unhandled exception of type 'System.IndexOutOfRangeException' occurred in Tower Defense.exe
Additional information: Index was outside the bounds of the array.
Is there a way to avoid this?
hm when i click on empty cell nothing happens ;(
ReplyDeletedon't know where is the problem
Hey Sean, I'm not sure, if you delete it does it give you an error? ;)
ReplyDeleteI think this is a mistake on my part, if you go to the GetIndex method and change the if statement to look like this : if (cellX < 0 || cellX > Width - 1 || cellY < 0 || cellY > Height - 1)
return 0;
That should fix your problem :)
I got that error too. I was just cheap though and did a try/catch solution...
DeleteHi Anonymous, have you tried comparing you source code against the code I provided?
ReplyDeleteThere must have been an error when you where writing some of your code :) If that doesn't help just leave another comment and I will try and offer some more advice!
If I don't include Microsoft.Xna.Framework.Input; in the Player class the MouseState code does not work. It doesn't recognize the MouseState object type or Mouse.GetState() methods.
ReplyDeleteok i found where was an error, now working
ReplyDelete@Sean then you do need to include it :P
ReplyDeleteHi, when i do this it says that all the Enemy, Tower, Level, etc. all the ones with capital letters arent there in the player.cs?
ReplyDeleteOk hi, I was the one who posted that thing about the capital letter things, and it got fixed. Now, i can build the towers, but it doesn't attack the enemy? help please?
ReplyDeleteHi, when you say that the towers don't attack, do the towers just not move or do they not shoot bullets?
ReplyDeleteAt this stage in the series all the towers should do is rotate to track enemies. In the next tutorial we move on to adding bullets!
Let me know if that helps!
ok thanks, that helps part of it. but they also aren't rotating. Im gonna log in with my google account after this, and hopefully we can understand each other better.
ReplyDelete-Chicken21200
Well at least that's one problem solved! At the end of the last tutorial did your towers rotate?
ReplyDeleteIf not I would go back over that tutorial to make sure you didn't miss anything! If that isn't the case there may be something missing from your update method in the TowerManager class.
Let me know if this helps!
Will do, thanks.
ReplyDeleteIs it correct that i don't see a mouse cursor in the game window yet?
ReplyDeleteYou should be able to see it by now, but it is easy to make it appear, just add the following line to the Game1 constructor :
ReplyDeleteIsMouseVisible = true;
Thanks, i needed that in there. Tower shoots now and i understand most of whats going on.
ReplyDeleteOutstanding!
ReplyDeletewhat if you're going to use these codes for win7 mobile? which i need to use the touch screen rather than a mouse... do i need to change some part of the codes?
ReplyDeleteHey,
ReplyDeleteYou won't need to change any of the mouse code that I know of, it should just work!
This link should confirm that :
http://msdn.microsoft.com/en-us/library/bb197572.aspx
Hey,
ReplyDeleteYour tutorials are great but i seem to be having an error.
In the foreach loop in isCellClear method.
It tells me it doesn't know "Position".
'WindowsGame4.Tower' does not contain a definition for 'Position' and no extension method 'Position' accepting a first argument of type 'WindowsGame4.Tower' could be found (are you missing a using directive or assembly reference?)
Hey, would you be able to post your copy of just the loop that is throwing the error?
ReplyDeleteFireFly, getting same prob here as Anon. mar 14, 2012 03:15am
ReplyDeletecopy of my loop:
---------------
private bool IsCellClear()
{
bool inBounds = cellX >= 0 && cellY >= 0 && cellX < level.Width && cellY < level.Height;
bool spaceClear = true;
foreach (Tower tower in towers) // check that there is no tower here
{
spaceClear = (tower.position != new Vector2(tileX, tileY));
if (!spaceClear)
break;
}
bool onPath = (level.GetIndex(cellX, cellY) != 1);
return inBounds && spaceClear && onPath; //if both checks are true return true
}
----------------
error says that the position is not accessible due to it being protected.
I'm looking up my code. I'll let you know where I stumbled if you dont' come up with any suggestions.
This comment has been removed by the author.
DeleteNever mind last comment. I kept reading your tut and quickly noticed you addressed it.
ReplyDeleteSilly me... heheh
Does anyone have an idea how I would code this to work for xbox 360, trying to show my son how to program games and I've converted everything in your tutorial to xbox 360, but this part and since this is my first game, still not all that familiar with gamepad positioning as I would think you would move a sprit around instead of a "cursor". Any help is appreciated.
ReplyDeleteHi,
ReplyDeleteI have created a version of the project for you which *should* work on the xbox that will hopefully give you an idea of what needs to be done to get things working. However if you have any more questions or problems let me know!
http://www.mediafire.com/file/tp9473ace2qrn2e/TowerDefenseTutorial_6_Xbox.zip
I'm currently trying it out, I will give you an update as soon I can, thank you for your help, at first glance looks like I wasn't too far off, having to adapt some code as I'm using it inside a game state manager so some code is actually called from the gameplayscreen.cs vice game1.cs. Thank you again for your help!
ReplyDeleteI'm currently trying it out, I will give you an update as soon I can, thank you for your help, at first glance looks like I wasn't too far off, having to adapt some code as I'm using it inside a game state manager so some code is actually called from the gameplayscreen.cs vice game1.cs. Thank you again for your help!
ReplyDeleteIs it possible to have towers set at the beginning of the game?
ReplyDeleteI'm working with my group on a project for school and we're currently using your tutorials, which are a great help! But we want to make different type(color) monsters which are only vulnerable to the same color tower.
Hi FireFly your tutorials are great. One thing I cant figure out is when im clicking on an empty free space my tower is being placed at pixels 0,0.
ReplyDeleteI have already got rid of this line.(tower = new Tower(towerTexture, Vector2.Zero);) just so you know its not that.
Does anybody have any ideas.
Thanks, Liam Harley
Hmm that is strange, have you tried setting a breakpoint inside Player.Update() to make sure it's being called?
ReplyDeleteIf it is make sure cellX and cellY are being set to the values that you would expect.
If thats the case then try pointing a break point on the line where you create the tower (Tower tower = new ...) and make sure cellX and cellY still have the right values.
Good luck!
hmm is there supposed to be a cursor present because when ever i run the game there isn't one and when i click i get this error An unhandled exception of type 'System.StackOverflowException' occurred in WindowsGame1.exe
ReplyDeleteon the line
private bool IsCellClear()
{ <--- this line
bool inBounds = cellX >= 0 && cellY >= 0 && // Make sure tower is within limits
cellX < level.Width && cellY < level.Height;
and is there a way to make it full screen?
Delete