AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Tools Unity Behavior Designer

skill-tjboudreaux-cc-plugin-unity-gamedev-tools-unity-behavior-designer · by tjboudreaux

Behavior Designer patterns for AI behavior trees including task creation, shared variables, conditionals, and debugging.

No reviews yet
0 installs
21 views
0.0% view→install

Install

$ agentstack add skill-tjboudreaux-cc-plugin-unity-gamedev-tools-unity-behavior-designer

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-tjboudreaux-cc-plugin-unity-gamedev-tools-unity-behavior-designer)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
7mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Tools Unity Behavior Designer? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Behavior Designer

Overview

Behavior Designer is a behavior tree implementation for Unity AI. This skill covers patterns for creating robust AI behaviors for enemies, NPCs, and game entities.

When to Use

  • Enemy AI behavior
  • NPC decision making
  • Boss fight patterns
  • Companion AI
  • Any complex state-based AI

Core Concepts

Behavior Tree Structure

Selector (OR logic - first success wins)
├── Sequence (AND logic - all must succeed)
│   ├── Conditional (check condition)
│   └── Action (do something)
├── Sequence
│   ├── Conditional
│   └── Action
└── Action (fallback)

Custom Tasks

Action Task

using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

[TaskCategory("Combat")]
[TaskDescription("Attacks the current target")]
public class AttackTarget : Action
{
    [Tooltip("Reference to the ability system")]
    public SharedGameObject abilitySystemOwner;
    
    [Tooltip("The ability to activate")]
    public SharedString abilityTag;
    
    [Tooltip("Target to attack")]
    public SharedGameObject target;
    
    private IAbilitySystem _abilitySystem;
    private bool _abilityStarted;
    
    public override void OnStart()
    {
        _abilityStarted = false;
        
        if (abilitySystemOwner.Value == null)
        {
            Debug.LogError("AttackTarget: No ability system owner");
            return;
        }
        
        _abilitySystem = abilitySystemOwner.Value.GetComponent();
    }
    
    public override TaskStatus OnUpdate()
    {
        if (_abilitySystem == null || target.Value == null)
        {
            return TaskStatus.Failure;
        }
        
        if (!_abilityStarted)
        {
            if (!_abilitySystem.TryActivateAbilityByTag(abilityTag.Value))
            {
                return TaskStatus.Failure;
            }
            _abilityStarted = true;
        }
        
        // Check if ability is still running
        if (_abilitySystem.IsAbilityActive(abilityTag.Value))
        {
            return TaskStatus.Running;
        }
        
        return TaskStatus.Success;
    }
    
    public override void OnEnd()
    {
        // Cleanup if interrupted
        if (_abilityStarted && _abilitySystem != null)
        {
            _abilitySystem.CancelAbilityByTag(abilityTag.Value);
        }
    }
}

Conditional Task

[TaskCategory("Combat")]
[TaskDescription("Checks if target is within attack range")]
public class IsTargetInRange : Conditional
{
    public SharedGameObject target;
    public SharedFloat attackRange;
    public SharedTransform selfTransform;
    
    public override TaskStatus OnUpdate()
    {
        if (target.Value == null || selfTransform.Value == null)
        {
            return TaskStatus.Failure;
        }
        
        float distance = Vector3.Distance(
            selfTransform.Value.position,
            target.Value.transform.position
        );
        
        return distance 
{
    public static implicit operator SharedAbilityData(AbilityData value)
    {
        return new SharedAbilityData { Value = value };
    }
}

// Usage in task
public class UseAbility : Action
{
    public SharedAbilityData ability;
    
    public override TaskStatus OnUpdate()
    {
        if (ability.Value == null)
            return TaskStatus.Failure;
            
        // Use ability data
        return TaskStatus.Success;
    }
}

Global Variables

public class AIGlobalVariables : MonoBehaviour
{
    public static AIGlobalVariables Instance { get; private set; }
    
    private GlobalVariables _globalVariables;
    
    private void Awake()
    {
        Instance = this;
        _globalVariables = GlobalVariables.Instance;
    }
    
    public void SetPlayerReference(GameObject player)
    {
        _globalVariables.SetVariable("Player", (SharedGameObject)player);
    }
    
    public void SetCombatState(bool inCombat)
    {
        _globalVariables.SetVariable("GlobalCombatActive", (SharedBool)inCombat);
    }
    
    public GameObject GetPlayer()
    {
        var playerVar = _globalVariables.GetVariable("Player") as SharedGameObject;
        return playerVar?.Value;
    }
}

Common Patterns

Target Selection

[TaskCategory("Targeting")]
public class FindClosestEnemy : Action
{
    public SharedGameObject result;
    public SharedFloat searchRadius;
    public SharedLayerMask targetLayers;
    public SharedTransform selfTransform;
    
    private Collider[] _hitColliders = new Collider[20];
    
    public override TaskStatus OnUpdate()
    {
        if (selfTransform.Value == null)
            return TaskStatus.Failure;
        
        int hitCount = Physics.OverlapSphereNonAlloc(
            selfTransform.Value.position,
            searchRadius.Value,
            _hitColliders,
            targetLayers.Value
        );
        
        if (hitCount == 0)
        {
            result.Value = null;
            return TaskStatus.Failure;
        }
        
        float closestDistance = float.MaxValue;
        GameObject closest = null;
        
        for (int i = 0; i ();
        _transform = transform;
    }
    
    public override TaskStatus OnUpdate()
    {
        if (target.Value == null)
            return TaskStatus.Failure;
        
        if (_ai != null)
        {
            _ai.destination = target.Value.transform.position;
            
            if (_ai.reachedEndOfPath)
                return TaskStatus.Success;
                
            return TaskStatus.Running;
        }
        
        // Fallback simple movement
        Vector3 direction = (target.Value.transform.position - _transform.position).normalized;
        _transform.position += direction * moveSpeed.Value * Time.deltaTime;
        
        float distance = Vector3.Distance(
            _transform.position,
            target.Value.transform.position
        );
        
        return distance ();
        
        if (waypoints.Value == null || waypoints.Value.Count == 0)
        {
            return;
        }
        
        SetDestinationToCurrentWaypoint();
    }
    
    public override TaskStatus OnUpdate()
    {
        if (waypoints.Value == null || waypoints.Value.Count == 0)
            return TaskStatus.Failure;
        
        if (_ai == null)
            return TaskStatus.Failure;
        
        // Check if reached waypoint
        float distance = Vector3.Distance(
            transform.position,
            waypoints.Value[currentWaypointIndex.Value].position
        );
        
        if (distance  currentWaypointIndex.Value)
        {
            _ai.destination = waypoints.Value[currentWaypointIndex.Value].position;
            _ai.isStopped = false;
        }
    }
}

Combat State Machine

// Tree structure for combat AI
/*
Selector (Root)
├── Sequence [Flee when low health]
│   ├── IsHealthLow
│   └── FleeFromTarget
├── Sequence [Attack when in range]
│   ├── HasTarget
│   ├── IsTargetInRange
│   └── Selector [Choose attack]
│       ├── Sequence [Special attack if ready]
│       │   ├── IsSpecialReady
│       │   └── UseSpecialAttack
│       └── UseBasicAttack
├── Sequence [Chase target]
│   ├── HasTarget
│   └── MoveToTarget
└── Patrol [Default behavior]
*/

[TaskCategory("Combat")]
public class IsHealthLow : Conditional
{
    public SharedFloat currentHealth;
    public SharedFloat maxHealth;
    public SharedFloat lowHealthThreshold = 0.2f;
    
    public override TaskStatus OnUpdate()
    {
        float healthPercent = currentHealth.Value / maxHealth.Value;
        return healthPercent ();
        }
    }
    
    public override TaskStatus OnUpdate()
    {
        if (_asc == null)
            return TaskStatus.Failure;
        
        if (!_activated)
        {
            var spec = _asc.TryActivateAbilitiesByTag(
                GameplayTag.FromString(abilityTag.Value)
            );
            
            if (spec == null)
                return TaskStatus.Failure;
            
            _activated = true;
            
            if (!waitForCompletion.Value)
                return TaskStatus.Success;
        }
        
        // Wait for ability to complete
        if (_asc.HasActiveAbilityWithTag(GameplayTag.FromString(abilityTag.Value)))
        {
            return TaskStatus.Running;
        }
        
        return TaskStatus.Success;
    }
}

Debugging

Behavior Tree Debugging

public class BehaviorTreeDebugger : MonoBehaviour
{
    [SerializeField] private BehaviorTree _behaviorTree;
    [SerializeField] private bool _logTaskChanges = true;
    
    private void OnEnable()
    {
        if (_behaviorTree != null)
        {
            _behaviorTree.OnBehaviorStart += OnBehaviorStart;
            _behaviorTree.OnBehaviorRestart += OnBehaviorRestart;
            _behaviorTree.OnBehaviorEnd += OnBehaviorEnd;
        }
    }
    
    private void OnDisable()
    {
        if (_behaviorTree != null)
        {
            _behaviorTree.OnBehaviorStart -= OnBehaviorStart;
            _behaviorTree.OnBehaviorRestart -= OnBehaviorRestart;
            _behaviorTree.OnBehaviorEnd -= OnBehaviorEnd;
        }
    }
    
    private void OnBehaviorStart(Behavior behavior)
    {
        if (_logTaskChanges)
        {
            Debug.Log($"[BT] {name} Started");
        }
    }
    
    private void OnBehaviorRestart(Behavior behavior)
    {
        if (_logTaskChanges)
        {
            Debug.Log($"[BT] {name} Restarted");
        }
    }
    
    private void OnBehaviorEnd(Behavior behavior)
    {
        if (_logTaskChanges)
        {
            Debug.Log($"[BT] {name} Ended");
        }
    }
}

Best Practices

  1. Use shared variables - For data passing between tasks
  2. Keep tasks simple - Single responsibility
  3. Use conditional aborts - For responsive AI
  4. Cache component references - In OnStart
  5. Handle null gracefully - Return Failure on null
  6. Use task categories - Organize custom tasks
  7. Profile behavior trees - Can be expensive
  8. Use external trees - For reusable behaviors
  9. Document task descriptions - Use TaskDescription attribute
  10. Test edge cases - Target dies, interrupted, etc.

Troubleshooting

| Issue | Solution | |-------|----------| | Task never completes | Check for infinite Running state | | Variables not synced | Verify shared variable binding | | Abort not working | Check abort type setting | | Performance issues | Reduce tree complexity, cache refs | | Null reference | Add null checks in OnUpdate | | Task not found | Check TaskCategory attribute |

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.