Modular Tower Defence Devlog
I had an idea for an FPS tower defence game, where ‘upgrade modules’ could be inserted into your towers to modify them, and mixing and matching these modules to find the best combinations sounded like a fun idea.
While prototyping, I often find myself having to battle my inner software developer, who is constantly trying to slow down the project by ‘future-proofing’ or adding things that aren’t completely necessary for a prototype. In the past, while making FPS prototypes, I’ve found myself making a Gun script that could eventually handle all different types of guns with different fire modes, hit-scan and projectile guns, and more. In this project, I really want to try and streamline the prototyping process, and get a playable game loop up as soon as possible, so I can begin iterating on the idea.
Player controller setup.
Player controller
What’s faster than making your own player controller for the 79th time? Grabbing one from Google! (I should really make a prototyping library at some point) I normally spend some time making a player controller from scratch, deciding whether I should use a rigidbody or Unity’s CharacterController, and adding extra features like Coyote Time jumps or step detection. I don’t have any unique movement mechanics necessary for this prototype, so this was a great time saver!
Placeholder weapon and crosshair setup.
Placeholders
I added some placeholders to get the game feeling like an FPS right away. I attached a weapon model to the player object, and added a quick crosshair to make it clear where you’re aiming exactly.
Basic tower firing
Towers are one of the first core elements I need in the game, so I set up their functionality first. I restrained myself from adding extra features to the tower script, and got it working quickly. I wanted the towers to have some feedback early on, so I added a firing sound effect, some recoil in the script, and a firing particle from a Synty VFX pack. I gave it a basic target to aim at, and it’s feeling like a tower!
Tower attacking enemies
Next up was adding something for the tower to shoot. I set up a basic enemy script that functions like a target dummy for now, with a health bar to give feedback on when they’re taking damage. When it came to tower targeting, I caught myself while thinking about different targeting modes for the tower; First, Last, Strong modes that are in many tower defence games would be great in a final product, but are far from necessary here, so the tower will just be targeting the first enemy it finds in the list.
Tower deploying
I wanted the towers deploying to have some feel to it, and decided on them dropping down from the sky. I added sound effects for the tower hitting the ground, and for it deploying shortly afterwards. I also added a small screenshake when the towers hit the ground, which was easy to add with Cinemachine, and adds some weight to their drop-in.
Simple controls UI.
Controls UI
Even as the developer I’m already forgetting what keys I put some game actions and debug actions on, so players definitely will be! I added some simple UI showing the controls for various game actions, with some great free key icons I found for free online.
Module prefabs setup.
Module prefabs
I found a model in an asset pack that looked vaguely like a ‘module’, and added some icons from an icon pack I had, to distinguish different types of module, with a large icon on the top, and a smaller one that will still show when they’re inserted in the tower. With these, I set up some prefabs for the three module types: Ammo, Core and Barrel.
Module insertion and swapping
The modules are the main unique gameplay element of the idea so far, so I wanted to get their functionality implemented as soon as I could. I set up an ‘interactable’ system, so towers and modules both have text above them when highlighted, so the player knows what action hitting the interact key will perform. I then added module insertion, removal, and swapping.
Damage numbers
To make it easier to see the precise effect of the modules, I quickly added some damage numbers that display when an entity takes damage. They hover in the air for a short duration, and yellow numbers denote a critical hit.
Modules modifying tower stats
I took some time setting up functionality for modules to modify the stats of a tower, and made sure they would interact sensibly with one another. The code will definitely need another look if I want to take this project further, but it’s very workable for a prototype. I created some example modules:
Rapid Fire: +Damage, +CriticalDamage, -FireRate
Big Bullets: -Damage, -CriticalDamage, +FireRate
Sniper: +Damage, +CriticalDamage, -FireRate, +Range
Shotgun: +Damage, +CriticalChance, -Range
Ricochet on-hit effect
Adding the ammo modules took some extra work, as I’d planned for them to not just be simple stat changes for the tower. I added a ‘Ricochet’ effect, giving the tower’s shots a 50% chance to ricochet to a nearby enemy. I also added an ‘On-hit Damage’ module, which adds a flat amount of damage to each shot, it’s powerful in combination with attack rate increases.
Player gun basic setup
It was time to add some very basic functionality to the placeholder gun. I reused some code from the tower firing, and gave the gun a sound effect, a hit particle, and a small amount of damage, with the chance of a critical hit. I found it looked odd when shooting the modules and they didn’t move, so I added a small amount of force when hitting physics objects.
Player gun recoil curve.
Player gun kick
I wanted to give the gun some kick to make it feel like it was firing, so I added some animation curves that control the translation and rotation of the gun after it fires, allowing me to edit the curves easily in the editor to edit the motion.
Test level setup.
Test level setup
I setup a very basic test level using ProBuilder, with a simple path for the enemies, but enough to test their pathfinding. I also added a ‘Core’ object for the enemies to attack, and for the player to defend. I added the A* pathfinding project and used it to setup some movement and pathfinding for the enemies.
Prevent enemies from hugging the walls to take shortest path.
Enemy wall-hugging
I found that enemies were all taking the shortest possible path to the goal, which involved hugging the inside edge of the wall, and this didn’t look great in gameplay. I quickly fixed this by increasing the collider diameter in the pathfinding detection, which makes the areas close to the walls unwalkable, and keeps enemies in the center of the path.
Test level basic gameplay
With the enemy pathfinding in, I was close to a simple, but full game loop. I was able to see what the game loop may look like, with enemies constantly spawning, and placing down a variety of towers.
Enemy bug
What good is a devlog without the occasional bug? I haven’t had many that are fun or interesting to see, but this was a good one. I forgot to reset the enemy’s attack timer after it attacked, so it would hit the core every frame, giving us a very deadly enemy.
Enemies attacking the core
Enemy attack timers are now working properly, and they slowly damage the core. I may eventually allow enemies to attack the player too, but that will be a gameplay decision to make later on in development. There were a bunch of small thing I needed now to complete a game loop, there’s not much interesting to talk about, so I’ll just list them here:
Money system: Towers and Modules cost money, enemy kills award money.
Phase system: Game transitions between phases when ready-up is pressed, the last enemy of a wave is killed, or the core is destroyed.
Wave system: Increasing numbers of enemies spawn every wave, at a faster rate too.
Game loop: Game starts at a basic menu which leads you to the game. Failing takes you to a game over screen, which leads you back to the menu.
Modules can be purchased - a random one is dropped from the sky, similar to how towers are dropped in.
Gameplay feedback and animations
The constant waves of capsules attacking the core didn’t seem very threatening, so I brought the enemies to life a bit more with some models from an asset pack, and animations from Mixamo. With this, I felt that the core game loop was ready to test, and my partner was nice enough to take the game for a spin. I got some great feedback, and noticed some extra things myself while watching:
Would be great to be able to visualise tower attack range.
Random module drops can be frustrating.
Waves are ending early due to incorrect enemy count bug.
There’s a bug with swapping a specific module type.
I got these bugs fixed up quickly, and added a key to visualise the attack range of the tower, which is great for seeing how modules like the ‘Sniper’ and ‘Shotgun’ affect it.
Buying random modules and getting three or four of the same type in a row could be quite frustrating, as you can’t equip multiple of the same type in a tower. We agreed that it would make more sense to be able to choose what type of module you were purchasing, so I added that functionality.
Weak point hitboxes
Next I wanted to make the enemies a bit more interesting by adding functionality for weak points. Enemies can now have any number of colliders, and specific ones can have damage multipliers applied, or be flagged as a weak point. For the basic enemies, I just set up a simple ‘headshot’ effect which doubles the damage applied when hitting the head hitbox, but for other enemies I plan to add more functionality.
Big fast enemy
For the first additional enemy type, I added a large enemy that is fast and can deal a lot of damage quickly if it reaches the core. The enemy can be crippled by shooting its weak point, an alien parasite on its head that is powering it up. After its weak point is destroyed, the enemy moves and attacks much slower, and deals less damage.
Tank enemy
The next enemy type I added was a tank type enemy. It moves slowly, but has a large amount of health, and a shield that greatly reduces the amount of damage it takes, until you destroy the shield generator on top.
At the start of the game, when the player’s towers don’t have upgrades, the player does the majority of the damage with their gun. As the game goes on and the towers get more powerful, the player will hopefully move to more of a support role, where they are hitting weak points to weaken powerful enemies, and prioritising the most deadly ones first.
Modules can be dropped or thrown in
At some point while testing, I air-dropped a module onto a tower and thought it would be cool if it could drop straight into the tower, so I added that functionality. I also allowed the modules to be thrown straight into towers, for a bit of fun.
Extra module types added.
Additional module types
I added an additional module of each type to give the gameplay a bit more variety:
Big Crit (Barrel): -Damage, +CritChance, +CritDamage.
Titan Slayer (Ammo): Shots do a percentage of the targets health as bonus damage.
Crit Pity (Core): +CritDamage, Every time you don’t crit, increase your crit chance. Resets upon scoring a critical hit.
Made interior wall thinner to give towers more range.
Wall thinning
After another playtest from my partner, they found that the inner wall was quite thick, and putting towers in the middle of the wall would waste a good amount of their range with just the wall. By making the inner wall a lot thinner, towers placed on it have a good amount of range on either side.
Weak spot particles
While the weak spots were obvious to me as the developer who made them, they weren’t as clear to playtesters, so I needed to make them stand out more. I added some passive particles to hopefully draw some attention to the weak points. I had some particle effects from an asset pack, and I modified them to make them suit this context.
Weak spot destruction feedback
It also wasn’t clear to players when a weak spot had been destroyed, so these needed some work too. Again by modifying particle effects from an asset pack, I added unique particle effects to the weak spots being destroyed, to make them stand out more. I also added unique sound effects for their destruction, to emphasise the action even more.
Module names shown when held or in towers
Another feature I felt was missing was the ability to see what modules were in a tower, or what module you were holding. This would greatly improve the player’s quality of life, as they could see what modules are in a tower at a glance, and know whether they want to insert, remove, or swap modules instantly.
Explosive barrels
For the next enemy, I had the idea for an enemy carrying an explosive payload that would deal large amounts of damage to the core if reached. I wanted the explosive to be a separate entity, so I created this simple explosive barrel, with a model from an asset pack I had.
Flying bomb enemy
I thought the idea of the enemy carrying an explosive that was very deadly to the core, but could be used as a weapon against the enemy was really interesting. I created a simple flying enemy and attached the explosive barrel I’d created to it. The player now has options when attacking these enemies:
Shoot the barrel straight away. It’s a large, easy to hit target, and it will explode and kill the drone.
Try to shoot the drone without hitting the barrel. It’s hard to hit past the barrel, but if you can take it out, it leaves an explosive that the player can detonate at their discretion.
Project Future
The original goal of creating a prototype for this game, without getting slowed down by non-essential features, went really well! I’ve enjoyed adding some extra features after the initial project was completed, and have more planned. Some of the bigger ones I have planned next are:
Add two extra tower types with different base stats, they’ll pair better with some modules and worse with others.
Set up a new map with some basic environment, and more interesting enemy paths, to give the player some thoughts about where to place their towers, and where different range towers could do well.
Instead of the player having a gun, they can pick up a tower, carry it around and fire it themselves.
For a brief overview of the project, and a short gameplay video, see here.