### Behavior Tree JSON Structure Example Source: https://context7.com/magicsea/behavior3go/llms.txt Provides an example of a behavior tree structure defined in JSON format. This illustrates how nodes (composites and actions) are organized, including their names, titles, children, and properties, which are used to configure and build the behavior tree at runtime. ```JSON { "id": "tree-1", "title": "Combat AI", "root": "node-1", "nodes": { "node-1": { "name": "Priority", "title": "Main Selector", "children": ["node-2", "node-3"] }, "node-2": { "name": "Sequence", "title": "Attack Sequence", "children": ["node-4", "node-5"] }, "node-3": { "name": "Patrol", "title": "Patrol Action", "properties": {"radius": 10} }, "node-4": { "name": "FindEnemy", "title": "Find Enemy" }, "node-5": { "name": "Attack", "title": "Attack Enemy", "properties": {"damage": 10} } } } ``` -------------------------------- ### Behavior3Go Status Constants and Node Categories Source: https://context7.com/magicsea/behavior3go/llms.txt Defines the core return statuses (SUCCESS, FAILURE, RUNNING, ERROR) and node categories (COMPOSITE, DECORATOR, ACTION, CONDITION) used in behavior trees. This Go code demonstrates their usage and provides an example of a custom condition node. ```go package main import ( b3 "github.com/magicsea/behavior3go" ) func demonstrateConstants() { // Return statuses _ = b3.SUCCESS // 1: Node completed successfully _ = b3.FAILURE // 2: Node failed _ = b3.RUNNING // 3: Node still executing (will be ticked again) _ = b3.ERROR // 4: Node encountered an error // Node categories (set automatically by base classes) _ = b3.COMPOSITE // "composite": Sequence, Priority, etc. _ = b3.DECORATOR // "decorator": Inverter, Repeater, etc. _ = b3.ACTION // "action": Custom actions, Wait, Log, etc. _ = b3.CONDITION // "condition": Boolean check nodes // Version _ = b3.VERSION // "0.2.0" } // Custom condition node example type IsEnemyInRange struct { Condition range float64 } func (this *IsEnemyInRange) Initialize(setting *BTNodeCfg) { this.Condition.Initialize(setting) this.range = setting.GetProperty("range") } func (this *IsEnemyInRange) OnTick(tick *Tick) b3.Status { agent := tick.GetTarget().(*GameAgent) enemy := tick.Blackboard.GetMem("currentEnemy") if enemy == nil { return b3.FAILURE } distance := agent.DistanceTo(enemy.(*Enemy).Position) if distance <= this.range { return b3.SUCCESS } return b3.FAILURE } ``` -------------------------------- ### Implement Modular Subtrees in Go Source: https://context7.com/magicsea/behavior3go/llms.txt Shows how to configure a subtree loading function and register behavior trees from a project file. This allows for complex behaviors to be broken down into reusable modules. ```go var treeRegistry = sync.Map{} func init() { SetSubTreeLoadFunc(func(treeID string) *BehaviorTree { if tree, ok := treeRegistry.Load(treeID); ok { return tree.(*BehaviorTree) } return nil }) } func main() { projectConfig, ok := LoadRawProjectCfg("game_ai.b3") maps := b3.NewRegisterStructMaps() maps.Register("Attack", new(AttackAction)) for _, treeCfg := range projectConfig.Data.Trees { tree := CreateBevTreeFromConfig(&treeCfg, maps) treeRegistry.Store(treeCfg.ID, tree) } board := NewBlackboard() agent := &GameAgent{Name: "Enemy1"} status := mainTree.Tick(agent, board) } ``` -------------------------------- ### Registering and Initializing Custom Behavior Tree Nodes Source: https://context7.com/magicsea/behavior3go/llms.txt Demonstrates defining custom Action and Condition structs, implementing the Initialize and OnTick methods, and registering them with the RegisterStructMaps factory to enable loading from JSON configurations. ```go package main import ( b3 "github.com/magicsea/behavior3go" . "github.com/magicsea/behavior3go/config" . "github.com/magicsea/behavior3go/core" . "github.com/magicsea/behavior3go/loader" ) type MoveToTarget struct { Action speed float64 } func (this *MoveToTarget) Initialize(s *BTNodeCfg) { this.Action.Initialize(s) this.speed = s.GetProperty("speed") } func (this *MoveToTarget) OnTick(tick *Tick) b3.Status { return b3.SUCCESS } type IsHealthLow struct { Condition threshold float64 } func (this *IsHealthLow) Initialize(s *BTNodeCfg) { this.Condition.Initialize(s) this.threshold = s.GetProperty("threshold") } func (this *IsHealthLow) OnTick(tick *Tick) b3.Status { agent := tick.GetTarget().(*GameAgent) if agent.Health < this.threshold { return b3.SUCCESS } return b3.FAILURE } func main() { customNodes := b3.NewRegisterStructMaps() customNodes.Register("MoveToTarget", new(MoveToTarget)) customNodes.Register("IsHealthLow", new(IsHealthLow)) treeCfg, _ := LoadTreeCfg("ai_tree.json") tree := CreateBevTreeFromConfig(treeCfg, customNodes) board := NewBlackboard() tree.Tick(agent, board) } ``` -------------------------------- ### Blackboard State Management Source: https://context7.com/magicsea/behavior3go/llms.txt Demonstrates how to use the Blackboard for global, per-tree, and per-node scoped state management, including type-safe getters. ```APIDOC ## Using the Blackboard for State Management The Blackboard provides three-level scoped storage: global, per-tree, and per-node. This enables state sharing between nodes while maintaining proper isolation for concurrent entity control. ### Example Action: FindEnemyAction This action demonstrates setting and getting values from the Blackboard across different scopes. #### Scopes: - **Global**: `tick.Blackboard.SetMem("globalGameState", "active")` - Shared across all trees. - **Per-Tree**: `tick.Blackboard.Set("treeCounter", 1, treeID, "")` - Shared within the current tree. - **Per-Node**: `tick.Blackboard.Set("lastSearchTime", time.Now().Unix(), treeID, nodeID)` - Isolated to the specific node. ### Type-Safe Blackboard Getters Demonstrates various type-safe getter methods for retrieving data from the Blackboard. ```go package main import ( "fmt" b3 "github.com/magicsea/behavior3go" . "github.com/magicsea/behavior3go/core" "time" ) // Assume GameAgent and other necessary types are defined elsewhere type GameAgent struct { Name string // ... other fields } func (agent *GameAgent) FindNearestEnemy() *GameAgent { // Placeholder for enemy finding logic return nil } // Example action using blackboard storage type FindEnemyAction struct { Action } func (this *FindEnemyAction) OnTick(tick *Tick) b3.Status { agent := tick.GetTarget().(*GameAgent) // Global scope: shared across all trees tick.Blackboard.SetMem("globalGameState", "active") state := tick.Blackboard.GetMem("globalGameState").(string) _ = state // Use state variable // Per-tree scope: shared within current tree treeID := tick.GetTree().GetID() tick.Blackboard.Set("treeCounter", 1, treeID, "") counter := tick.Blackboard.GetInt("treeCounter", treeID, "") _ = counter // Use counter variable // Per-node scope: isolated to this specific node nodeID := this.GetID() tick.Blackboard.Set("lastSearchTime", time.Now().Unix(), treeID, nodeID) lastTime := tick.Blackboard.GetInt64("lastSearchTime", treeID, nodeID) _ = lastTime // Use lastTime variable // Find nearest enemy enemy := agent.FindNearestEnemy() if enemy != nil { tick.Blackboard.SetMem("currentEnemy", enemy) return b3.SUCCESS } return b3.FAILURE } // Type-safe blackboard getters func demonstrateBlackboardGetters(board *Blackboard, treeID, nodeID string) { // Various type-safe getters intVal := board.GetInt("counter", treeID, nodeID) int64Val := board.GetInt64("timestamp", treeID, nodeID) uint64Val := board.GetUInt64("entityID", treeID, nodeID) float64Val := board.GetFloat64("speed", treeID, nodeID) boolVal := board.GetBool("isActive", treeID, nodeID) fmt.Printf("Values: %d, %d, %d, %f, %t\n", intVal, int64Val, uint64Val, float64Val, boolVal) } ``` ``` -------------------------------- ### Load Project with Multiple Trees in Go Source: https://context7.com/magicsea/behavior3go/llms.txt Loads a project configuration containing multiple behavior trees from a JSON file using `LoadProjectCfg`. It demonstrates registering custom nodes and loading individual trees into a map for use with different AI agents. ```go package main import ( "fmt" b3 "github.com/magicsea/behavior3go" . "github.com/magicsea/behavior3go/config" . "github.com/magicsea/behavior3go/core" . "github.com/magicsea/behavior3go/loader" ) func main() { // Load project configuration with multiple trees projectConfig, ok := LoadProjectCfg("project.json") if !ok { fmt.Println("Failed to load project configuration") return } // Register custom nodes maps := b3.NewRegisterStructMaps() maps.Register("Attack", new(AttackAction)) maps.Register("Patrol", new(PatrolAction)) // Load all trees from project trees := make(map[string]*BehaviorTree) for _, treeCfg := range projectConfig.Trees { tree := CreateBevTreeFromConfig(&treeCfg, maps) trees[treeCfg.ID] = tree fmt.Printf("Loaded tree: %s\n", treeCfg.Title) } // Use specific tree for AI agent mainTree := trees["main-behavior-tree-id"] board := NewBlackboard() // Game loop for { mainTree.Tick(agent, board) } } ``` -------------------------------- ### Go Custom Node Lifecycle Implementation Source: https://context7.com/magicsea/behavior3go/llms.txt Demonstrates implementing lifecycle methods (Initialize, OnOpen, OnTick, OnClose, OnEnter, OnExit) for a custom ChaseAction node in Go. This allows for custom initialization, state management during execution, and cleanup. It uses a blackboard for state persistence and interacts with game agents and enemies. ```Go package main import ( b3 "github.com/magicsea/behavior3go" . "github.com/magicsea/behavior3go/config" . "github.com/magicsea/behavior3go/core" "fmt" "time" ) // Action with full lifecycle implementation type ChaseAction struct { Action speed float64 maxTime int64 } func (this *ChaseAction) Initialize(setting *BTNodeCfg) { this.Action.Initialize(setting) this.speed = setting.GetProperty("speed") this.maxTime = setting.GetPropertyAsInt64("maxTime") } // OnOpen is called once when node starts (not already running) func (this *ChaseAction) OnOpen(tick *Tick) { // Initialize chase state tick.Blackboard.Set("chaseStartTime", time.Now().UnixMilli(), tick.GetTree().GetID(), this.GetID()) fmt.Println("Chase started") } // OnTick is called every tick func (this *ChaseAction) OnTick(tick *Tick) b3.Status { treeID := tick.GetTree().GetID() nodeID := this.GetID() // Check timeout startTime := tick.Blackboard.GetInt64("chaseStartTime", treeID, nodeID) if time.Now().UnixMilli()-startTime > this.maxTime { return b3.FAILURE // Timeout } agent := tick.GetTarget().(*GameAgent) enemy := tick.Blackboard.GetMem("currentEnemy").(*Enemy) // Move towards enemy distance := agent.MoveTowards(enemy.Position, this.speed) if distance < 1.0 { return b3.SUCCESS // Reached target } return b3.RUNNING // Still chasing } // OnClose is called when node completes (SUCCESS, FAILURE, or ERROR) func (this *ChaseAction) OnClose(tick *Tick) { fmt.Println("Chase ended") } // OnEnter is called every tick before processing func (this *ChaseAction) OnEnter(tick *Tick) { // Called every tick } // OnExit is called every tick after processing func (this *ChaseAction) OnExit(tick *Tick) { // Called every tick } ``` -------------------------------- ### Manage State with Blackboard Scopes in Go Source: https://context7.com/magicsea/behavior3go/llms.txt Demonstrates how to store and retrieve data using three-level scoping (global, tree, and node) within a behavior tree action. It also illustrates the use of type-safe getters for various data types. ```go type FindEnemyAction struct { Action } func (this *FindEnemyAction) OnTick(tick *Tick) b3.Status { agent := tick.GetTarget().(*GameAgent) tick.Blackboard.SetMem("globalGameState", "active") treeID := tick.GetTree().GetID() tick.Blackboard.Set("treeCounter", 1, treeID, "") nodeID := this.GetID() tick.Blackboard.Set("lastSearchTime", time.Now().Unix(), treeID, nodeID) enemy := agent.FindNearestEnemy() if enemy != nil { tick.Blackboard.SetMem("currentEnemy", enemy) return b3.SUCCESS } return b3.FAILURE } func demonstrateBlackboardGetters(board *Blackboard, treeID, nodeID string) { intVal := board.GetInt("counter", treeID, nodeID) int64Val := board.GetInt64("timestamp", treeID, nodeID) uint64Val := board.GetUInt64("entityID", treeID, nodeID) float64Val := board.GetFloat64("speed", treeID, nodeID) boolVal := board.GetBool("isActive", treeID, nodeID) fmt.Printf("Values: %d, %d, %d, %f, %t\n", intVal, int64Val, uint64Val, float64Val, boolVal) } ``` -------------------------------- ### Load Behavior Tree from JSON in Go Source: https://context7.com/magicsea/behavior3go/llms.txt Loads a behavior tree configuration from a JSON file using `LoadTreeCfg` and creates a behavior tree instance with `CreateBevTreeFromConfig`. It supports custom node registration and execution with a blackboard. ```go package main import ( "fmt" b3 "github.com/magicsea/behavior3go" . "github.com/magicsea/behavior3go/config" . "github.com/magicsea/behavior3go/core" . "github.com/magicsea/behavior3go/loader" ) func main() { // Load tree configuration from JSON file treeConfig, ok := LoadTreeCfg("tree.json") if !ok { fmt.Println("Failed to load tree configuration") return } // Register custom nodes (optional) maps := b3.NewRegisterStructMaps() maps.Register("MyCustomAction", new(MyCustomAction)) // Create behavior tree from configuration tree := CreateBevTreeFromConfig(treeConfig, maps) tree.Print() // Debug: print tree structure // Create blackboard for state storage board := NewBlackboard() // Execute tree in game loop for i := 0; i < 100; i++ { status := tree.Tick(gameAgent, board) fmt.Printf("Tick %d: Status = %d\n", i, status) } } ``` -------------------------------- ### Subtree Implementation Source: https://context7.com/magicsea/behavior3go/llms.txt Explains how to use subtrees for modular behavior design and configures subtree loading using `SetSubTreeLoadFunc`. ```APIDOC ## Using Subtrees for Modular Behavior Subtrees allow breaking complex behaviors into reusable modules. The `SetSubTreeLoadFunc` function configures how subtrees are resolved at runtime. ### Subtree Loading Configuration - **`SetSubTreeLoadFunc`**: This function is used to define how the BehaviorTree system resolves and loads subtrees by their ID. - **`treeRegistry`**: A global `sync.Map` is used to store loaded `BehaviorTree` instances, keyed by their IDs, allowing `SetSubTreeLoadFunc` to retrieve them. ### Loading and Executing a Behavior Tree with Subtrees This example demonstrates loading a project configuration, registering custom node types, creating a main behavior tree, and executing it. ```go package main import ( "fmt" "sync" b3 "github.com/magicsea/behavior3go" . "github.com/magicsea/behavior3go/config" . "github.com/magicsea/behavior3go/core" . "github.com/magicsea/behavior3go/loader" ) // Assume AttackAction, PatrolAction, GameAgent, and other necessary types are defined elsewhere type AttackAction struct { Action } type PatrolAction struct { Action } type GameAgent struct { Name string // ... other fields } // Global tree registry var treeRegistry = sync.Map{} func init() { // Configure subtree loading function SetSubTreeLoadFunc(func(treeID string) *BehaviorTree { if tree, ok := treeRegistry.Load(treeID); ok { return tree.(*BehaviorTree) } fmt.Printf("Warning: Subtree %s not found\n", treeID) return nil }) } func main() { // Load raw project file (.b3 format from desktop editor) // Ensure 'game_ai.b3' is in the correct path or provide the full path. projectConfig, ok := LoadRawProjectCfg("game_ai.b3") if !ok { fmt.Println("Failed to load project") return } maps := b3.NewRegisterStructMaps() maps.Register("Attack", new(AttackAction)) maps.Register("Patrol", new(PatrolAction)) // Load all trees and register them for subtree references var mainTree *BehaviorTree for _, treeCfg := range projectConfig.Data.Trees { tree := CreateBevTreeFromConfig(&treeCfg, maps) treeRegistry.Store(treeCfg.ID, tree) if mainTree == nil { mainTree = tree } } // Execute with subtree support board := NewBlackboard() agent := &GameAgent{Name: "PlayerAgent"} // Example agent fmt.Println("Starting tree execution...") for i := 0; i < 60; i++ { status := mainTree.Tick(agent, board) if status != b3.RUNNING { fmt.Printf("Tree completed with status: %d\n", status) break } // Optional: Add a small delay or other logic here if needed } fmt.Println("Finished tree execution.") } ``` ``` -------------------------------- ### Create Custom Action Node in Go Source: https://context7.com/magicsea/behavior3go/llms.txt Defines a custom action node 'AttackAction' by extending the base `Action` struct and implementing the `OnTick` method. It demonstrates how to read properties from configuration, access game state via the Tick instance, and interact with the blackboard. ```go package main import ( b3 "github.com/magicsea/behavior3go" . "github.com/magicsea/behavior3go/config" . "github.com/magicsea/behavior3go/core" ) // Custom action node for attacking type AttackAction struct { Action damage int } // Initialize reads node properties from JSON configuration func (this *AttackAction) Initialize(setting *BTNodeCfg) { this.Action.Initialize(setting) this.damage = setting.GetPropertyAsInt("damage") } // OnTick executes the action logic func (this *AttackAction) OnTick(tick *Tick) b3.Status { // Get target object from tick agent := tick.GetTarget().(*GameAgent) // Get enemy from blackboard enemy := tick.Blackboard.GetMem("currentEnemy") if enemy == nil { return b3.FAILURE } // Perform attack enemy.(*Enemy).TakeDamage(this.damage) if enemy.(*Enemy).IsAlive() { return b3.RUNNING } return b3.SUCCESS } // Register the custom node func registerCustomNodes() *b3.RegisterStructMaps { maps := b3.NewRegisterStructMaps() maps.Register("AttackAction", new(AttackAction)) return maps } ``` -------------------------------- ### Behavior3Go Built-in Action Nodes Source: https://context7.com/magicsea/behavior3go/llms.txt Action nodes perform specific tasks within a behavior tree. This library includes nodes like Wait, Log, Succeeder, Failer, Runner, and Error for testing and common operations. Configuration is done via JSON objects. ```go // Wait: Waits for specified milliseconds, returns RUNNING until complete // JSON: {"name": "Wait", "properties": {"milliseconds": 1000}} // Log: Logs a message (for debugging), always returns SUCCESS // JSON: {"name": "Log", "properties": {"info": "Debug message"}} // Succeeder: Always returns SUCCESS // JSON: {"name": "Succeeder"} // Failer: Always returns FAILURE // JSON: {"name": "Failer"} // Runner: Always returns RUNNING (for testing) // JSON: {"name": "Runner"} // Error: Always returns ERROR // JSON: {"name": "Error"} // Complete example tree JSON: /* { "id": "debug-tree", "title": "Debug Test Tree", "root": "seq1", "nodes": { "seq1": { "name": "Sequence", "children": ["log1", "wait1", "log2"] }, "log1": { "name": "Log", "title": "Start Log", "properties": {"info": "Starting sequence"} }, "wait1": { "name": "Wait", "title": "Wait 1 second", "properties": {"milliseconds": 1000} }, "log2": { "name": "Log", "title": "End Log", "properties": {"info": "Sequence complete"} } } } */ ``` -------------------------------- ### Behavior Tree Composite Node Descriptions Source: https://context7.com/magicsea/behavior3go/llms.txt Describes the functionality of built-in composite nodes in behavior3go, including Sequence, Priority, and their memory-based variants. These nodes control the flow of execution among child nodes based on their return status (SUCCESS, FAILURE, RUNNING). ```Text // Sequence: Executes children in order until one returns FAILURE or RUNNING // Returns SUCCESS only if all children succeed // JSON: {"name": "Sequence", "children": ["node1", "node2", "node3"]} // Priority (Selector): Executes children until one returns SUCCESS or RUNNING // Returns FAILURE only if all children fail // JSON: {"name": "Priority", "children": ["node1", "node2", "node3"]} // MemSequence: Like Sequence but remembers last RUNNING child // On next tick, resumes from the RUNNING child instead of starting over // JSON: {"name": "MemSequence", "children": ["node1", "node2"]} // MemPriority: Like Priority but remembers last RUNNING child // JSON: {"name": "MemPriority", "children": ["node1", "node2"]} ``` -------------------------------- ### Behavior3Go Built-in Decorator Nodes Source: https://context7.com/magicsea/behavior3go/llms.txt Decorator nodes modify the behavior of their child nodes. This includes nodes like Inverter, Repeater, Limiter, and MaxTime, each offering unique control over execution flow. They are configured using JSON objects specifying the node name and its properties. ```go // Inverter: Inverts child result (SUCCESS <-> FAILURE) // JSON: {"name": "Inverter", "child": "node-id"} // Repeater: Repeats child execution up to maxLoop times // JSON: {"name": "Repeater", "child": "node-id", "properties": {"maxLoop": 3}} // RepeatUntilSuccess: Repeats child until it returns SUCCESS // JSON: {"name": "RepeatUntilSuccess", "child": "node-id", "properties": {"maxLoop": 10}} // RepeatUntilFailure: Repeats child until it returns FAILURE // JSON: {"name": "RepeatUntilFailure", "child": "node-id", "properties": {"maxLoop": 10}} // Limiter: Limits total number of times child can be activated // JSON: {"name": "Limiter", "child": "node-id", "properties": {"maxLoop": 5}} // MaxTime: Fails if child doesn't complete within time limit // JSON: {"name": "MaxTime", "child": "node-id", "properties": {"maxTime": 1000}} // Example tree with decorators: /* { "id": "tree-patrol", "title": "Patrol Behavior", "root": "root", "nodes": { "root": { "name": "Repeater", "title": "Repeat Patrol", "child": "patrol-seq", "properties": {"maxLoop": -1} }, "patrol-seq": { "name": "Sequence", "title": "Patrol Sequence", "children": ["move-to-point", "wait"] }, "move-to-point": { "name": "MoveToPoint", "title": "Move to Waypoint" }, "wait": { "name": "Wait", "title": "Wait at Point", "properties": {"milliseconds": 2000} } } } */ ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.