Vous êtes sur la page 1sur 17

TUTORIAL:

A TOWER DEFENSE GAME


<by ATHANATOS>

This is what your game should look like in the end. Please dont copy or distribute this tutorial in any way. Feel free to use it, and to copy the codes for your game, as long as you are the one who has done the work, not me. And please DONT make simple rip-offs for competitions. Id hate it if someone else would win $1000 while I have done the boring part of the making of it. Make sure it remains your own game, and else, please give me credit too. Thank you for using this tutorial.

A few weeks ago, I made the TD game Interceptor. Since then, I have received some messages about how I had made it. Thats why I wrote this tutorial for the people who want to make their own TD game with Game Maker 6 or 7. It can be made with GM Lite and Pro. NOTE: this is a new tutorial: the examples are made largely with GML and have more comment added, and now Ive included an upgrade system. The wave system has also been updated. Now you have one wave at a time. When you make a TD game, you need some basic content: Towers page 2-5 Enemies page 6 A path (or more) for the enemies page 7-8 A building system page 9-13 A wave system page 14-15 An upgrade system page 16 Also, please read the Final Words on page 17 when youre done.

Towers
The first thing you need when you play tower defense games is of course: towers. They automatically attack the enemies in range. Ill describe two kinds of towers: laser towers and bullet-firing towers. In the first steps of the tutorial, well assume that the towers already exist, so you dont have to build them yet. Thats easier if you want to test if they work. OK first, we make two sprites for towers: one for the laser and another for the bullet-firing tower. If you have GM Pro, you can make sprites that can rotate nicely, with the origin in the center. Next, we make two objects, one for each tower. We give them both a depth of -2 and we make the laser tower the parent of the bullet-firing tower.

Laser towers
This tower will fire a continuous laser at the nearest target. The laser will be a simple drawn line. Therefore, we need a variable to indicate whether the tower is firing or not, which will be: firing (set to 0). We also add a variable, target (set to noone), which will indicate the enemy that is being attacked. And finally, well add these variables: Range = 200 Rate = 3 Damage = 10
2

Price = 20 Name = Laser tower These variables indicate the range, firing rate (shots per second), damage, the building cost and the real name for the tower. You can change them if you like. Now its time to make the script which will make the tower attack enemies within range. __________________________________________________________
//Check if enemies are in range and activate firing if needed { if if if if { firing > 0 firing += 1; firing = 30/rate firing = 0; not instance_exists(target) target = noone; instance_exists(ob_enemy) if target = noone target = instance_nearest(x,y,ob_enemy); if point_distance(x,y,target.x,target.y) > range target = noone; else if firing = 0 firing = 1; = -3;

} if firing = 1 depth else depth = -2;

First, we check if the firing variable is larger than 0. If thats the case, it must increase until we think the tower can fire again. This is calculated using the rate. Next, we check if there are any enemies at all. Then, we check if the target is in range. If it isnt, the target can be reset. Otherwise, the firing sequence is started. And finally, the depth will be set to -3 if the tower is firing. Thats because when the laser fires, and another tower is in the way, it looks like you shoot through it. If you temporarily decrease the depth, the laser will fire above the tower. Now its time to make the visible laser beam. For this, we use the following script:
//Draw a laser beam and the tower { draw_sprite(sprite_index,0,x,y); if not instance_exists(target) target = noone; if (target != noone && firing = 1) { draw_set_color(c_red); draw_line_width(x,y,target.x,target.y,4); target.own_health -= damage; if target.own_health < 1 target = noone; draw_set_color(c_white); sound_play(sd_laser); }

Here we draw the sprite at its position. Then we check if a laser beam must be drawn. If so, we draw the beam first and then damage the target. The damage is done in the very end, because, if an enemy is nearly dead and the damage is too much, the targets instance is destroyed. But the index still exists, but it doesnt indicate an instance, which will get errors in the game. (By the way, I recommend making an enemy object already, and adapting it later. That way, you can test the towers if you want.)

Bullet-firing towers
Now its time to make a bullet-firing tower. One of the questions I got was about making towers shoot at moving targets without missing. Thats why I made a script for this. One big difference between lasers and bulletfiring towers is that bullet-firing towers need another object the bullet to damage their targets. It is a bit more difficult, because bullets can miss their targets, or they go through them. The reason bullets can go through enemy objects, is that they often go too fast. This is especially a problem if you dont have GM Pro, because if you have Pro, you can make a long-shaped bullet, which can be transformed, so that it still hits. This is when masks are useful. To make bullets hit easier, we make a mask for the bullet object, which is a black filled circle, with the diameter of the speed you want the bullet to go. Dont forget to set the origin in the center. Next, we make a bullet object, with depth -1 and the mask set. Also give it the variable damage If you havent already made an enemy object, its time to make one. You should give him the variable own_health (you decide the health amount) and set the depth to -1. After that it can just be a simple object which moves across the screen. But you need it to configure the bullet. In the collision event with an enemy, add the following actions: Set variable own_health for other object relative to -damage. Destroy the instance (self) If you want, you can also add an effect or explosion or something like that, and play a sound. Now its time to adapt the tower. Although it has the laser tower as its parent, we still need to do every event again. In the Create event, we first use the action Call Parent Event, to summon the variables. Then we change the variable name to Cannon. If you want, you can change other stats as well. The script in the Step event is slightly different. This time, we dont fire a laser, but bullets, so when we need to fire, we add the function of creating a bullet, and giving it the appropriate speed and direction. If you do that, the code becomes like this:
4

__________________________________________________________
//Check if enemies are in range and activate firing if needed { var ii, dist, xx, yy; if firing > 0 firing += 1; if firing = 30/rate firing = 0; if not instance_exists(target) target = noone; if instance_exists(ob_enemy) { if target = noone target = instance_nearest(x,y,ob_enemy); if point_distance(x,y,target.x,target.y) > range target = noone; else if firing = 0 { firing = 1; //Create a bullet ii = instance_create(x,y,ob_bullet); dist = point_distance(x,y,target.x,target.y)/24; xx = target.x+target.speed*dist*cos(target.direction*pi/180); yy = target.y-target.speed*dist*sin(target.direction*pi/180); ii.speed = 24; ii.direction = point_direction(x,y,xx,yy); ii.damage = damage; } } }

As you can see, the script becomes a bit longer, because of the bulletcreating functions. Also, some additional variables had to be used. The result is: a bullet is created when the tower should fire, and it heads to the point where the enemy should be at the moment the bullet should be nearest to the enemy, using its speed and direction. This can only be a problem if the enemy turns left or right. For the drawing event, we simply use the draw sprite action to draw the sprite and prevent the tower from using the lasers drawing functions. Now we have two towers, which should work nicely. The file tdtutorial1.gmk shows what your game should look like now. In this file, there are some enemies that move into random directions and jump back if they are dead or outside the room. Next, well talk about better enemies.

Enemies
Making enemies in tower defense games is quite easy. There are only a few things to set with enemies. First, it must be destroyable (it must have health). Second, it must follow the appropriate path. There is a simple system to make that. Lets start with the basics. The enemy object weve created is really simple. Now lets make it a bit more complicated. At the beginning of the game, the enemies have very little health. But as the game continues, you want the enemies to become harder to defeat. Thats why we give the enemies a second variable: health_def, the default health amount, which is the same as the own_health variable once the enemy is created. We need that to draw the relative health of the enemies. This is optional; you could also show smoke or spark effects if the health is half the starting amount. But in this tutorial, well use health bars. Well need this piece of code to draw them for every enemy: __________________________________________________________
//Draw the sprite and a healthbar { draw_sprite(sprite_index,image_index,x,y); draw_set_alpha(0.8); draw_healthbar(x-12,y-16,x+11,y-13,own_health/health_def*100, c_black,c_red,c_lime,0,1,1); draw_set_alpha(1);

Now we have a transparent health bar above every enemy. As you can see, any sprite we set for the object will be drawn, with the correct sub image. Change the colors if you like, or fill in your own coordinates or make the health bar move the other way or up-down. Check the Game Maker Help if you want to know how this works. Next, well make sure the enemy follows a path. One important issue is that the enemies must move behind each other, not all at the same time. There are several tricks to do this: place them all in a row at the same time as soon as the wave starts, or place them at the same spot, but not at the same time, or place them all at the same spot at the same time, but make the enemies wait for each other. All are a bit difficult. But well talk about that in the final chapter. In the next chapter, Ill discuss the making of a nice path, and how to make it visible.

Paths
In most tower defense games, the enemies follow a visible path, one in each level. For that, you need a few components: Objects that will mark the path and prevent tower building on the path. Tiles to give the path a better look The actual path, which is not visible

Markers
First, lets make the markers. These are simple objects, for which you wont need difficult sprites. Remember to make them invisible. For now they are just markers to help you to build up a background using tiles, or to make the actual paths. But in the next chapter, these things will be more important.

Tiles
Now its time to make some decent graphics for the background. You can make a tile set for this. Another option is to use the markers to draw the path. But then youll need to make multiple marker objects, one for every part of the path. Adding more scenery can also make great levels. For example, you could make a bridge for the path, or a few trees, or a river etc. Anyway, thats up to you. In the example theres only a tile set for the path. Theres also a background for the level.

The actual path


Now that we have a visible path to rely on, we can begin constructing the actual path for the enemies. Create a path and set the right coordinates for the path. Make it start outside the room in front of the start of the visual path (example: if you start at the left of the room, you set the xcoordinate of the first point at -32) and make it end after the end of the visual path, using the same system. Tips for creating a path: When you begin constructing, indicate the level room to show as a background. By doing this, you can make the path exactly like the visual path. Make the path not closed. If you want to make smooth turns, add one point in front of the turn and another behind it. Then set smooth curves for the lines, precision 4, and youll get something like this:

Dont mind who are your towers and who are enemies: just look at the path and the markers. In the game, you would only see the path that is made of tiles. One thing you need to do: check the code of the bullet-firing tower. Since the enemy has no speed if going through a path, we need to adapt the code, and mention the variable path_speed (this is a built-in variable, so dont make it in the Create event) instead of just speed. If you do this, the piece of code about creating a bullet will be like this: __________________________________________________________
//Create a bullet ii = instance_create(x,y,ob_bullet); dist = point_distance(x,y,target.x,target.y)/24; xx = target.x+target.path_speed*dist*cos(target.direction*pi/180); yy = target.y-target.path_speed*dist*sin(target.direction*pi/180); ii.speed = 24; ii.direction = point_direction(x,y,xx,yy); ii.damage = damage;

Remember, this is only a small piece of the entire script. It is just the code below the comment //Create a bullet until the line with ii.damage = damage;. Everything else is still the same. So dont erase the }-markers at the end. Now you have one level, complete with a path. Compare your game with the file tdtutorial2.gmk and check if youve done well.

Building towers
OK its time to make this stupid, uncontrollable junk into a real TD game. The next key part is: the building system to build the towers. Of course, theres nothing challenging about a game in which there are towers already and you cant build any: its very boring. When you play a TD game, theres often a small window somewhere above or below the path, in which you can select and build towers. There are several buttons to build towers. If you click one, the appropriate tower appears and you can drag it to a nice spot, and click again to build it. Another often-used function is: the status window. If you select an existing tower, you can see its stats, and upgrade them if you have the money for it. We are going to make all of this in our example game. First, we make two sprites for buttons: one for each tower. Make two subimages in both: one for the default moment and one for when the mouse cursor is on top of it. Next, create a font for the text. You can also draw text in the button images, but make the font anyway, because well need it later. Make it small, but readable. Now we make two objects for the tower buttons. Give them a depth of -10000 and make the first button parent of the second one, just like before. Then, in the Create event of the parent, set the image_speed to 0 and initiate the variable price, which is set to the price you want the player to pay for the tower. In the Begin Step event, set the variable image_index to 0. And in the No Button event of the mouse, set image_index to 1. By doing this, the sprite will change if the mouse cursor is on top of the button, and change back if it isnt. Finally, in the Draw event, draw the sprite with the appropriate image index and, when the image index is 1, draw a text with the needed money.

Creating an in-game menu


Now its time to introduce a new kind of object: the controller. This object checks important variables, draws the menu background and text, and it will make the enemies appear in waves once weve finished the tutorial. The controller object doesnt need a sprite (although it makes it easier to recognise). When youve created it, give it a depth of -1000, so itll be drawn first in any case, except for the buttons. Then, add the variable money. Most TD games use money to buy the towers. Well do so too. Set the money to a nice start value (like 50). Add the variable ii_select, with the value noone. This variable will indicate if the stats of a tower must be displayed. If it is noone, no stats will be displayed.

Now you make a background for the menu. This can be a sprite, or just a simple drawn box. In any case, draw it somewhere at the edge of the room, but create enough room for text, buttons etc. A very simple example: use these actions: Set Color (you can decide which color it will be) and Draw Rectangle (use these coordinates: 256,0,639,111) and youll get a nice, simple background for the menu. Now its time to draw some text. Set the font, set a good text color, and think about some text to put in the menu (like a header, or just some information). Then, draw a text with the following content: 'MONEY: '+string(money). By doing this, you can show how much money you have. Check the area in the room that is covered by the menu. Indicate that by using path marker objects. By doing this, you can prevent the towers from being built inside your menu screen. Also, add the button objects and the controller to the room. Put your buttons somewhere in your menu (dont forget to turn delete underlying off) and place the controller somewhere at a visible spot.

Building the towers


Now lets return to the button objects. They might look good, but we still cant do much with them. But that changes now. Open the Left Pressed mouse event for the laser tower button. Then, add this piece of code: __________________________________________________________
//Check for money and other towers and create a tower object. { var ii; if instance_position(mouse_x,mouse_y,ob_tower_laser) exit; if controller.money < price exit; ii = instance_create(x,y,ob_tower_laser); ii.active = 0; }

First, it checks if there is a tower object at the mouse position (ob_tower_laser is the parent of the towers here) to prevent multiple tower creation at the same time (like when you accidentally click twice, or when you try to place two different towers at the same time at the same spot). Then it checks if there is enough money to build the tower (if you want, you can change the amount). After that, it creates the tower. Set a similar function for the other button object, but replace the text
10

ob_tower_laser in the function instance_create for the index for the other tower. The variable active shows in what mode the tower should be. Since towers are now immediately unmovable when they are created, we add the variable active to the towers (set to 1). By doing this, we can check if the tower should be active, or movable. Next, we change the Step event for both towers. Add these actions (both towers): Test Variable (check if active is 0), after that a block start. Then: Jump to Position (coordinates mouse_x, mouse_y) and Align to Grid (no changes). Then close the block and add the Else action. By doing this, the tower will move to the mouse position if inactive, aligning to a grid to make building towers more accurate. If it is active, the tower will do what its supposed to do. Next, adapt the Left Pressed mouse event for the parent only. There, check if active is 0, and if it is, set it to 1. Also, set the money of the controller relative to -price. Now we can build our own towers. Still, there are a few things that need to be adapted. We have path markers, but we can still build on top of them. This problem also occurs when you build on top of another tower. To disable that, we introduce another variable: coll_path (0). This indicates whether there is a path object at the position of the tower. After the variable is created, we need to set it to 0 at the beginning of every step, if the following expressions are not true: collision_rectangle(x-16,y-16,x+15,y+15,[fill in the tower parent object],0,1) collision_rectangle(x-16,y-16,x+15,y+15,[fill in the path marker object],0,1) Then, in the Collision event with the marker object, and also with the tower parent object, we set coll_path to 1. Next, we check this variable in the Left Pressed event (which means it must be 0), before we set active to 1. That way we can only build at free spots. Now we need to improve the drawing functions. In many TD games, when you are building towers, you can see the range circles of towers. You can also see whether you can build at the current position or not. For this we have the following code: __________________________________________________________
//Draw a ghost of the tower and its range { draw_set_alpha(0.5); if coll_path = 0 draw_set_color(c_lime); else draw_set_color(c_red); draw_circle(x,y,16,0); draw_set_color(c_white); draw_circle(x,y,range,1); draw_set_alpha(1);

11

Use this code when active is 0. Else, use the default code or action. Now it starts to look like a real TD game. But we can still add a few things to it, before continuing to the wave system. Since you lose money buying towers, you want to earn some new money. That can be easily done by adding some cash to the money variable of the controller, as soon as an enemy is destroyed. That was rather easy. Now comes a more difficult thing.

Tower status
Now you can build towers, but the player doesnt know the stats of a tower. If we add another mechanism, he can see it. When we made a controller object, we added the variable ii_select. Now its time to do something with that. Go back to the tower parent and add the variable selected (0) (I know, there are really many variables) which will indicate if the tower is selected or not. Now lets change the Left Pressed mouse event. We let the existing actions remain the same, but we add some actions above it. First, we check if active is 1. Then add a block start, and then we check if selected is 0. If it is, we first set selected for all instances of the tower parent object to 0, then set selected for the instance itself to 1 and set the variable controller.ii_select (the variable from the controller) to instance_position(x,y,[fill in the tower parent object]. If it isnt, we set selected back to 0, set controller.ii_select to noone and end the block. Now we add a few actions to the Draw event of both towers. When selected is 1, draw a circle (using a code, draw_circle(x,y,range,1); ). Now lets shift to the controller object. In the Step event, add the following code: __________________________________________________________
//Check whether the tower should be deselected { if not instance_exists(ii_select) ii_select = noone; if mouse_check_button_pressed(mb_left) if not instance_position(mouse_x,mouse_y,ob_tower_laser) if not instance_position(mouse_x,mouse_y,btn_tower_laser) { ii_select = noone; ob_tower_laser.selected = 0; } if mouse_check_button_pressed(mb_right) { ii_select = noone; ob_tower_laser.selected = 0; }

This code checks whether mouse buttons are pressed and if the towers should be deselected.
12

Remember all those variables which I introduced when we created the first towers? This is where it pays off. Now we can show the towers name, range, rate, damage and cost. To show these variables, we add some text to the code in the Draw event. Place this text between the last line of code and the end of the code: _________________________________________________________
//Draw tower stats if ii_select != noone { draw_set_font(ft_header); draw_text(400,2,ii_select.name); draw_set_font(ft_hud); draw_text(400,32,'Damage: '+string(ii_select.damage)+' HP'); draw_text(400,48,'Range: '+string(ii_select.range)); draw_text(400,64,'Fire rate: '+string(ii_select.rate)+' shots/second'); draw_text(400,80,'Selling price: '+string(floor(ii_select.price/2))); }

The first action changes the font to a better font for headers. Then it draws the towers name. Next, it changes the font to the stats font, and it draws some variables. The selling price is determined by dividing the buying price by 2. About the selling price: we can build towers now, but if you put one at the wrong position and dont want it there, its better to sell it. To do that, we need another button object. Make a sprite for a sell button. Then, make an object for that button, and make the button parent also the parent of this object. In the Step event, set the variable visible (a built-in variable) to 0 if the variable controller.ii_select = noone. And else, set visible to 1. Then, in the Left Pressed mouse event, check if visible is equal to 1. After that, execute this piece of code: __________________________________________________________
//Check which tower should be sold and then sell it. { var ii; ii = controller.ii_select; controller.money += floor(ii.price/2); with ii instance_destroy(); controller.ii_select = noone;

This code adds the appropriate amount of money to the money variable of the controller. Then, it destroys the sold tower. Well, be proud of yourself, because weve had the worst part of the tutorial now. I recommend you compare your game to the file tdtutorial3.gmk to check if you have done the right things. There are just two things left to make: a wave system and an upgrade system.
13

Wave system
Nearly all TD games use a wave system to create enemies. The higher the wave, the more difficult are the enemies, and the harder it gets to beat them. Well, our game wouldnt be finished if we hadnt such a system. In the chapter about enemies I mentioned a few ways to create enemies every wave. Well make a system that creates every enemy at the same spot, but not at the same time. If you have played Interceptor, you could see that I used a system that creates all enemies at once in a row, because the towers often fired at targets far outside the room. To prevent that, well create them near the border of the room. The wave system will be controlled by of course the controller object. To do this, we create three additional variables named wave, wave_time and wave_enemies. Wave indicates the current wave number, wave_time indicates the time remaining until the next wave and wave_enemies indicates the number of enemies to be created. Both are set to 0. Now we just need a script for the Step Event that will handle all this stuff. __________________________________________________________
//Check the wave time and calculate how many enemies should be created //Create the arrays for the next wave { globalvar ee; if wave_time = 600 { wave += 1; wave_time = 0; wave_enemies = min(5+floor(wave*0.4),16); ee = wave_enemies; } //Create enemies if needed if wave_time < 600 wave_time += 1; if wave_enemies > 0 { if wave_time = (ee-wave_enemies)*15+15 { instance_create(-32,0,ob_enemy); wave_enemies -= 1; } } }

This script works as follows: first it checks if the wave time is 600, which is the moment it will move on to the next wave. It also sets the time back to 0 and sets the amount of enemies that should be created, which is limited by 16. Then it checks if the time has a certain value. At such a value an enemy is created.
14

Of course, you want to see in which wave you are and how long it takes before the next wave. Therefore, we draw the wave indicator just below the money indicator at the same way. To draw the time, you could either use a health bar, or some kind of sprite, like a clock. Thats up to you. If you want to see how its done in the example, check the file at the end of this tutorial. In either case, be sure to indicate that the time variables uses arrays. Now we have a wave system, but thats all. There are some things that we need to change: First, we set the enemy health variable (and the default health variable too) to round(40+power(controller.wave,1.5)*10). That will make the enemy health increase by the wave number, power 1.5, times 10. Then, we set the appropriate path for the enemy instead of moving some way, and we make sure the enemy wont return to the start position if destroyed or at the end of the path. Instead, we destroy the instance if the health is 0 and we adapt the function that adds 1$ of money to the money variable of the controller. Now we add instead of 1: controller.wave. By doing this, we get an amount of money equal to the wave number if the enemy is destroyed, which will make the game a little easier and more fun to play. Finally, we make it possible to lose the game (or else, it still wouldnt be a real game). In the Create event of the controller, set the number of lives to a nice amount, like 10 or 15 and set the score to 0. Then, in the No More Lives event (Other events), set the score to the wave number, display the highscore table and restart the game. Draw the amount of lives somewhere in the menu. Next, when an enemy is at the end of the path, add the function to subtract one life. Now we are done with this simple wave system. Check the file tdtutorial4.gmk and compare your game to it. Now you have a nice TD game. There is only one thing were going to add here: an upgrade system.

15

Upgrade system
As you know, in many TD games you can upgrade your towers. Since I got a lot of requests to explain how it can be done, I decided to add this to the tutorial as well. This, however, isnt the same kind of system that Interceptor uses. It works a bit different and is actually a lot easier to make. The first thing we need here is another button. Duplicate the Sell button, make a new sprite for an Upgrade button, and add it to this object. Then, make sure the button has only a Step Event and a Left Pressed Event. In the Step Event, use the same checking system as used in the Sell button, but when visible is equal to 1, the variable price (which already exists, since the button has a parent) is set to controller.ii_select.price*0.4. Now go to the tower parent object, and add the variable level (set to 0). Then go back to the upgrade button, and execute this piece of code in the Left Pressed Event:
//Check which tower should be upgraded and then upgrade it. { if visible = 1 { var ii; ii = controller.ii_select; if ii.level < 5 if controller.money >= price { controller.money -= price; ii.damage = round(ii.damage*1.1); ii.range = round(ii.range*1.1); ii.rate = min(ii.rate*1.2,10); ii.price = round(ii.price*1.5); ii.level += 1; } }

As you can see, it first checks if the level of the tower is smaller than 5. It cant get higher. Then it checks the money that is needed. After that, it increases the level, decreases the money, and multiplies the stats by certain amounts (the rate is limited, because a very high rate could make the game a bit weird). Now YOU make sure the tower shows what level it has. Thats all for the moment. The last example is tdtutorial5.gmk, so have a look at it, see how far you can get (with me, it still was very easy at wave 108, compared to impossible at wave 70 without upgrades).
16

Final words
Well, take a well-deserved breather, because we are finally done! Well, actually only I am done here. You now have the basics for a nice TD game, but thats not enough. If you want a game that scores highly on the YoYoGames website, or anywhere else, you need more than this. I can give you some tips to make your own TD game: Make more different towers, which vary in cost, range, damage and rate. Make more (different kinds of) enemies. Make multiple levels, and a new path for each of them. Make additional upgrades, like power-ups for towers, or superweapons (like EMP or FBA in Interceptor). Make the rooms larger. That makes more room for a menu. Use your own sounds. Make a main menu from which you can access all features in the game. If you need some inspiration, you can play other TD games, like Onslaught, Vector TD, Flash Element 1 or 2, and of course Interceptor. If you need additional tips, for a new tower, or anything like that, ask me. Ill at least reply. And please tell me if you have Lite or Pro. That makes it easier to answer your question. This whole tutorial has been made along with GM 7 Lite, so dont worry about any errors. Good luck!!! ATHANATOS.

17

Vous aimerez peut-être aussi