I am pleased with the design:
- Runs in a separate thread (separate hardware thread on the Xbox)
- Shares processing fairly between all the AI controlled entities (Bots)
- Unlimited levels of child behaviours
- Parallel behaviours
It is the ability to add parallel tasks that I needed. I got stuck at that point with my earlier state machine version.
The problem I had with the state machine was that a Bot may wander about and while wandering needs to be able to look out for enemies in the area. In addition when the Bot enters combat it needs to be able to shoot at a target while running for cover. In this Behaviour Tree design the behaviour can replace its own parent behaviour or launch a new behaviour to enter combat. That combat behaviour then launches two more behaviours, one to shoot at a target and the other to decide where to move to.
Each behaviour is a separate class so I have things like, Hide, Chase, Evade, Combat, Find something to do, Wander, etc. I had to create twelve separate behaviours before I could even test it. This is because so many of the behaviours are made up of other behaviours and so few actually control the Bot.
To avoid creating garbage** I had to design the structures and classes carefully. I finished by having only one instance of each behaviour shared by all the Bot classes. Instead of adding the behaviours as needed and using the individual behaviour classes to store results, the results are passed in to and returned from the behaviour classes. The results being stored between updates within their respective Bot's class'.
This image shows a simplified version of the design:
I have added a method to show on screen what any individual Bot is thinking. I have this running in the game and I can see that the Bots respond to changes in the environment by changing their behaviour.
At the moment some of the behaviours themselves are not carrying out the actions that I would like but as each one is now independent and broken down in to smaller and smaller behaviours I can work through them to adjust each in turn to get the results I want.
** Garbage is the term used for adding and then freeing up memory on the heap. This freed up memory needs to be collected for re-use by a framework process that is relatively slow on the Xbox. Just adding to the heap is eventually sufficient to trigger the slow collection process even if there is no memory available to free up.