Access the target in Tick functions
masterTick, the target typically refers to the owner of the AI. The owner should have access to the blackboard members to facilitate communication between the tree and the game entity.repository·master·Indexed 43 days ago
https://github.com/magicsea/behavior3goA Go implementation of the Behavior3 behavior tree library. It features a stateless tree structure where all node states are stored in a blackboard, allowing multiple agents to share a single tree. It is compatible with the official Behavior3 online editor and supports loading trees from exported project files, raw project files, and JSON.
Tick, the target typically refers to the owner of the AI. The owner should have access to the blackboard members to facilitate communication between the tree and the game entity.Unlike many behavior tree implementations where each agent holds its own copy of the tree structure containing state, behavior3go uses a stateless tree structure.
This design allows multiple agents to share the same tree structure while maintaining individual states via their own blackboards.
The library provides several ways to load behavior tree logic depending on your source data:
For example, if you have a bin/b3.json file from an IO-style game, you can import it into the editor via Project -> Import -> Tree as json to reconstruct the project.
project -> new tree menu option. You can rename the tree in the left-hand directory by selecting the new tree's Root node and modifying its Title property.To run the official online editor locally for editing logic nodes, follow these steps:
https://github.com/magicsea/behavior3editornpm install -g bowercd behavior3editor
npm install
bower installnpm install --global gulpgulp servehttp://127.0.0.1:8000Deployment: To deploy your own web version, simply move the generated build directory to your web server (e.g., Tomcat or IIS).
In certain behavior tree configurations involving memory nodes (specifically memseq nodes), a bug may prevent the first node in a memory node chain from executing its onclose method.
Symptoms:
runningChild state is not cleared/reset in the memseq node.Wait0-2 to Wait1-2 instead of entering the next logical job like job 1-0).Debugging:
To reproduce and verify this issue, enable print logging for Wait nodes to track job transitions and timing.
The memsubtree example demonstrates how to resolve issues where multiple subtrees are reused, which can lead to inconsistent or chaotic running states.
To fix this, the standard subtree implementation is modified to consolidate multiple tick calls into a single primary tick. This ensures that state management remains consistent even when the same subtree structure is utilized across different parts of the behavior tree.
Use CreateUUID() to generate a unique identifier for behavior trees and nodes. The implementation generates a 32-character MD5 string derived from random bytes, intended to serve as a unique ID (referencing RFC 4122 concepts).
import "github.com/magicsea/magicsea/behavior3go"
// Generate a unique ID for a node or tree
uid := behavior3go.CreateUUID()The RegisterStructMaps type allows you to map string names to Go types, enabling dynamic instantiation of structs by name. This is useful for deserializing behavior tree definitions where node types are identified by strings.
NewRegisterStructMaps().Register(name string, c interface{}). Note that c should be a pointer to the struct, as the registry uses reflect.TypeOf(c).Elem() to store the underlying type.CheckElem(name string).New(name string). This returns an interface{} which you can type-assert to your desired struct pointer.import "github.com/magicsea/magicsea/behavior3go"
type MyNode struct {
Value int
}
// 1. Initialize
rsm := behavior3go.NewRegisterStructMaps()
// 2. Register (pass a pointer to the struct)
rsm.Register("MyNode", &MyNode{})
// 3. Check
if rsm.CheckElem("MyNode") {
// 4. Instantiate instance, err := rsm.New("MyNode")
if err == nil {
// Type assert to the pointer type
node := instance.(*MyNode)
node.Value = 10
}
}Methods available on the *RegisterStructMaps type:
| Method | Signature | Description |
|---|---|---|
New | func (rsm *RegisterStructMaps) New(name string) (interface{}, error) | Returns a new instance of the registered type as an interface{}. Returns an error if the name is not found. |
Register | func (rsm *RegisterStructMaps) Register(name string, c interface{}) | Registers a type under the provided name. The type is extracted from reflect.TypeOf(c).Elem(). |
CheckElem | func (rsm *RegisterStructMaps) CheckElem(name string) bool | Returns true if a type is registered under the given name, false otherwise. |
NewRegisterStructMaps | func NewRegisterStructMaps() *RegisterStructMaps | Constructor that initializes an empty RegisterStructMaps instance. |
The library uses string constants to categorize different types of nodes within a behavior tree. These categories help define the structural role of a node:
COMPOSITE: Nodes that manage multiple children (e.g., Selector, Sequence).DECORATOR: Nodes that wrap a single child to modify its behavior.ACTION: Nodes that perform a specific task or effect.CONDITION: Nodes that check a specific state or predicate.const (
COMPOSITE = "composite"
DECORATOR = "decorator"
ACTION = "action"
CONDITION = "condition"
)