Assume you have a long-term AI behavior controlling a character in a game. It could be implemented as a script, a behavior tree, or any other sequence of actions to follow. How do you deal with events happening in the world in a way that’s coherent with this active behavior?
The AI logic needs to make two major decisions when receiving incoming events from the wold. It must work out:
Whether to terminate the running behaviors or just suspend them.
If new behaviors should start, or resume existing ones from suspension.
Obviously, in special cases, events should trigger both operations. However, you can simplify the AI tremendously by separating the logic into two types of event handlers.
Handling Broken Assumptions
Generally when you build AI, it’s useful to make behaviors goal-oriented, so they each solve a specific problem. As you design behaviors to reach their goals, you’ll find yourself relying on certain conditions about the context they run in.
Then, to make sure that the AI responds to events in a logical way, you need to capture these assumptions using some custom logic. For example:
In a script, you could monitor your assumptions by writing a custom event handler for all the facts that could change and break the behavior.
In a behavior tree, you would use parallel conditions high-up in the tree to check those assumptions regularly and bail out if necessary.
Once each behavior can monitor its own pre-conditions and make sure it terminates immediately once its assumptions are wrong, you can start thinking about handling other events.
Responding to New Opportunities
To react to potential opportunities, you need a global handler to take care of all the events (including those which also invalidate certain assumptions). This type of handler is much harder to implement, as there are many more cases to handle, but you can keep things simple by:
Only deciding what new behaviors to start.
Establishing priorities of the new behaviors.
Letting other behaviors suspend or terminate themselves.
Basically, this approach makes it a bit easier to deal with the huge matrix of possible choices that grows with the number of active behaviors. Instead, you just select new behaviors, give them a priority, and if there’s a clash over resources, let other behaviors bail out if their assumptions are broken.
It’s hard to capture all the subtleties in this problem without writing a whole dissertation on the subject, but at least now you have the basics of a solid system for handling events. It’s best to separate event handlers into two groups: one for monitoring running behaviors and shutting them down if assumptions are broken, and the other as a global event handler for starting new behaviors and working out their relative priorities.
Do you use any particular tricks for handling events in the context of purposeful behaviors?