This is part 5 of my journey in making a Tower Defense game. It is also 2 Jan, the second day of 2016 at the time of this post, so Happy New Year!
I added special turrets and structures to the game. I put a lot of time into the thought process behind the coding and design and managed to extend my turret class to house a new type of structure – Buildings.
Most of them don’t shoot at enemies, and those that don’t give some kind of bonus.
With this script, I can create different types of structures that do very unique things.
I can also convert any ‘building’ to a turret as well with my build() script.
The structures you see sitting out in the sea are oil rigs – they generate additional gold income at the end of each wave.
I have also coded runes. Runes are a different kind of ‘structure’ that you can build on top of. Unlike buildings, they don’t occupy a grid slot and have a very unique effect. They boost the damage of a turret built on it. Their damage will also be highlighted in green to indicate the boost.
These can be upgraded to have increased effect on the turret built on it.
My runes were actually drawn too large that they appeared so small on the actual map. The funny thing about my previous game was how I always drew things too small, but in this game my graphics are drawn way too large that it became a problem.
I added two new turret types, so the game now has 9 turret types + 1 hero. The game had plans for 12 turret types, but thanks to buildings, there is enough variety in structures. This game is already getting a bit too complex in terms of turret types so I will see how it goes first.
The Gold Turret / Gold Factory will increase gold dropped by creeps in a certain radius. However, if there are more than 1 gold factories, I want it to always take effect of the highest leveled gold turret. So I made each creep stores a list of the gold factories’ gold multipliers, and when it dies, it checks through the list and multiplies its gold only by the highest multiplier.
The turret class was also expanded to house several new data with the introduction of the Booster turret – introducing a new mechanic where turrets can boost damage of other turrets.
It sounds sraightforward to code. But you got to make sure that multiple booster turrets buffing one tower are stacking the way you intend, and when you upgrade a booster turret, or sell it, you have to remember to update the turrets that were previously boosted – to take into account the difference between the new boost and the old boost and add or subtract accordingly. Or rather, recalculate the entire boost amount. The booster must also remember not to boost itself.
Next up is another one of my scripts, the Info Script. It was named that way because of its original use – to display info panels.
However, I extended my simple info panels to be able to include story panels as well, have pictures along with text, and a very convenient class that stores every dialogue available for use to easily trigger when I need to and can be expanded easily:
Adding new panels is literally just typing what you want to read, and what you want to see by setting imageNames to the “picture name”. The field can be left blank if I don’t want to use any picture. I can even set the picture size to “BIG” or “NORMAL” or “NONE”. Most story panels use the BIG pictures whereas information panels use normal sized or no pictures.
Info Scripts can also be queued so I can stream story panels together seamlessly. I also added a fade in fade out effect as a final touch.
I really love the story panels by the way + it’s fun to draw the pictures. I put a lot of effort into the dialogue and hopefully it doesn’t sound too awkward or cringe worthy. I want to go with minimal dialogue for this game to tell the story and I’m pretty happy with it so far.
Monsters that spawn other monsters
I managed to code a SpawnScript. It is a script that can be attached to a monster, to make it a parent. A parent that gives birth to children. So basically in the script, I can set a couple of things just by flicking switches and setting values:
- The HP requirement to start spawning
- Conditions to stop spawning (eg HP drops below 50%)
- How many monsters to spawn and max monsters if any
- Spawn interval
- Any break durations, for example, stop spawning for 20 seconds for every 8 monsters you spawned
- The enemy type to spawn
This simple yet complex script is pretty flexible enough that it allows me to create a very unique first epic boss.
The Incinerator has a couple of abilities. For one, it is able to spawn miniature ships. And the first fight with it was pretty epic.
This is only Map 10, but I really feel like making it the final map. This map has a couple of special effects – darkening on spawn, story panels and a very unique epic boss that is challenging to fight. In fact, this map is so special that it feels like a finale to the game already. I really am tempted to make this the final level. That’s how complex this map and boss is.
Sadly, I think I drew a couple of other epic bosses for the future levels, and I have a story script that pans out across 25-30 maps.
Lots of work I guess. Also, more monsters spawning more monsters:
Also, in league with the new epic boss, I made two specialFX functions, which I can use to trigger when a monster spawns, and when it dies. For example, when the Incinerator spawns, it triggers an event to darken the map.
I also did manage to make a very wonderful side-map. In the screenshot, below you can notice a special building – the Control Tower. It is another one of my unique buildings that uses my new Building class script. It was initially non-interactable – i.e. just a decoration , but I programmed it to be an actual turret instead.
I really love this map a lot. I think it’s my favourite map so far. This isn’t a story map, but for some reason I felt like I wanted to put a lot of effort in each of my maps. I think that should be my goal – to make each map special and not just make maps for content’s sake. I have stopped drawing new maps for now and if anything, I have way too many map graphics. I will only draw new ones if there is really strong inspiration or good map ideas/designs.
I also made new enemies unique to this map.
Each map can be set to have a unique array of monster spawns. For example, I can specify that I want Ladybugs, Turtles, and Larva monsters. My enemy wave spawner will then loop these 3 monster spawns automatically.
My maps are getting more diversified enemy types as well. Waves can come in different forms and unique orders.
Multiple Spawn Points!
I was really NOT keen to do this. In fact, there was no need to. My game is super complex already and I don’t want more stuff in it. But I felt obliged to do this because of several reasons. Let’s skip the reasons though.
Making multiple spawn points is NOT an easy task. I want to make my spawn system flexible. And in the end, I managed to make a very robust spawning system. And this is one of the things I’m really happy about. When making games, I learned that you really want to make your game easily modified and expanded.
I can add new paths to my maps just by duplicating a current path, resetting the waypoints, and renaming it Map2A. That’s all I have to do. The code takes care of everything else.
In fact, I even coded a weight system for handling the distribution of monsters to the different paths. By default, all paths have the same weightage. But if let’s say I want like 3x more monsters to spawn from the left, and 1 from the right, I can simply do
“spawnRatio = [3, 1];”
I’m really happy about the spawn system because of its robustness. In addition, I can even enable/disable each spawn point individually, to stop them from spawning monsters at example, wave 10, during special story events, or re-enable them for like an epic wave at wave 20.
A lot of this functionality is barely used in the game though – there just isn’t much maps that need it. But I believe it adds to the cool-ness when you encounter a map that does. It makes that map feel so much more unique.
Also, monsters can spawn in the middle of the map, from special spawn points called Summoning Runes (story-related?):
So monsters do not always necessarily come from the edge of the map. I have plans to use summoning runes as a story element.
Seemingly one of my favourite things to do in any game, and have done so for my previous games: Introvert, Idle Heroes, is to create special effects.
These are specially scripted story events involving camera movement, zooming, special animations and event triggering that do not happen anywhere else in the game. These special effects can take awhile to code, depending on the complexity and the code is often unique – unique as in the code executes stuff specific to this special effect.
The special effect you see above is a combination of two individual special events. First, the monster movement animation. It is actually my first attempt at making a fluid movement animation by using rotation, position and speed. It actually took awhile to do that because I wanted the animation to look smooth. I set keyframes during the animation of where I wanted it to move to, then switched to graph mode to try to fine-tune the values and tangents.
My original intention was to just have it fly from point to point, then rotate at each waypoint to face its new direction. But doing the whole thing as a fluid animation… it was just really special.
NOTHING ELSE IN THE GAME moves like that. So it’s a very pretty special event considering every other creep or object moves on pathways. This is a one-time event that occurs once in the game. I think the animation can be improved though, but I’m still pretty happy nevertheless. It was really exciting to actually try something new and bold (to me at least haha).
The second thing you’ll notice is I incorporated the camera zooming function I coded in Part 4 of Making a TD Game.
I extended my Camera Script to allow it to do two things. First, follow an object I specify, and secondly, lock the camera and disable the player from trying to move it with WASD, scrolling with the mouse, etc.
When this event plays, the camera will automatically and gradually zoom to its desired position and zoom level, which is really, really cool because all this while the game is being played at 1x zoom and you don’t see the camera zooming in or out. The camera is also locked during zooming so you can’t break the zoom.
Here is another special effect of this map that plays once every few waves:
This is actually what is categorized as an ‘Epic Map’ in my game, which is a special map with a unique boss and advances the story quite a bit. This particular map has 3(!) special effects. The last special effect is the river in the center of the map. With each wave you summon, the river turns more and more red until it beomes a river of blood. Creepy!
Game Performances / Lag
So I wanted to test the limits of my game – to see if it lags when I have a lot of monsters on the map. So I multiplied my monster count by 10. But it was a little underwhelming. My turrets were killing too quickly. (I have to have the turret shoot and kill stuff because I want to simulate them shooting and have monster death as potential lag variables as well).
The game didn’t lag above because my turrets were killing stuff at the same rate they were spawning. So in order to test mob count limit, I set all monsters to spawn monsters every second while alive and on death, spawn up to 6 copies of itself.
Worst idea ever. The lag multiplied exponentially. The game ran okay for about half a minute, but within the next half, it dwindled to 4 fps because everything that died respawned copies of itself.
Basically, don’t have monsters spawning monsters spawning monsters. What a horrible idea that was. Oh, notice the two screenshots above are actually the same map, just with different tints?
I actually tint my maps based on the mode you enter it on. There is a ‘Day’ mode, ‘Sunset’ mode and a ‘Night’ mode to correspond with my difficulty modes, ‘Normal, Hard, Hardest’. I really love this small little touch to the game. Nothing major, but it just makes me happy. The tints used to be more obvious, but I made them more subtle because the tint settings in Unity aren’t as advanced as Photoshop – they add color directly to the graphic, which make it look darker. Too much tinting makes the map look too dark and lowers contrast. I actually created a night color filter in Photoshop that automatically makes any of my maps look like night, and looks better without contrast issues, but the problem is that having the game use 3 map graphics means the game size will grow too big. Rather than doing it via Photoshop and saving each map on 3 different filters, I decided it was best to use tints, the tradeoff being that they didn’t look as good as color adjustment settings in Photoshop.
But back to the topic on lag, I think I managed to play around with the Profiler to try and see what are the top contributors to lag. In the end I did Object Pooling for my bullets and enemies. I think whether or not they contributed hugely to the lag, it is a good idea to pool them so they consume lesser resources.
I extended target priorities (options which allow you to customize what kind of creep you want a turret to target specifically). I added new targetting options and you can toggle a turret to fire or not and also, selecting a creep sets it as a top priority target, forcing all turrets to focus fire on it. It’s pretty cool.
I also centralized a lot of my turret graphics. Probably a bit OCD, but previously some of my turrets were a little too much to the left or right, so when the disable icon appeared, it was ugly.
Anyhow, below is a portion of how my recalculateStats() function looks like.
What it does is basically calculate how much damage a turret should do. Looks simple, but consider this. There are many factors that affect a turret’s damage:
- Its own upgrade level
- Is it built on a special power-upped grid?
- Your hero’s skills can increase turrets’ damage (via a stat called Turret damage multiplier)
- Booster Turrets – a special type of turret that boosts damage of nearby turrets
- Special Upgrades – The sniper turret has a special skill to increase its damage
The same goes for other variables – range, attack speed, and turret cost.
There are other additional factors for damage of course, but they are calculated separately when the turret’s bullet is fired and hits the target. Things like:
- Does the bullet ignore enemy armor?
- What is the damage type? DOT (burn), A.P (Armor Piercing), N (Normal), INSTANT (e.g. from external sources that do instant damage like -% of enemy HP and possibly spells if added in the future)
- Is the target affected by status effects that can multiply damage? e.g. shielded OR burning enemies
So when calculating damage and updating it, you have to make sure you account for all variables and that they stack in the way they are supposed to – additively, multiplicatively, or as percentages if two things stack in the same way.
So yeah, this game is really getting a bit ambitious I guess. It has a lot of things and I really don’t want too much stuff. But *sigh*
Multiple Save Files
Also, I actually made my main menu have a scrollable interface that supports up to 30+ save file slots. This was due to the fact that I have to extensively test and balance progression so I needed a lot of save file to test with. I also fixed a fatal bug in auto save where it did not actually clone itself when you try to load it into a currently empty save slot, but actually loaded itself. It was pretty hilarious when I had 3 save slots of the auto save and playing on one file modified the other two because they were all literally referencing the same thing.
The most boring part of game development to me is probably the amount of Math behind it. I don’t think many people will find it fun doing this, but it’s still something I put a lot of effort into. I just learnt that there’s even an entire module in University that teaches this kind of stuff.
Game balancing is not something common sense that you can brute force into your game. And neither is it something everybody can do well – it explains why some games are made imbalanced, and I think it’s something I might not be able to completely prevent in my game – there are just a lot of variables and different ways players can play that it can lead to unpredictable situations.
The game is pretty big now – the size of the project is about to be as big as Idle Heroes, which is my previous game and my very first game made in Unity, and I think it is about to reach as big in terms of content.
There are lots of problems about big projects – riskier, but perhaps more rewarding. But honestly, I prefer to work on smaller projects. It also gets harder to organize stuff in growing projects. I think it is inevitable no matter how organized you try to be, you tend to feel a little confused sometimes.
For example, I have a SpawnEnemy script and a Map script. The map stores data about the map, while the spawn enemy controls spawning. If I were to define a spawn multiplier, say make a special map spawn DOUBLE the amount of creeps. Do I set this multiplier in the spawn script or the map script? I actually went ahead and defined it in the spawn script because it seemed obvious, but my scripts worked such that all the map data is initialized in the Map script. The Map script contains all information about a map or level, such as number of waves, map health, difficulty, and such. It defines all the variables that make each map unique, and spawn multiplier is one of those variables that should be defined in the map.
It seems like a subtle difference, but considering that the game has a lot of scripts, they all need to initialize orderly, in a specific sequence to make sure all variables of all objects have the correct values when the game starts.
This is more of an organization problem. The code works fine and the game runs correctly whether I put the spawn multiplier in the Map or SpawnEnemy script.
So yeah, there were times I felt there was a better way to organize my code and so I actually spend some time shifting stuff around because I know I will be working on this game for a long time and it is good to at least have a very workable and easily understood codebase, even if I am working alone and I know the game code at the back of my head.
But don’t we all get forgetful sometimes?