Tower Defense in AS3 – Part III
I decided to take a slightly different approach to Phase III of this series than I had initially intended, since there was a little restructuring that needed to be done in order to support a cleaner and more efficient system. The intent was to have this post cover the basic solution to the towers themselves, but after getting back into the code, it made more sense to discuss stage structure, wave management and creep variety first. This way, we will actually have something on which to test our towers when we make them.
When you finish reading this post, you will have the ability to customize your waves via an XML file along with defining the path of the road — and therefore the creeps — in the same XML document. Allowing for customization in this way will pave the way for building a framework that can be enhanced for many different uses.
Here is an example of what your SWF might look like when you are done:
As you can see, in my demo, there are three different waves of creeps, each with its own type of creep as well. A quick glance at the code will show that I have built in allowances for normal, fast and boss type creeps. Eventually, I will build sub classes that extend the base creep class for each different type. This will allow for customization of not only behavior and appearance, but also of responses to different attack types and status effects. By taking a quick peek at the XML document included with the demo code (available for download at the end of this post), you will very easily be able to decipher how I am defining my waves.
With this basic understanding of what we are looking at for this phase in development, let’s jump right in. There will be three basic code modifications in addition to the updated documentation that we want to cover:
- XML Parser: loading our path and waves
- Timing and launching our waves
- Creep differentiation: variety is the spice of life
- Documentation: readable code base
XML Parser: Loading Our Path and Waves
XML parsing in AS3 is actually quite intuitive, and because of the support for E4X (ECMAScript for XML), it is quite simple to implement as well. Quite possibly the biggest challenge in reading data from an external source in a case such as ours is waiting for the XML to finish loading before we try to access any of the variables populated by that script. If the wrong actions are triggered too soon, we will have all sorts of unusual errors thrown. To accomplish the desired timing in this case, I simply pass the Level object which we are loading into the StageLoader object. This way, the StageLoader can activate the init() method of the Level object after the XML has been fully parsed.
So, let’s look at the internals of the StageLoader class to better understand our XML parsing. The constructor of the class takes the path to the XML file, level number and Level object as parameters. Using the supplied URL or path, a URLRequest and URLLoader object are created:
path = base_href + url + n + '.lvl';
xml_string = new URLRequest(path);
xml_loader = new URLLoader(xml_string);
xml_loader.addEventListener("complete", init);
The final line sets the event listener to call our member method init() as soon as the XML loader is finished loading the file. If we glance at our init() method, we can see how the data is loaded and the level is initialized:
result_xml = XML(xml_loader.data); parsePoints(); parseWaves(); my_level.init();
Remember that the my_level variable is the Level object that created the current StageLoader object, so by calling its own init() method from within this method, we have successfully forced the delay in initialization we were after.
If you took a moment to read the link to the E4X overview above, it will take no effort at all to understand the parsePoints() and parseWaves() methods here. I am only going to post the former here for review, but the driving principle is identical for both. Basically, to load the path coordinates, we want to loop over all the point nodes in the XML document and assign the X and Y coordinates to the points array:
for (var i = 0; i < result_xml..wave.length(); i++)
{
var pt = [int(result_xml..point[i].@x), int(result_xml..point[i].@y)];
points.push(pt);
}
You will notice that we explicitly cast our coordinates to integers: this is necessary to avoid any calculation issues in the use of the coordinates in other areas of our system.
That pretty much sums up everything we need to load any and all of our level details from an XML document. With this knowledge, you can modify the StageLoader class to supply as many or as few of the details necessary for your game as you wish. Play around with different settings and simply create additional parse and get methods for additional attributes to your game.
Timing and Launching Our Waves
As you may expect, it is our Level object (the same one that was initialized from within our StageLoader object) that throttles our waves and launches them when the time is right. Learning to use the AS3 Timer object is extremely useful for this. Rather than using the setTimeout or setInterval methods we would have used in AS2, we can set a timer and actually have an event handler to launch our new wave whenever the timer is triggered. To do so, let's look into the init() method and see how simple it is to set up our timer:
launchNextWave(); timer = new Timer(15000); timer.addEventListener(TimerEvent.TIMER, doLaunchWave); timer.start();
That's all there is to it! First, we directly call our launchNextWave() method to launch the first wave. Next, we instantiate our timer (with a 15 second interval in this case): keeping in mind that the timer acts on milliseconds, so 15000 == 15 seconds.
Once we have the timer primed, we want to set the event handler for it. In our case, we want to use the TimerEvent.TIMER event, since this is what is triggered every time the timer counts to our declared number of milliseconds. We set our event handler to call doLaunchWave(). Finally, we actually start the timer.
The timer will now run, triggering a TimerEvent.TIMER with every count until we stop the timer — which should be done after the last wave is launched, of course. So, how we handle all of this is from within our event handler method: doLaunchWave():
if (current_wave < waves.length - 1)
{
// Launch the next wave
launchNextWave();
}
else
{
// All waves have been launched
timer.stop();
timer.removeEventListener(TimerEvent.TIMER, doLaunchWave);
}
You can see that we simply check the current wave running against the total number of waves in this Level. If we have not yet launched the last wave, we launch the next one in line; however, if we have already launched all waves, we stop the timer and remove the event listener from the timer itself.
One final thing I would be remiss not to mention is the actual launching of each wave. We have seen two locations where launchNextWave() is called from within the Level object, so let's see how simple the code within that method is:
var w = waves[++current_wave]; waveMC.addChild(w); w.go();
That's it! Look familiar? It should. The second and third lines here is the exact same code we used in Phase II to manually launch our single wave of creeps. We have encapsulated it within this method along with a counter to keep track of our current wave. If you know anything about the precedence of incrementing in programming, you understand that we increment the current_wave variable by one first and then use that variable to access the next wave in our waves array. Once we have the targeted wave, we add it to our wave MovieClip and launch it with the go() method.
There are some other nuances changed from Phase II to here in the Level and Wave objects as well, but it would be more beneficial to you to view the additions in the code, as they are primarily accessor methods and minute changes to help with the flow. As always, please review the code itself for any specific implementation questions you may have.
Creep Differentiation: Variety is the Spice of Life
The last big attribute we have added in this phase is the ability to differentiate creep types in each wave. Ideally, each creep type will have its own class definition that extends our base Creep class and will define its own basic behavior, strengths and weaknesses, but for now, we just use a couple accessor methods to make some visual differences in our creeps.
The first thing to note is that, based on the type of creep defined in our XML document for the current wave, the Wave object has a setCreepType() method that sets the spacing for the creeps according to type (currently accounts for "fast" and "boss" types aside from the normal). In addition, when the individual creeps are created, the type of creep is passed to the Creep constructor from the Wave object. A setupCreep() method has been added to the constructor method that simply contains the following code:
if (type == 'fast')
{
graphics.lineStyle(1, 0x7777DD);
graphics.drawRect(-4, -4, 8, 8);
speed *= 1.5;
}
else if (type == 'boss')
{
graphics.lineStyle(2, 0xCCCCCC);
graphics.drawCircle(0, 0, 5);
graphics.lineStyle(2, 0xBB0000);
graphics.drawCircle(0, 0, 8);
}
else
{
graphics.lineStyle(1, 0xFFFFFF);
graphics.drawCircle(0, 0, 5);
}
Once again, for demo purposes, we are only accounting for fast and boss types, but this is easily expanded into as many different types as you wish. I would personally encourage having a database or XML document of some sort that defines the details for each of the creep types you wish to support. This way, you can add new types extremely easily and have them supported without ever having to touch the code directly. Open ended code is very helpful in a framework such as we are building.
So, for the most part, the creeps have not changed save for these visual allowances we have made. Otherwise, things continue as normal.
Documentation: Readable Code Base
First, let me apologize that my code documentation has not been up to par so far in the series, and I will do my best to have a much better representation of my personal coding standards in all my posted code in the weeks and months to come. I have attempted to add in docblocks for the classes and methods already in place as well as those added ones. If you find a segment of code that is not accurately documented and would like input on what it does, please don't hesitate to contact me, and I will try to quickly get back with you. I fully understand the frustration of code that is unreadable.
Thanks again to those of you who have contacted me with comments and questions about this series. I will continue to work on this as I have time, but through the holidays, I may be a bit slow. If you have suggestions or ideas for improvement on the actual concepts or code, I also want you to be willing to contact me. In my mind, a good coder is one who is always willing to better himself, and I definitely believe I have long way to go — especially in AS3 Ᾱ so please message me with any comments or questions!
Garth Henson has been working professionally as a web developer for nearly 10 years. When not coding in PHP, JavaScript or Actionscript, he can usually be found trying to refine his photography skills.






Michael D
2 Dec, 2008
Awesome tutorial…looking forward to the discussion on towers! Keep up the good work.
john
2 Dec, 2008
Excellent! thank you Garth!
Joe Henson
2 Dec, 2008
Hey Garth, I have no idea what you are talking about, but I like it 8-).
Dad
Robert
12 Dec, 2008
I have made like 10 differnet types of enemys i cant wait for towers when is the next tutorial coming out
OWNAGEINABOTTLE
14 Dec, 2008
This is a great tutorial.
But, I cant figure out how to create a start button for the waves that works, because I have no clue where to put the data. Plz. sum1 help me out.
obsidian
14 Dec, 2008
@Robert – I’m not sure when exactly, but it will most likely be after the first of the year. With the holidays and work load currently, I’m not finding much “extra” time.
@OWNAGEINABOTTLE – The action for launching waves is a public method that the Level class calls. So, if you create a public method in the TD class that activates the launch method of the Level class, you can easily tie the MouseEvent.CLICK event handler from the button to that TD.launchWave method you create. That would probably be your best approach at this point.
Mike D
15 Dec, 2008
Oh boy…i get two christmases this year! One on the 25th and after the new year when the tower discussion comes out!
whiteo2
16 Dec, 2008
Great tutorial! Possibly one of the most detailed i’ve found so far. School only teaches so much… the stuff you got here is way better XD
can’t wait till the building starts
WeaponX
4 Jan, 2009
I like this tutorial alot. Keep going I cant wait to see how you will hande towers, bullettypes, ressources etc.
Robert
14 Jan, 2009
hey when’s the next part coming out?!?!?!?
obsidian
14 Jan, 2009
To those who have been so patiently waiting, be assured that the next part is coming very soon. I have recently finished the code base for my towers, and I am currently writing the actual tutorial.
Thanks for your patience!
Michael D
14 Jan, 2009
WOOT! WOOT! WOOT! WOOT! Ok…calm down…can’t wait man! Keep up the good work.
Robert
15 Jan, 2009
nice job cant wait
JM
21 Jan, 2009
i want to replace the circle creeps, but I don’t know how to make the creep object show a movieclip. i’m brazilian, sorry if i didn’t write correct.
if someone can help, i’d thank for that.
Robert
21 Jan, 2009
man whens it coming out i cant wait much longer
Michael D
26 Jan, 2009
JM if you can’t figure it out I can send you the code I did…but if you take a look at it piece by piece you should be able to figure it out…they way it is designed…modifying the code is pretty easy.
Robert
27 Jan, 2009
ya in mine i have circles, squars, triangles, ovels, dimonds, stars of david
james
30 Jan, 2009
I am waiting patiently for the next part, this is has been really great, thanks a lot for writing it!!
Mark
11 Feb, 2009
Nice