Freeforge TD is an ongoing personal project of mine started in 2016 and being developed in Unity. I wanted to build a tower defense game for both PC and mobile where players could place down tiles and towers and create the tower defense maps they would want to make. I also wanted the option, later on, to add more towers or tiles types so that players would have to pick and choose between certain ones.
With these requirements in mind, I decided to use Unity to develop it and learned about their 2D options. Traditionally Unity is for 3D games but in the past few years they have really been pushing their engine and documentation to better support 2D games and I really enjoy working with it.
There are a few main pieces of the game that I can talk about:
The finite state machine
The players “deck” of towers and tiles
AI pathing using a node based system
The Finite State Machine
This is the most recent part I have been developing for the game. When I first started working on the game I was not considering the need for this and boy was I wrong. For mobile controls, I wanted to have a sort of circle selection ring that would be easy to use with touch. A tap would open up a buy/sell menu, if you tap on the buy menu button, you could then choose between towers or tiles, picking towers would show the available towers (same thing for tiles) and once you select a tower it enters a mode to place it on the map grid.
I was trying to do this with a mix of booleans and checks and it became unmanageable once I wanted to add the option to go back one menu. I needed to think of a solution to this problem and it was the perfect time to use a finite state machine. I learned about these in my Software Design Patterns class back in college, I have never had to use them in a project for practical reasons though. This was the first time and I think it turned out well. I have a few states that the player can be in. Normal, Buy/Sell, Buy, Sell, Towers, Tiles, Placing. Most of them are simple states that can only go to two or three other states. Normal can only go to Buy/Sell. Buy/Sell can go back to Normal, or it can go to Buy or Sell.
When it came to implementing the finite state machine I was a little lost. It had been two or three years since I learned about them in school and I had never had to implement one in a game related project. I took to reading and found some write-ups that explained a multitude of uses for them, those can be found at: http://howtomakeanrpg.com/a/state-machines.html and http://gameprogrammingpatterns.com/state.html
After refreshing my knowledge and planning it out, I ended up putting a together a diagram to make sure I knew which states, transitions, and actions were possible. Once I had everything planned out, it was just a matter of putting together basic method stubs and plugging my existing code in once the transitions were working. It was a big task, especially when working on it part time after work, but it helped make my code more manageable and much cleaner.
The Players Deck
One of the features I want to add to my game in the future is to have an expanding selection of towers and tiles that the players can put down. I think it would be interesting to have the player choose a “loadout” of a few towers and a few tiles. So from the start when I was developing the tower and tile logic I kept this in mind.
In order to do this, I created two different lists. One list holds towers which all inherit from a base tower class and the other holds tiles that all inherit from a base tile class. These base classes hold common properties and functions that can be used or overridden by any classes that inherit from it. This will make it easy to implement newer types of towers or tiles in the future while not having to change any of my data structures.
AI pathing using a node based system
Because the maps are being built by the players, the paths that the enemies take will be determined by them. I had to think of a way to efficiently create a path for the enemies to traverse. There’s always the option of using A* pathfinding, but I felt I could solve this much simpler. While the enemies are moving, their start and end point will not be changing so I thought creating a simple node system would be nice and efficient.
For each tile type, I have 2 or 3 nodes, a node is always found on the ends of a tile. If the tile has a bend in it, then it will have an extra node right at the corner. When a tile is placed it basically appends the node to the end of the list and the list gets parsed to create a series of positions for the enemies to travel to. I also use the nodes to check if placing a node is valid or not. If a player tries to place a tile in a location that doesn’t connect to any other node (specifically the most recent node) then it will not let the player place it.
With this type of pathing, there is no need for complex computations and should keep the AI relatively efficient.