Overview

Using Unity I was tasked with demonstrating a number of features that AI would uses in Games, this involved Steering behaviours, Pathfinding and Decision Making that combined to create an AI agent that would fight off in a horde shooter.

Grade: First

Buliding the Foundations

First came the maths that would lay the foundation of the AI, this included basic vector maths such as Magnitude, Normalise, Dot product and Cross product. This was crucial to get right as it is the way the AI will perceive the world around it, how it knows where the enemies are or what direction it is facing.

First Steps

Next came Steering Behaviours while considered basic steering behaviours are great at adding extra behaviours to a more advanced AI. For example, I used evade for my AI agent if an enemy is too close, I felt this looked more natural than pathfinding away from the enemy. The steering behaviours include seek, flee, pursuit, evade, arrive, wander, collision avoidance and group behaviours.

The Path Taken

Pathfinding upgraded how the agent will navigate the space, while seek could be use to take the agent from one space to another if there are obstacles in the way its unlikely the AI will make it to the destination let alone in an immersive way. The first Pathfinding Algorithm I implemented was Dijkstra’s algorithm being the first place to start for many pathfinding algorithms, although Dijkstra will always return the shortest path, should there be one available, it is an undirected search so will search all nodes without direction and fans out from the start location, this lead to A* a directed version of Dijkstra’s using heuristics to guide the search in the direction of the goal.


The Video shows off A* pathfinding with multiple different heuristics, the coloured squares highlighted shows the nodes searched by the algorithm.

Jump Point Search

The focusing of the search makes A* more efficient than Dijkstra’s algorithm and in this pursuit of efficiency Jump Point Search, JPS, pushes it further by only adding nodes to the path should they meat certain conditions mainly if another jump point has been found in the direction of the goals.

Image of all behaviour tree nodes Used

Decision Making

To create my AI agent, I want it to feel as real as possible I decided to create and implement a behaviour tree, as it had more flexibility than the other methods and did not have the drawback of state thrashing that may occur with finite state machines. So, I started by creating all the potential nodes that could be used so when it can to following the logic it was a simple case of coding my designs.

First, I wanted to make my agent have a base state of wander make it look like they are exploring the map while not in danger. By utilising my previous wander implementation, and Jump Point Search (JPS) pathfinding the agent was able to wander without getting caught on walls.

After getting pathfinding working for my agent to wander around the map it made sense to create a new leaf node that would change the target position of the pathfinding to the pickup location, this was done using the actions inside of unity to listen for when a pickup is spawned and set the target locations in the dictionary and conversely if the pick up is collected then the locations are reset in the dictionary and the wandering begins again. Deciding on which pickup to go for comes down to the players health, while using different guns can be useful once bellow a certain health the agent is in danger of getting swamped by enemies before having time to react.

Shooting and evading were created with the other in mind while shooting at an enemy it is likely we want to be backing away from them to limit the danger from them. However, I wanted to keep each node as decoupled from the others as possible, this led to ordering them in the tree appropriately. As the agent won’t be firing at an enemy unless there is one close then it is safe to assume there is no enemy to flee from, so using a sequence for these two nodes specifically means that if the agent doesn’t find anyone to shoot, we don’t bother trying to find an enemy to flee from. After implementing both of these nodes I ran into an issue where the agent would try to flee from or shoot at an enemy behind a wall, this was a quick fix by firing a raycast at the enemy the agent is trying to shoot and if that ray had hit a wall before the enemy ignore it as it is not in line of sight, this can be seen in the video, especially towards the end, with the red lines locking onto the enemies and if they are behind a wall they break.

Finally, I wanted to switch weapons based on certain conditions, for this I wanted to try and recreate what weapon I would likely select, this meant that if the rifle has ammo, I want to be using it unless there are main enemies, or I am low on health. Putting this node into the tree took some consideration as the agent should select the gun before shooting the enemy but not get locked into choosing a weapon and not shoot, after some testing I put the node before the shooting node as it I felt it gave the more believable behaviour.