Best Practices
Tree Structure
Section titled “Tree Structure”Keep Trees Shallow
Section titled “Keep Trees Shallow”// Good - flat structure.selector('Main') .sequence('Combat') .sequence('Patrol') .sequence('Idle').end()
// Avoid - deep nesting.selector('Main') .selector('Level1') .selector('Level2') .selector('Level3') // ...Use Subtrees
Section titled “Use Subtrees”Break complex behaviors into reusable subtrees:
// Define reusable behaviorsconst combatBehavior = createCombatTree();const patrolBehavior = createPatrolTree();
// Compose main AIconst enemyAI = BehaviorTreeBuilder.create('EnemyAI') .selector('Main') .subtree(combatBehavior) .subtree(patrolBehavior) .end() .build();Blackboard Design
Section titled “Blackboard Design”Use Clear Naming
Section titled “Use Clear Naming”// Good.defineBlackboardVariable('targetEntity', null).defineBlackboardVariable('lastKnownPosition', null).defineBlackboardVariable('alertLevel', 0)
// Avoid.defineBlackboardVariable('t', null).defineBlackboardVariable('pos', null).defineBlackboardVariable('a', 0)Group Related Variables
Section titled “Group Related Variables”// Combat-relatedcombatTarget: EntitycombatRange: numberattackCooldown: number
// Movement-relatedmoveTarget: Vector2moveSpeed: numberpathNodes: Vector2[]Action Design
Section titled “Action Design”Single Responsibility
Section titled “Single Responsibility”// Good - focused actionsclass MoveToTarget implements INodeExecutor { }class AttackTarget implements INodeExecutor { }class PlayAnimation implements INodeExecutor { }
// Avoid - do-everything actionsclass CombatAction implements INodeExecutor { // Moves, attacks, plays animation, etc.}Stateless Executors
Section titled “Stateless Executors”// Good - use context for stateclass WaitAction implements INodeExecutor { execute(context: NodeExecutionContext): TaskStatus { const elapsed = context.runtime.getNodeState(context.node.id, 'elapsed') ?? 0; // ... }}
// Avoid - instance stateclass WaitAction implements INodeExecutor { private elapsed = 0; // Don't do this!}Debugging Tips
Section titled “Debugging Tips”Add Log Nodes
Section titled “Add Log Nodes”.sequence('AttackSequence') .log('Starting attack sequence', 'Debug') .action('findTarget') .log('Target found', 'Debug') .action('attack') .log('Attack complete', 'Debug').end()Use Meaningful Node Names
Section titled “Use Meaningful Node Names”// Good.sequence('ApproachAndAttackEnemy').condition('IsEnemyInRange').action('PerformMeleeAttack')
// Avoid.sequence('Seq1').condition('Cond1').action('Action1')Performance Tips
Section titled “Performance Tips”- Reduce tick rate for distant entities
- Use conditions early to fail fast
- Cache expensive calculations in blackboard
- Limit subtree depth to reduce traversal cost
- Profile your trees in real gameplay
Common Patterns
Section titled “Common Patterns”Guard Pattern
Section titled “Guard Pattern”.sequence('GuardedAction') .condition('canPerformAction') // Guard condition .action('performAction') // Actual action.end()Cooldown Pattern
Section titled “Cooldown Pattern”.sequence('CooldownAttack') .condition('isCooldownReady') .action('attack') .action('startCooldown').end()Memory Pattern
Section titled “Memory Pattern”.selector('RememberAndAct') .sequence('UseMemory') .condition('hasLastKnownPosition') .action('moveToLastKnownPosition') .end() .action('search').end()