This is my third post on programming NPC behavior in a prototype game engine. I summarize my design (it’s working!). Next I’ll look into other features I might want to add to the prototype.
Summary of design so far
The Script class provides methods to setup a script using simple, statement-like function calls. The Script class also realizes a data structure for the resulting script. Classes:
- Script
- Type
- ScriptDef
- Selector
- Statement
The Interpreter class binds a script to a scene by setting up an action graph
An action graph is defined as a hierarchy of actions and conditions. The root of the action graph (typically a Transition instance) is invoked at every model update. Classes used in the action graph:
- Action – defines the apply method
- Transition – a simple action group
- XTransition – extends transition with the ability to replace an action by an alternative if failed, or a next action if complete.
- PrepareActors
- BasicActorUpdates
- XAction – adds the (BOOL)failed and (BOOL)completed methods along with definition for alternative/subsequent action
- ReflexMap – Select and apply the first action that matches an associate trigger condition
- Decision – Base class for actor actions (all decisions define an agent)
- Do – general purpose action (these actions typically match to a sprite animation)
- LookAt – make an actor look at another actor
- Approach – come closer to another actor
- Follow – come closer to an actor if not within a given threshold
- Avoid – go away from an actor if within a given threshold
- Transition – a simple action group
- Condition – defines the (BOOL)check method; used in reflex maps
- NearestTarget – evaluate the nearest target given an agent and a list of actors
- TargetSelector – a complex action used to select one or several actors based on several criteria (min/max distance, actor tags, actor activity)
In my tests, I create an XTransition instance as root (this is invoked by an NSTimer) and add the following nodes in order:
- PrepareActors. This is used to clear some actor parameters. In my first tests, the speed vector associated with an actor wasn’t cleared. This would be OK if speed was redefined every time, but some actions (e.g: Avoid) are modifiers rather than setters.
- An XTransition node, used to hold actor decisions.
- An instance of BasicActorUpdates. BasicActorUpdates allows resolving conflicting decisions; for example, BasicActorUpdates prevents actors from entering a solid cell.
I have tested this design with a simple script and (sure, after some debugging) it works quite well. I’ll try to post a picture or video later on.
Main design features
- The action graph separates game behavior from game data. It also allows specifying complex behaviors at runtime by adding and removing nodes. At compile time, the action graph requires the data model.
- Tags are string or integer labels used to identify actors indirectly (used in place of data pointers). This allows defining both class and character specific behaviors.
- Scripts are defined independently from either the action graph or the scene. Scripts have no compile time dependencies with either the data model or the action graph, so they allow simple, highly portable game behavior definitions – An interpreter is used to bind a script to an actual action graph and scene.
- Separate decision and action. This is not required by the API, but simplifies behavior programming a lot. First all actors evaluate ‘what they would like to do’, next interactions are resolved locally, taking conflicts into account.


Comments