Use Shared Blackboards for swarm behavior
masterUnityContext.GetSharedBlackboard(name) to access shared blackboard instances from anywhere in your code.repository·master·Indexed 22 days ago
https://github.com/meniku/npbehaveA 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.
UnityContext.GetSharedBlackboard(name) to access shared blackboard instances from anywhere in your code.NPBehave uses four primary node types:
Sequence, Selector). Defines order and logic for child execution.Service nodes).Blackboards act as the AI's 'memory' and are implemented as dictionaries that can be observed for changes.
Root, but you can provide your own instance via the constructor (useful for Shared Blackboards).Service nodes to update blackboard values periodically.BlackboardCondition or BlackboardQuery to monitor the blackboard. When a monitored value changes, the tree can react immediately.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();NPBehave uses four primary node types:
Selector, Sequence).Service, BlackboardCondition).Action for simple tasks or subclass Task for complex logic.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.Blackboards act as the "memory" of your AI. They are essentially dictionaries that can be observed for changes.
Root node.Root constructor (useful for Shared Blackboards).Service nodes to update blackboard values.BlackboardCondition or BlackboardQuery to observe changes and trigger tree traversal.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();NPBehave folder into your Unity project. The package includes an Examples subfolder containing sample scenes for reference.When creating custom node types, you must follow these three rules to ensure tree stability and prevent state corruption:
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().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.Stopped().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:
Condition decorator: This checks the condition frequently (polling) rather than being event-driven. It is simpler but less performant for heavy queries.ObservingDecorator and override isConditionMet(), StartObserving(), and StopObserving().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.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();
}
}Action nodes perform the actual work in the tree. There are several ways to define them depending on the complexity of the task:
Action(System.Action action) executes a delegate and always returns success immediately.Action(System.Func<bool> singleFrameFunc) returns true for success and false for failure.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.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.