AI | Influence Map | Navmesh Baking | Learning | Troop Formation
TLDR: With help of an Influence Map that could modify a navmesh in runtime, a God AI would send troops in formation towards a fortress with a goal of breaching the defense and attacking it's runestone.
The specialisation was the final course at The Game Assembly where we had 100 hours to specialise ourselves in a game programming field and create our webpages. I put around 80 hours into the specialisation.
I have the project on Github if it is of interest.
When deciding on what I wanted to do for my specialisation, I knew it was AI. But what I wanted to do was unknown. I was in this spotify period where I was mostly listening to soundtracks from Lord of the Rings and World of Warcraft. This inspired me, with the picture of Uruk-hais attacking Helm's Deep, to create a "God AI" that could send units running in formation, trying to breach a fortress. I wanted to make something that looked neat.
I decided to use Unity as game engine. Since I was quite experienced with Unity, the engine would not hinder me in my work. I was keen on trying out Unreal again, but then there was a risk of my specialisation turning into trying out Unreal more than AI. As I wanted it to be somewhat cool and not code with capsules, I used assets from the previous project, Spite - Ragnars Mjöd, and created some own models in Maya.
The god AI didn't have a Saruman telling weakpoints in the defence and ladders to climb over walls with. I needed something for the AI to figure out which part of the defense was weak and better to attack, something to read from when "learning". The solution would be to use an influence/heat map to collect data from. I created a grid for the Influence Map to store data whenever a unit damage an object, a unit died or an object was destroyed. This data was transported via a postmaster system that I also created for the project.
After each attack made by the god AI, the influence map would place out navmesh modifiers on areas that are unwanted or prefered to walk upon. After placing, the influence map would rebake the navmesh in runtime, creating these areas.
Although I've had issues with Unity's navmesh in the past, I decided to use it as it would save me a lot of time. The scene had two navmeshes, one for the formation generals that would move the formation, and one for the units to walk upon. The difference was for the formation generals navmesh, as it was for a broader agent and would rebake depending on the influence maps data. When rebaking the navmesh, the troops would either avoid and walk around areas where there previously had been a lot of casualties, or walk through an area that had shown success.
Unity however didn't have an option to calculate the cost of a path with the areas crossed, so I had to do that calculation myself, with help of the influence objects (boxes in image) placed by the influence map. At each corner of the path to available targets, I would raycast to the next corner when calculating the length, adding modifier values depending on the area cost.
The God AI would do the grind work. It would tell the influence map to bake the navmesh, send out troops and do data calculations. The AI would send out two formation generals with 30 units each to the best target. The calculation of the best target was the part that took the longest to work on. There were a lot of numbers and different "fitness" values that needed to be tweaked. And then I was a bit silly and created a big map, which could have long distances to run. This resulted in longer simulation times.
The final recipe for the best target was the one that had the least amount of failure, biggest threat, most damaged, highest value, best area value and the shortest path cost.
Whenever the troops succeeded or failed (they all died), the God AI would do a new calculation to determine which target to attack. The algorithm was as follows. Influence Navmesh -> Rebake Navmesh -> Calculate best target -> Send out Troops -> Wait for result.
As my main focus wasn't formations, I started of creating them early with simple positioning. I had a Formation General that handleded the pathfinding to targets. The formation general had a list of units, and depending on which formation the general used, it determined their positioning and made them move to those positions. I tried a bit with steering behaviours making them avoid each other, but decided to only use it while attacking, as it looked better and more smooth when they could clip into each other when running to their formation slot.
Unity has a built in Agent obstacle avoidance, but I turned it off as it freaked out the general when the units were close by, which was always. This caused units to detect obstacles only when close by, trying to move around them afterwards. I didn't see it as a big issue, as my simulation map didn't have really tight areas and much collision avoidance.
The result of the project was a God AI that eventually would crack the gates and reach its target. I am overall pleased with the result, most happy of reaching my goal of creating something that looked neat! I had a lot of fun moving around and following the characters whilest listening to epic music. When I added the final touch with night time, it got even better!
I've once again learnt that working with AI takes a lot of time balancing, trying to find the values that make it look good. Especially with the "learning" AI, I could have been tweaking variables for ages. I found it difficult finding a balance of what the correct behaviour should be. Honestly, the troops could run into the same direction over and over again, eventually cracking the base, with no casualties taken into consideration. That, however, would not have been fun at all.
If I had more time, I would have definitely created some troops on the defending side, creating close combat battles. My initial plan was also to have ranged units, but I cut them from the plan quite early as the project already had a high scope. When the enemies attack, they all kinda clump together, fighting to find a spot where they could attack. This could have been solved with a feature where they would wait for their turn to attack. Also, it would have been nice and more of a "battle simulation" to send out troops in intervals, instead of only sending out two troops at a time and wait. And finally, of course, tweaked a lot more on the decision making variables.