For the final part of this portfolio assessment we've been asked to create a simple zombie game. In this text I will talk about the process of creation and link to the video of the finished product, then talk about the AI in detail with the use of some diagrams, and finish with conclusions.
While most of the work spent on this game was following the tutorials described in the four weeks up until the assessment, at the end we’ve been asked to add upon the base and make it ours. And so, my project used different assets then preferred. The base of the game is very similar – we have a set number of zombies roaming the area, and the player’s task is to survive as long as possible. The zombies have an AI system that I will talk about further into this text. The area is fairly small, composed of wide corridors and halls with walls and columns strewn about to give the player a chance to hide and to showcase AI’s pathfinding. Both the player and zombies have a set amount of health, and once it drops to zero either a zombie dies, or the game ends and we get our score and a chance to play again.
In addition to that, I added some non-AI extras such as the environment and lighting – the tutorial’s environment was very bare-bones, so I decided to spruce it up a little and make the atmosphere darker. This is why the game takes place in a closed, dark environment with only a few lights, adding some extra with the shadows. I also wanted the player to make sure how much life they have left, and so created the health bar.
Here is the video showcasing the game and the AI
AI in the project
In this project the main bit of AI is the zombies’ decision making, which is a fairly straightforward Finite State Machine which consists of 4 different states – Wandering, Chasing, Running, and Dead. While dead is fairly self-explanatory, and the machine doesn’t exit from it, others are more robust. For the wandering state there is a set of waypoints which the AI will shuffle and go through in that order. From that state, if it sees the player character there is an if statement. If the zombie has 50 health or less (so half or less) then it will switch to Running state, otherwise it will start Chasing.
When it’s Chasing it simply targets the player and runs towards them as long as they can see the player. Once they lose the sight of them or have 50 or less health points they will go back to Wandering. This basically means that once we leave the Chasing because of amount of health we will never visit it again. Running uses a function to find the waypoint which is the furthest from the zombie and run towards it, giving an illusion that the zombie is running away from the player. It will stop Running and start Wandering once two conditions are met – the zombie has to be out of the player’s range (which is just a variable, they have to be a certain distance away) and the timer, set to 3 seconds has to pass – this makes sure that the enemies will run for long enough to move outside of the player’s reach.
The AI that I created isn’t the top of the line. It is very simple, and sometimes has some hiccups but other than that it’s fast, and it could allow for many enemies to use it at the same time. I also ran short on time as other deadlines kept me pushing this one away, but I think that what I have created is at least presentable, if not decent. More could be done with the way the enemies either run away or look for the next waypoint, but I simply run out of time to do so and know it would work. On the other hand I’m happy with the non-AI extras that I added in the short amount of time, making sure that the game actually feels like a game and not a tiny demo showing off nothing new.
Here is the link to the Github repository of the game files.
Week 5 started with presentations about our projects that we'll be doing for assignment 2. For me it was about adding AI to a 2nd year project - a single stick shooter with a lots of bullets. There would be two types of AI - a unit AI which would be a finite state machine, and a wave AI which could work between waves and make the games harder or easier based on how well the players are doing.
Here's the link to the presentation.
With the zombie game the last week was all about adding our own touches to the game to make it more interesting, and the AI/game more refined. To do that I added health for both the player and the zombies, made it so the player can die, and added a score and a menu that allows to try again. I will write in more detail about it in the next post detailing the whole game.
Week 4 – the main topic was AI within board games. The first session was a discussion about different types of board games and AI techniques it could use. The tasks were related to board games too – first to create a game tree for a game of choice. I have chosen a game of Checkers – this is a two-player game, which is zero-sum (one player wins, one player loses).
To win is to remove all of the enemy’s checkers – an interesting rule involved is that given the opportunity a player has to remove enemy’s piece. Even though it’s played on a chessboard, the number of moves involved is smaller than with chess, since pieces only move one space diagonally on black spaces – the first turn has only 8 possible moves. This means that the MiniMax algorithm could work nicely for this game. There could be better ones such as AB Pruning, but for simplicity’s sake MiniMax should suffice.
The second task was in relation to tactical games and use AI in them – we got asked to investigate the Condensation Algorithm, which outputs the best possible positions based on tactics used using a grid and separates locations that are not connected. It is actually called Conditional Density Propagation algorithm.
Next question was about non-military games using group coordination. I think it can be used in those, with any games where using a crowd of characters is used, example being games such as Pikmin. It could be used in coordination with boids to create an illusion of realistic birds being controlled.
With the Zombie game it’s the last stretch of the basics is inclusion of random spawning of enemies to make it more difficult and like a game. This means figuring how many zombies we want at any given time, and where to spawn them from.
This week was about AI decision making. There is a lot of ways of going about decision making, between decision trees and different types of finite state machines.
The first task was creating our own decision tree for a bot with a few different behaviours, such as attacking, fleeing, wandering and healing.
Next was creating a finite state machine for a guard with a few behaviours such as patrolling, searching for enemies, and attacking – all with different ways to pass between them.
And after that, that finite state machine had to be adapted to a hierarchical state machine, expanding the search state and the attack state.
The second lab session was all about the portfolio feedback, so in the background we got asked to research some open problems in the game Ai, something we mostly talked about without writing anything down.
On the Zombie game front, we added a new state to our state machine, chasing – the enemy can now check whether the player it’s within its cone of vision, and if so will chase the player until it no longer sees them. We also added the ability to change between states, so this can be actually done. Additionally, we want the zombie to be able to die – to do that first we have to do the red section from last week which describes how to make the character able to punch – we are adding a new animation to the animator, as well as a check that makes it able for the character to punch – it came out kind of wonky, but you can see that it’s supposed to be an animation of a punch. Once we have that set up we can continue with killing the zombie. We first create a ragdoll using the unity ragdoll wizard, this way we can make the enemy flop once it dies, and make sure we activate it in-code. Then we create a trigger collider on the player’s right hand and create a script that checks whether we’re pressing the attack button, and if so turn the collider on – and then check if it’s hitting a zombie, and if so invoking their kill function. Additionally, as an extra thing we were told to texture the walls at our own leisure.
Week 2 of the 304CR module was about movement and pathfinding in use with AI.
The first lab task was about movement and asked us to check out an example of boids, inspect the code and figure out how do cohesion, separation and alignment values affect it. After checking out, I can tell that the alignment value affects singular boids and how fast they’re turning. The cohesion weight affects how fast the boid will try to go towards the centre of the swarm and the separation affects the distance between the boids that they try to achieve.
Another task was about pathfinding – we got a program which allows for creating maps and running different pathfinding algorithms on it – for the first part we had to check Djikstra algorithm (which goes in all directions equally until it hits the goal point), and next BFS algorithm which is faster since it estimates how far each point is from the goal, but it is a greedy algorithm in a way that it goes towards the goal as much as possible without realizing that there might be an obstacle that takes a long way to go around if you go straight at it. The next algorithm was A* - it combines Djikstra and BFS and is known to be the best out of pathfinding algorithms. It estimates the cheapest cost of arriving at the node as well as the heuristic estimate of the cost and based on that decides whether to use Djikstra or BFS to pathfind from any given node.
For the zombie game the next step was creating a simple Finite State Machine for the enemy AI – we created three different states and figured out how to make them work. For wandering state, the AI has a list of points it will go between at random. Additionally, I learned how to grab characters from sites such as Mixamo and use them in Unity. This is helpful in my future endeavors.
Labs for week 1 were fairly straightforward – we looked at different AI behaviours in video games. We had two different examples to observe – a body guards one, where the guards AI seemed to be highly intelligent, trying to maximize the area seen by them, while also taking into account windows and corners since that’s where enemies usually appear. Another example was a predator and prey one – this AI was fairly simple in comparison, with prey just grazing and running away if the predator was within reach, although they did seem to travel in herds. The predator was even easier, resting for a bit, and if it saw a prey then it tried to rush it, but it did seem to get tired after a bit if no prey was caught.
Additionally, we had to group up and discuss good and bad AI in video games, one that stood out to us. I personally enjoy the AI in tactical video games such as Jagged Alliance 2 or the XCOM series. The enemies in those games seem to be very aware of their surroundings and tend to use crowd control techniques to move the playable characters where they can pick them out easily. A game with AI that wasn’t so great could be some of the Worms games. The AI would either pot perfect shots that wouldn’t be humanly possible, or miss by a country mile, leaving no illusion that it was an AI that just decided to not hit. And the difficulty on the AI would only decide how often it would hit/miss.
However, for the presentation about good and bad AI we talked about XCOM2 and Stellaris – I know very little about it, but my presentation partner knew that it was very poor.
Link to the presentation is here.
We also had to start working on the zombie game – a simple project that shows that we can create simple AI following the tutorial. First week’s tutorial was fairly simple, with creating the project, setting up the scene and creating a first character that could follow to a point of our choosing. It however begins with grabbing the Unity’s collection of assets that we can use, and then the scene with some walls and a character. We then change the camera and set up the navigation mesh and the agent that we assign to the character so that he can use Unity’s path-finding to walk on the plane we created. Finally we get to change the AI’s target with a script.