ai_behavior icon indicating copy to clipboard operation
ai_behavior copied to clipboard

Behavior design

Open bvssvni opened this issue 10 years ago • 0 comments

The current design is based on AI behavior trees, extended for parallel semantics.

AI behavior trees

An AI behavior tree is a tree structure where each node represents a process. When the process terminates, it signals Success or Failure. This can then be used by the parent node to select the next process. A signal Running is used to tell the process is not done yet.

For example, a non-player character walks back and forth a straight line until the player character is closer than a preset distance. This behavior can be thought of as a single process, or consisting of smaller processes. The higher up in the tree a node is, the higher level of behavior. The same behavior could be described in more detail, for example how it moves its legs and turns around to walk back. Therefore, we use the term "behavior" for all processes in the tree, whether they are low level or high level.

In many cases programming a behavior is as simple as describing what the entity is doing:

while player is further away than distance X, walk to point A, walk to point B.

In a custom scripting language, this could be programmed as:

while player_is_further_away_than_distance(X) {
    walk_to_point(A),
    walk_to_point(B)
}

Piston's AI behavior trees has parallel semantics. The operation while checks a process taking place while executing a list of processes over and over. This means that walk_to_point can be interrupted.

Parallel semantics

Parallel semantics has two important properties:

  • Makes it easier to create more complex behavior from simpler ones
  • Infinite loops of behavior can be terminated based on external conditions

This semantics solves the problem of scaling up building blocks of behavior to any arbitrary size. Such trees can be put together in many complex ways, resulting in many complex states.

The building blocks used in this library has been crafted to cover as many parallel scenarios as possible using common sense as guide.

One effect of parallel semantics is that it can't be simulated perfectly on a single thread. The behavior is deterministic, but the logic breaks down for shorter time intervals than given by updates per second. The consequences are determined by the interaction of side effects.

For example, in a racing game, WhenAny can be used to detect when there is a winner, keeping track of a process for each car. The first car in the list might trigger Success even if the second car logically should come first, but only if the first car completes within a delta time interval. However, this will have no logical consequences if the physical simulation runs separately and the winner is picked based on who actually crossed the finish line first.

Behavior vs State

For each behavior there is a state that keeps track of current running process. When you declare a behavior, this state is not included, resulting in a compact representation that can be copied or shared between objects having same behavior. Behavior means the declarative representation of the behavior, and State represents the executing instance of that behavior.

Events

There are to kinds of events, update events and non-update events. Update comes with a delta time interval. When an process terminates, it tells how much time is left of the delta time interval, such that the next process can be executed for the remaining time.

State uses Piston's event model to modify the delta time interval. This makes it possible to use AI behavior trees together with other controller objects, including calling other State objects as actions.

When an action terminates on a non-update event, the action is consuming the event. For example, when a button press trigger an action to succeed, the button press event is not passed to the next action. If you have Sequence(Press(A), Press(A)) the player need to press A twice.

Update events are partially consumable. When one action terminates, it can pass on the remaining delta time to the next action.

Therefore, actions in general are usually divided into update actions and non-update actions. Update actions are usually doing game logic stuff, while non-update actions are usually handling player input or some external events.

Instant actions

Update actions that does not consume delta time, returning the same delta time as they receive, can lead to infinite loops. A Wait behavior can be used to prevent this. The meaning of update is defined as "consume time to stop" so it will continue running actions until it hits one that does not have enough time to terminate.

bvssvni avatar Aug 19 '15 12:08 bvssvni