As part of our first project for Studio 2, we had to create a creature-based AI. Conditions for this being:
- Must be able to path find within the world. The pathfinding algorithms must be implemented individually. Built in pathfinding or third-party solutions are not permitted to be used.
A procedurally generated map or terrain for the AI operate in.
- Have individual behaviours where the individual behaviours attempt to achieve some goal (eg. collecting food). Have a higher level AI that decides which behaviours to run.
My Creature Feature
Instead of a creature, I decided to use Taxi's in a procedurally generated city fighting over fares. Each taxi will have their own set of Trust logic and Behaviour logic systems with which they will be able to decide on whether they should help their fellow AI or not.
What went right
1. Design / planning process
From the beginning it was important to me to layout everything in a manner I could visualise. This started with just writing down and setting the goals that needed to be achieved over the life time of the project:
Node based Movement and generation
- Procedurally generated cities using Perlin Noise
- A* pathfinding logic
- Behaviour Logic
- Trust Logic
Then from here working out which I needed to do first and start writing and planning the technical design. Even from early on I was creating images of the process I wanted to achieve, this is important for two reasons;
1: Visualising helps you think about how you want the thing to look and operate
2: Making these in a step by step process helps you find all the bits needed and possible issues early
Remembering as well that none of this is concrete and mostly likely your first plan will be your worst plan. Don’t feel tied to your first process.
This was one of the step processes I made for the map generation
The process I used here changed at least three times (and honestly even this process has changed slightly since release)
2. A* creation
This was my first attempt at A*, it’s not perfect but I’m pretty without it came out. Once again (going to sound like a broken record here) I think I owe the success due to planning. By laying out the process in which you want to complete things it becomes easier to work out how that needs to be done.
This was the basis I worked A* off:
Need a list of all the node to process (Open list)
· If the node has been processed (closed)
· The cost to a node (G cost) and the estimated cost to a node (H cost)
· The initial G cost for all nodes should be set to the max possible value
The starting nod is added to the open list, its G cost is set to 0 and its H cost is calculated
While we still have nodes in the open list:
· Remove the node with the lowest total cost (F) from the open list. This is the current node
· If the current node is our destination, then exit
· Mark the current node as closed
· Loop over all of the neighbours for the current node
Calculate the total cost (G) to the neighbour from the current node
In the new G cost is less than the stored G cost and the node is not closed
Set the g cost of the neighbour node to the newly calculated g cost
If the neighbour is not in the open list, then add it to the open list
Set the current node as the parent of the neighbour
By following this process and taking time to plan out the technical design before touching code made for a much smoother project.
This was the results of me path finding from one seed point to another.
3. Map generation
As I mentioned before this went through multiple different design processes to try a create the best results and it showed. The first method used to create the maps and link the roads was actually the gif above. With each seed point connecting itself to every other seed point. However, this came out looking really messy and cluttered. So, after a conversation with a teacher I trailed a new process in which it was all split into sections (kind of like a city grid).
This came out looking pretty good and is the basis for my generation at the moment from here I made tweaks here and there and add new tiles to create more variation.
Considering this was a project exclusively based on coding this wasn’t exactly essential, but I feel like it having these “Creature comforts” (see what I did there?) made the sim feel a lot better while still being interactable.
The only issue that arose from this was involved with the mathematics for generating the live table data. Which honestly wasn’t that big of a deal. Overall, I feel like the project came out looking the way I intended.
5. Json Implementation
This was a core component of the project having state that could be saved out and loaded. Considering I’ve used json in the past it wasn’t much time to get this up and running, I just had to work out a few things with regenerating the map.
There were no issues here and it works great, it surprised me how fast it can write 50,000 lines to memory.
What went wrong
1. Rewriting logic system
This was an interesting case, so previously I had divided up the logic of each taxi into two sections, the behaviour logic and the trust logic. The behaviour logic taking the grunt of the workload by being a giant state machine.
However because I was far ahead of schedule, my teacher suggested I tested myself and rewrite it to a different approach. That approach being GOAP (Goal Oriented Action Planning). I struggled quite a bit with this, new versions were vastly two complicated for my needs and older versions (such as Tyrion) were extremely hard to find information on (can you guess why with a name like that).
With a lack of understanding, I tried implementing a far more complicated system and ended up buggering myself spending much longer than I needed on this.
With some planning help from my teacher though and some visualisation I created, I was able to finish it and I have to say its pretty sweet seeing in action.
2. Debugging logic
Oh boy, debugging AI logic, this was and still is (with some current logic bugs I have) a nightmare to deal with. Finding something that only happens at very ambiguous times and only happens on a Sunday (ok, I’m joking about that part) can be super difficult. Breaking up the logic into separate goals and actions for GOAP did help narrow down that process but I spent far too many hours attaching visual studio in debug mode to very little avail.
What can I learn from this?
1. Rewriting logic system
Using expandable and modular systems like GOAP are a great way to improve the quality of a project but it is important before making a start on it to know how complicated of a system is required for your needs and then planning the logic from there.
2. Debugging logic
A couple of things here;
· Create separate and well defined systems:
So that it is easier to read and track
· Take breaks from debugging:
Go outside gets some air and give yourself some time to mull things over. This can save a lot of stress in the end
· Ask for help / get a pair of fresh eyes:
Similar to the above, you can be so focused on a particular thing that I can be hard to narrow something down. Getting another opinion can be really useful
For the most part I am super happy with how this project turned out, it has been one of my favourites so far during my time at SAE. Not only have a learned interesting and new practice methods but I also had fun while doing it and that’s the best you can really ask for.
Thanks for reading!
· Developer: Ben Walker
· Link to project blog: https://blog248708493.wordpress.com/2018/09/27/studio-2-creature-feature/
· Link to Sim: https://walkies3.itch.io/creaturefeaturetaxis
· Length of development: 7 weeks
· Development tools: Unity, visual studio, MagicaVoxel