It’s a new year and a new post from me. Today I’m going to be writing about something I’ve been playing around with using the Unity game engine for a bit.
A while back I had a fun little idea for a game that never came to be: A sheepdog simulator. Basically, the user plays the role of a sheep dog and there are some sheep and that’s about it really. While this has never become an actual game I thought that the sheep would be nice as a little test bed to play around with some simple AI concepts.
When I first slapped the prototype together I included a relatively straightforward behaviour script for the sheep. This was essentially a Finite State Machine (FSM) but governed using if…else… statements within the sheep controller script.
A very brief explanation of Finite State Machines: An FSM is one of the most simple forms of AI in games. The AI has a finite number of states it can be in, for example patrolling, attacking, or idle. When in a state, the AI will execute a set of pre-defined scripted behaviour. The AI can switch between states based on set triggers that initiate a transition into the new state. It is also common for behaviours to be exectuted as a state is entered or as it is exitted.
Since I first made this, I have read a bit more about artificial intelligence in games and decided to tidy the sheep AI up a bit by formalising the behaviour in a properly coded FSM. To do this I pretty much followed the “Advanced FSM” example in the FSM section of Unity Artificial Intelligence programming, fourth edition by Dr. Davide Aversa, Aung Sithu Kyaw, and Clifford Peters. In this FSM setup, each state runs a function (or method in C# nomenclature) that executes a decision process (Reason()) , followed by a function that executes the behaviour associated with that state (Act()).
To begin with I gave the sheep three simple behaviours: wander, flee and stand. When wandering, the sheep just perform a random walk: they move a bit, then they might turn a bit and then move forwards a bit more and so on. In the flee state, the sheep turn towards a vector pointing directly away from the player at a set turning speed and moves forward at a rate inversely proportional to the distance from the player (within a minimum and maximum speed). Finally, in the standing state, the sheep stands still and does absolutely nothing. The sheep begin in the wandering state and will transition into the fleeing state if the player comes too close. Once in the fleeing state, the sheep will transition back into the wandering state if the player is sufficiently far away. If at any point the sheep enters a pre-defined area known as the pen, the sheep enters the stand state and cannot transition into either of the other states.
This simple FSM makes for quite a fun experience chasing sheep around. But now that it is in place, I want to play around with it a bit more! So sheep hang around in flocks right? So sheep are birds. That means that we can model our sheep as boids (or shoips?… no…. that’s stupid).
Okay so what are Boids? This is where it gets cool. Boids is a term coined by Craig Reynolds to describe an artificial life program that simulates the flocking of birds. This essentially implements three simple rules: 1) the boids must avoid colliding/crowding by maintaining a certain separation. 2) The boids should try to maintain a common alignment by steering towards the average heading of the flock. 3) The boids should maintain cohesion of the flock by moving towards the centre of the flock. To learn more, I would recommend this great video by a guy called Sebastian, and if you’re interested in this sort of thing he does a great job of explaning interesting coding concepts on his youtube chanel as well as providing some excellent tutorials.
So how does this work with my flock of sheep? Well first of all, I add a new state for the FSM that I’ve called BoidingState. Within the new BoidingState the sheep will execute a Reason() method that checks whether there are any nearby sheep that can be added to the flock. The transforms (a data structure unique to each game object that contains the position , rotation etc.) of each other sheep in the current sheep’s flock are stored in a list in the BoidingState class. If the player is too close then the sheep switches to the fleeing state. Otherwise, if any of the other sheep in the current sheep’s flock are too far away, they are removed from the flock with a CullFlock() method. Then, if there are no more other sheep within the sheep’s flock, then the sheep will transition into the wandering state.
The Act() method of the BoidingState is super simple to implement. First of all the sheep checks to see whether it is about to collide with something using a RayCast (basically a straightline shot from the front of the sheep that reports back the first thing it hits). If the sheep is on a collision course, it turns. Otherwise, if the direction the sheep is facing is different to the average heading of it’s flock by more than a certain tolerance value, it turns towards the average heading. If it is already moving roughly in the same direction as the flock, it turns slightly towards the centre of the flock. These simple conditions allow the three properties of Boid-like behaviour to be realised.
Now, because I went to the trouble of setting up the FSM properly this time, all I have to do is add the new state and associated transition to the ConstructFSM method in the SheepController class and add the new transition to the wandering state and I’m done. So the lesson here is to code well. Sloppy code might seem like a good idea if you’re in a hurry to see results, but as your AI becomes more complex it will take more time to add more states. Now my simple AI is easily expandable thanks to my simple FSM system and each new state is as easy to implement as the first. So… do good, not fast. Good lesson eh?
This has been a really fun experiment and the sheep seem to be boiding nicely, although to be honest at the moment the effect is pretty creepy. I think the creepyness will go away once have implemented an offest in the animation for different sheep. At the moment it looks like they are marching, and that’s pretty weird. Another fun thing is that because I haven’t included any collision avoidance in the fleeing state, I can force the sheep to collide if I chase them and because the physics material used is distincly unsheep-like they bounce off each other very impressively. Suffice to say, as a simulation of a flock of sheep, more work is needed, but it is still a lot of fun to play around with. If you want to check out the code, take a look at the Github repo here.
If you liked this post, you might enjoy playing a little game I threw together recently. It’s a retro space-based infinite scroller that you can play on my website here.