NPBehave Documentation

repository·master·Indexed 22 days ago

https://github.com/meniku/npbehave

A lightweight, event-driven Behavior Tree library for Unity designed for code-based AI definition. NPBehave improves performance by staying in the current state and traversing only when events occur via Blackboards, rather than traversing from the root every frame. It includes support for Root, Composite, Decorator, and Task nodes, as well as shared blackboards for swarm behavior and a clock system for timers and frame updates.

Tokens
5.9K
Snippets
4
Records
29
Agent score
29%

What's inside NPBehave

  1. Use Shared Blackboards for swarm behavior

    master
    You can share blackboards across multiple AI instances to implement swarm behaviors. You can also create blackboard hierarchies by combining a shared blackboard with a non-shared one. Use UnityContext.GetSharedBlackboard(name) to access shared blackboard instances from anywhere in your code.
  2. Understand Node Types in NPBehave

    master

    NPBehave uses four primary node types:

    1. Root: The entry point; has exactly one child that starts or stops the entire tree.
    2. Composite: Controls the execution of multiple children (e.g., Sequence, Selector). Defines order and logic for child execution.
    3. Decorator: Has exactly one child. Used to modify child results or perform actions during execution (e.g., Service nodes).
    4. Task: The leaf nodes that perform actual work. You typically implement custom tasks by extending this type.
  3. Use Blackboards for AI memory and event-driven behavior

    master

    Blackboards act as the AI's 'memory' and are implemented as dictionaries that can be observed for changes.

    • Automatic Creation: A blackboard is automatically created when you instantiate a Root, but you can provide your own instance via the constructor (useful for Shared Blackboards).
    • Updating Values: Use Service nodes to update blackboard values periodically.
    • Observing Changes: Use BlackboardCondition or BlackboardQuery to monitor the blackboard. When a monitored value changes, the tree can react immediately.
    • Accessing Values: You can access or modify blackboard values from anywhere, including within Action nodes.
    // Example of an event-driven tree using a Service and BlackboardCondition
    behaviorTree = new Root(
        new Service(0.5f, () => { behaviorTree.Blackboard["foo"] = !behaviorTree.Blackboard.Get<bool>("foo"); },
            new Selector(
                new BlackboardCondition("foo", Operator.IS_EQUAL, true, Stops.IMMEDIATE_RESTART,
                    new Sequence(
                        new Action(() => Debug.Log("foo")),
                        new WaitUntilStopped()
                    )
                ),
    
                new Sequence(
                    new Action(() => Debug.Log("bar")),
                    new WaitUntilStopped()
                )
            )
        )
    );
    behaviorTree.Start();
  4. Identify NPBehave Node Types

    master

    NPBehave uses four primary node types:

    • Root node: Has exactly one child; used to start or stop the entire tree.
    • Composite nodes: Have multiple children; used to control execution order and logic (e.g., Selector, Sequence).
    • Decorator nodes: Have exactly one child; used to modify child results or perform side effects (e.g., Service, BlackboardCondition).
    • Task nodes: The leaf nodes that perform actual work. You can use Action for simple tasks or subclass Task for complex logic.
  5. Understand Stop Rule behaviors

    master

    NPBehave stop rules define how a decorator reacts when its condition changes:

    • Stops.NONE: The decorator checks the condition only once when started and never stops running nodes.
    • Stops.SELF: If the condition is met, it observes the blackboard. If the condition is no longer met, it stops itself, allowing the parent composite to proceed to its next node.
    • Stops.LOWER_PRIORITY: If the condition is not met, it observes the blackboard. Once the condition is met, it stops the lower priority node (the node defined after it in the parent composite), allowing the parent to proceed.
    • Stops.BOTH: Stops both the decorator itself and the lower priority nodes.
    • Stops.LOWER_PRIORITY_IMMEDIATE_RESTART: If the condition is met, it stops the lower priority node and orders the parent composite to restart the Decorator immediately.
    • Stops.IMMEDIATE_RESTART: If the condition is met, it stops the lower priority node and orders the parent composite to restart the Decorator immediately. It also stops itself as soon as the condition is no longer met.
  6. Use Blackboards for AI memory

    master

    Blackboards act as the "memory" of your AI. They are essentially dictionaries that can be observed for changes.

    • A blackboard is automatically created when you instantiate a Root node.
    • You can provide a custom blackboard instance via the Root constructor (useful for Shared Blackboards).
    • Use Service nodes to update blackboard values.
    • Use BlackboardCondition or BlackboardQuery to observe changes and trigger tree traversal.
    • You can also access or modify values directly from Action nodes.
    // Example of an event-driven tree using a Blackboard
    behaviorTree = new Root(
        new Service(0.5f, () => { behaviorTree.Blackboard["foo"] = !behaviorTree.Blackboard.Get<bool>("foo"); },
            new Selector(
                new BlackboardCondition("foo", Operator.IS_EQUAL, true, Stops.IMMEDIATE_RESTART,
                    new Sequence(
                        new Action(() => Debug.Log("foo")),
                        new WaitUntilStopped()
                    )
                ),
                new Sequence(
                    new Action(() => Debug.Log("bar")),
                    new WaitUntilStopped()
                )
            )
        )
    );
    behaviorTree.Start();
  7. Golden rules for extending NPBehave nodes

    master

    When creating custom node types, you must follow these three rules to ensure tree stability and prevent state corruption:

    1. Every call to DoStop() must result in a call to Stopped(result): NPBehave needs to cancel running branches immediately. If you have child nodes, call Stop() on them; they will call ChildStopped() on your node, and then you must finally call Stopped().
    2. Stopped() is the last call you do: Never modify state or call other methods after calling Stopped(). Calling Stopped() immediately triggers tree traversal on other nodes, which can corrupt your node's state if you continue execution.
    3. Clean up observers: Every registered clock or blackboard observer must be removed. Typically, you unregister callbacks immediately before calling Stopped().
  8. Use Blackboard alternatives for performance

    master

    If you do not need event-driven observation (i.e., you aren't using BlackboardCondition or BlackboardQuery with non-NONE stop rules), you can avoid the blackboard entirely and use plain member variables in your MonoBehaviour. This is often faster and cleaner.

    If you want to use stopsOnChange logic without a Blackboard, you have two options:

    1. Use a Condition decorator: This checks the condition frequently (polling) rather than being event-driven. It is simpler but less performant for heavy queries.
    2. Build custom event-driven Decorators: Extend from ObservingDecorator and override isConditionMet(), StartObserving(), and StopObserving().
  9. Create a basic 'Hello World' Behavior Tree

    master
    You can define a simple behavior tree by instantiating a Root node and passing it a child node (such as an Action). Note that by default, when the last node in a tree finishes, the Root will restart the entire tree. To prevent this, use a WaitUntilStopped node at the end of your sequence.
  10. Create a basic Behavior Tree with Root and Action

    master

    A minimal behavior tree can be created by instantiating a Root node and passing it an Action node. Note that by default, the Root node will restart the entire tree once traversal reaches the end. To prevent continuous looping, use a WaitUntilStopped node at the end of your sequence.

    using NPBehave;
    
    public class HelloWorld : MonoBehaviour
    {
        private Root behaviorTree;
    
        void Start()
        {
            // Basic tree that loops indefinitely
            behaviorTree = new Root(
                new Action(() => Debug.Log("Hello World!"))
            );
            behaviorTree.Start();
        }
    }
  11. Implement Action nodes for tasks

    master

    Action nodes perform the actual work in the tree. There are several ways to define them depending on the complexity of the task:

    • Immediate Success: Action(System.Action action) executes a delegate and always returns success immediately.
    • Single Frame: Action(System.Func<bool> singleFrameFunc) returns true for success and false for failure.
    • Multi-frame (Result-based): Action(Func<bool, Result> multiframeFunc) handles tasks spanning multiple frames using Result:
      • Result.BLOCKED: Task is not ready yet.
      • Result.PROGRESS: Task is currently in progress.
      • Result.SUCCESS or Result.FAILED: Task completed.
    • Multi-frame (Request-based): Action(Func<Request, Result> multiframeFunc2) provides a Request object to manage state:
      • Request.START: First execution or last Result.BLOCKED state.
      • Request.UPDATE: Subsequent updates after a Result.PROGRESS return.
      • Request.CANCEL: Signal to cancel the operation and return success or Result.FAILED.