If your low-level behaviors are designed to run concurrently, and you’re using parallels to fork control inside hierarchies, whole subtrees of behaviors can run at the same time. Now you can generate many interesting concurrent behaviors, but the problem is that most of them are just not realistic. For example, it’s probably possible (thanks to your audio engine) for your actor to say two things at the same time — but it’s not ideal!
A resource allocator managing access for two parallel trees.
So you need a way to synchronize trees, in order to prevent these unrealistic behaviors from occurring. To do this, you can use a resource allocation strategy, which is just another node in the tree. Put one of these allocators around each resource that needs special treatment to handle concurrent behaviors, like the voice of your actors.
Types of Allocators
The most simple strategy for resource allocation is a first-come-first-served system. There’s a queue which stores the behaviors as they request the resource. Only the first is allowed to access the resource, and all others are paused until it’s their turn.
This simple strategy resolves most synchronization problems, but often you’ll need more control over the scheduling of the behaviors that need to access the resource. The next step is to use priorities for each behavior, and order the queue using their priority level. So the most important behaviors would get access to the resource first.
Finally, your allocators could be much more complex, and attempt to arbitrate between the different behaviors in the queue. This is possible, for example, if you have a path-planner than supports multiple goals. The “destination” resource would be chosen by the arbitrator as a compromise between the requests of the individual behaviors. (This approach is rarely currently in games.)
Common Configuration Options
The main decision you face when creating resource allocators is to decide the scope. Which behaviors in the whole game share that allocator? It could be on a per-actor basis (like the voice), a per-group basis (the right to fire at the player), or even a global basis.
Also, you may decide how many behaviors are allowed to access the resource at the same time. For things like limb animation, it’s wise to limit the number to one in most cases. But other cases it may be a higher limit, for example the total number of actors speaking at the same time may be set to two or three in a large crowd.
When using priorities, it may also be necessary to add an option whether the resource should support interrupts or not. So a low priority behavior using animation would be kicked out by a high-priority reaction. Then, you also have extra options whether to allow certain behaviors to queue-up or not…
Words of Advice
As you can tell, resource allocation strategies can get pretty complex. The best advice is to start simple! Once you’ve identified which additional features you need, separate them into options for the resource itself (like scope) and options for the behaviors using it (like priorities).
As you add features, make sure you unit test everything as it could backfire otherwise. Priorities and interruptions are pretty tricky to implement, so avoid them if the design permits you to do so.
Generally, try to keep the behaviors very short so there’s not much need for interruptions. You can do this by making behaviors that change the state of the resource and then terminate; for example, a behavior for SitDown rather than StaySitting which would lock up the whole body resource for much longer.