πWelcome to AI Behavior Architect
AI Behavior Architect is a visual scripting tool and runtime framework designed from the ground up for Unity's Data-Oriented Technology Stack (DOTS) and the Entity Component System (ECS).
π‘ The DOTS Challenge
While there are many Behavior Tree solutions available for Unity, most are built around object-orientedMonoBehaviourworkflows. Attempting to force those object-oriented trees into an ECS environment often results in heavy memory allocations (Garbage Collection), thread-safety issues, and lost performance.
AI Behavior Architect solves this by bridging the gap between designer-friendly visual scripting and hardcore developer performance.
π― Who is this for?
- π¨ For Designers: A clean, intuitive node-based editor right inside Unity. Design AI logic, manage sub-trees, configure blackboards, and watch visual debugging in real-timeβall without touching code.
- π» For Programmers: A robust backend that takes that visual graph and uses a custom code-generator to automatically write highly optimized, Burst-compiled
ISystemandIJobChunkC# scripts.
You get the rapid iteration of visual scripting with the extreme runtime performance of pure DOTS code.
β¨ Core Features at a Glance
- 100% DOTS Native: Executes entirely inside Burst-compiled jobs.
- Zero-GC Allocations: No runtime garbage collection. Memory is handled strictly via unmanaged buffers and Blob Assets.
- Auto-Code Generation: Click "Compile" and watch the framework generate optimized backend systems for you automatically.
- Advanced Spatial Queries: Built-in spatial hashing nodes to easily find entities (nearest, random, etc.) without writing complex math.
- Parallel Execution: Native support for evaluating multiple nodes simultaneously.
π§ Core Concepts & Architecture
To get the most out of AI Behavior Architect, it is helpful to understand how it operates under the hood. The framework relies on three core DOTS pillars to achieve maximum performance:
βοΈ 1. Automated Code Generation
Behavior Trees are inherently polymorphic (e.g., a generic "Node" can be an Action, a Sequence, or a Condition). Standard ECS does not support polymorphism well, as everything must be strictly typed data.
To bypass this, AI Behavior Architect uses a Code Generator. When you save your graph, the tool reads your nodes and automatically generates partial struct systems that process your specific logic. You never have to manually register nodes into systems; the architect wires it all up for you.
π¦ 2. Blob Assets (BlobAssetReference)
When you bake your sub-scene, the framework reads your visual Graph Asset and converts the tree's layout into a BtBlob (a Blob Asset).
π Why Blob Assets?
Blob Assets guarantee that the entire structure of your behavior tree is stored in a single, contiguous block of unmanaged memory. This makes navigating the tree incredibly cache-friendly and allows it to be safely read inside Burst-compiled multithreaded jobs.
πΎ 3. Unmanaged State Buffers
Because a Behavior Tree needs to remember what it was doing last frame (e.g., waiting for a timer, or running a custom action), it requires memory. AI Behavior Architect allocates unmanaged DynamicBuffer<T> components directly onto your Agent Entity during the baking process:
BtNodeState: Stores internal timers and loop counters (e.g., for Wait or Repeater nodes).BtActionState: Tracks which custom actions are currently running and their results.BtBlackboardEntry: A 16-byte unmanaged wrapper that stores your Blackboard variables (floats,ints,bools, andEntityreferences) directly on the memory chunk.
Because all state data is strictly unmanaged and tightly packed on the Entity, the runtime evaluator never triggers the Garbage Collector.
π₯Installation & Requirements
Before importing AI Behavior Architect, please ensure your Unity project meets the necessary minimum requirements for DOTS development.
π System Requirements
- Unity Version: 2022.2 or higher
- Entities Package (
com.unity.entities): Version 1.4.5 or newer - Burst Compiler (
com.unity.burst): Version 1.8.27 or newer
π‘ Render Pipeline Compatibility
The core AI framework is 100% render-pipeline agnostic. Because it relies purely on ECS and the Job System, you can use it in the Built-in Render Pipeline, URP, or HDRP. (Note: The included Demo Scene uses URP materials, but the AI logic functions universally).
π¦ Installation Steps
- Open your Unity Project.
- Ensure you have installed the Entities package via the Unity Package Manager.
- Import the AI Behavior Architect
.unitypackageinto your project. - Unity will automatically resolve the remaining dependencies.
βοΈ Project Setup
Because AI Behavior Architect automatically writes C# code for you, you need to tell it where to save those generated scripts.
Configuring the Generator
- In the top menu bar, navigate to Edit > Project Settings.
- Select the AI Behavior Architect tab on the left-hand menu.
- Locate the Generated Directory field.
- Choose or create a folder where the framework will safely output the auto-generated DOTS systems.
- Default path:
Assets/SnivelerCode/AiBehavior/Generated
- Default path:
- Ensure Auto Compile On Save is checked. This ensures your code backend updates seamlessly whenever you modify your visual graphs.
β οΈ Important: Do not manually edit the C# files inside your Generated Directory! They will be overwritten the next time you compile your Behavior Tree.
π Quick Start: Your First AI Agent
Let's build your very first DOTS-driven AI agent from scratch. We will create a simple behavior that tells an entity to simply wait in place.
Step 1: Create the Behavior Graph
- Right-click anywhere in your Project window.
- Go to
Create > Entities > Sniveler Code > AI Graphand name itMyFirstAgent. - Open the editor by going to
Window/Sniveler Code/AI Behavior Architect. - Drag and drop your new
MyFirstAgentasset into theActive Treeslot in the editor toolbar.
Step 2: Add Logic Nodes
- Right-click in the empty grid area (or press
Spacebar) to open the Node Search Window. - Search for and add a
Sequencenode. - Right-click again and add an
Action Waitnode. - Connect the bottom output port of the
Sequencenode to the top input port of theWaitnode.
Step 3: Compile the Tree
- On the left-hand Settings Panel, click the Compile button.
- Check your Unity Console. You should see a message indicating the graph compiled successfully and DOTS systems were generated.
Step 4: Setup the Agent Entity
- Open a Sub-Scene in your Unity project (required for DOTS baking).
- Create a new empty
GameObjectand name it AI Agent. - Add the
BtAgentAuthoringcomponent to thisGameObject. - Drag your compiled
MyFirstAgentGraph Asset into the Tree field of the authoring component. - (Optional) Add the
BtSettingsAuthoringcomponent if you wish to configure the maximum evaluation iterations per frame.
Step 5: Bake and Play!
- Ensure your
Sub-Sceneis closed/baked. - Press Play in the Unity Editor.
- While in Play Mode, select your AI Agent Entity using the
Entity Hierarchywindow. - Look at your Behavior Editor windowβyou will see the nodes light up Yellow, indicating that your unmanaged DOTS tree is successfully executing the Wait action!
π¨ Interface Overview
The AI Behavior Architect Editor is your central hub for visually authoring AI logic. You can open it at any time via Window/Sniveler Code/AI Behavior Architect.
The editor is divided into three primary zones:
π 1. The Top Toolbar
Located at the very top of the window, the toolbar manages which behavior tree you are currently editing.
- Active Tree: Drag and drop a
GraphAssethere to open it. - Breadcrumbs / History: When you dive into Sub-Trees (nested graphs), this area displays your navigation history (e.g.,
Hierarchy -> PatrolBehavior -> OpenDoor). Click on any previous name to jump back up the hierarchy.
π 2. The Settings Panel (Left)
This side-panel contains the contextual settings for your graph and nodes.
- Node Tab: Whenever you select a node on the canvas, this tab acts as an Inspector. It allows you to configure parameters, dropdowns, and data specific to that node.
- Variables Tab: This is where you manage the Blackboard variables for the entire graph (we will cover this deeply in Section 4).
- Compile Button: Located at the bottom of the panel. Click this to trigger the code-generator and update your DOTS backend.
πΊ 3. The Graph Canvas (Right)
This is your infinite workspace.
- Navigation: Middle-click (or hold Alt + Left-click) to pan. Scroll to zoom.
- Add Nodes: Right-click anywhere (or press Spacebar) to open the Node Search window.
- Ports: Nodes connect via ports.
- Top/Bottom Ports (Vertical): These are Flow Ports, used to define execution order (e.g., connecting a Sequence to an Action).
- Left/Right Ports (Horizontal): These are Data Ports, used to pass Blackboard variables directly between specific nodes (like passing a spatial position to an entity spawner).
π Working with Sub-Trees
As your AI logic grows, your graphs can become massive. Sub-Trees allow you to encapsulate logic into smaller, reusable Graph Assets. For example, you can build an Attack Sub-Tree and reuse it across multiple different enemy types.
π How to Setup a Sub-Tree
- Create a new
GraphAssetin your project folder (e.g.,Sub_MeleeAttack). - Open your main behavior tree in the Behavior Editor.
- Add a Composites > Sub-Tree node.
- Select the
Sub-Treenode. In the left-hand Node Tab, assign yourSub_MeleeAttackasset to the field.
π§ Navigating Sub-Trees
- Diving In: Simply double-click the Sub-Tree node on the canvas, or click the "Open Sub-Tree" button in the Node Tab. The editor will instantly load the nested graph.
- Going Back: Look at the top toolbar. You will see a breadcrumb trail showing your current depth. Click the name of the parent tree to return.
β οΈ Circular Dependency Protection
The compiler strictly prevents infinite loops. You cannot assign a Graph Asset to itself, nor can you place a Sub-Tree inside a graph that eventually calls the parent graph. The compiler will catch this and throw a helpful error in the console.
π Runtime Visual Debugging
Debugging DOTS applications can be notoriously difficult since the logic happens inside multithreaded Burst jobs. AI Behavior Architect completely solves this by providing real-time, visual debugging directly on the graph.
π How to Monitor an Agent
- Enter Play Mode in the Unity Editor.
- Open the Entity Hierarchy window (provided by Unity's Entities package).
- Select any Entity that possesses a
BtAgentcomponent. - If your
Behavior Editorwindow is open, it will immediately snap to theGraph Assetcurrently running on that Entity.
π¦ Visual Feedback
As the C# Job evaluates the behavior tree, the Editor listens to the state buffer and updates the nodes on your canvas in real-time. Look at the colored borders of the nodes:
- π‘ Yellow Border: The node is currently Running.
- π’ Green Border: The node returned Success this frame.
- π΄ Red Border: The node returned Failure this frame.
π Read-Only Mode
When you enter Play Mode, the entire graph canvas and toolbar become locked (Read-Only). This prevents you from accidentally making structural changes to the graph while the backend DOTS systems are actively iterating over the unmanaged Blob Assets.
β‘ Zero Overhead in Production
You might be wondering: "Does visual debugging slow down my DOTS jobs?"
The answer is No.
The debug state buffers (BtDebugState) are wrapped in strict #if UNITY_EDITOR preprocessor directives. When you build your game, the debugging memory footprint and logging logic are completely stripped from the compilation. Your production builds run with absolute maximum performance.
π§ The Blackboard System
In Behavior Trees, nodes need a way to share data with one another. For instance, a "Find" node might locate an enemy, and a "Move" node needs to know where that enemy is.
AI Behavior Architect handles this using a Blackboardβa shared memory space accessible by all nodes within a tree.
β‘ DOTS Performance Note
In object-oriented systems, Blackboards are usually dictionaries containing objects, which causes massive Garbage Collection. AI Behavior Architect stores Blackboard variables internally inside a 16-byte unmanaged wrapper (BtBlackboardEntry) attached directly to your Entity's chunk as aDynamicBuffer. This guarantees rapid, cache-friendly memory access during Burst jobs with zero allocations.
β Creating Variables
- Open the Behavior Editor and select your
Graph Asset. - In the left-hand Settings Panel, click the Variables tab.
- Click the Add Variable button.
- Configure your variable:
- Name: Give it a readable name (e.g.,
TargetPositionorHealth). - Type: Select between
Float,Int, orBool. (Note: For advanced DOTS data like Entity orfloat3, you will pass these viaData Ports, which we cover below). - Value: Set the default starting value.
- Name: Give it a readable name (e.g.,
π Connecting Variables via Data Ports
Some nodes (like Find or Create Entity) explicitly require complex data types like an Entity ID or a float3 position.
These nodes feature Horizontal Data Ports (on the left for Inputs, and on the right for Outputs).
- Left-click and drag from a
Data Portto another compatibleData Portto create a link. - The framework will automatically assign a hidden
Blackboard hashto transfer this unmanaged data from the outputting node directly into the receiving node during runtime.
π Blackboard Nodes
While custom C# actions can read and write to the Blackboard programmatically, you can also manipulate Blackboard variables directly using visual nodes.
βοΈ Blackboard Condition Node
This node acts as a gatekeeper. It checks the value of a Blackboard variable and returns Success if the condition is met, or Failure if it is not.
How to use:
- Add a Blackboard Condition node via the Search Window (
Blackboard/Blackboard Condition). - Select the node.
- In the Node Tab (left panel), configure the check:
- Variable: Select the variable from your
Blackboard list. - Operator: Choose how to compare it (
Equal,NotEqual,Greater,Less). - Value: The static value to compare against.
- Variable: Select the variable from your
Example: Check if Health is Less than 20. If true (Success), the tree can move on to a "Flee" action.
βοΈ Blackboard Modify Node
This node allows you to mathematically alter the value of a Blackboard variable during the execution of the tree. It always returns Success upon completion.
How to use:
- Add a Blackboard Modify node (Blackboard/Blackboard Modify).
- Select the node.
- In the Node Tab, configure the modification:
- Variable: Select the variable you want to change.
- Operator: Choose the math operation (
Set,Inc[Increase/Add],Dec[Decrease/Subtract]). - Value: The static value to apply.
Example: When a "Take Damage" action completes, use this node to Dec (decrease) the Health variable by 10.
π³ Composites
Composite nodes are the structural backbone of your Behavior Tree. They control the flow of execution, deciding which of their children (connected below them) to run, and in what order.
β‘οΈ Sequence
The Sequence node executes its children from left to right.
- Success: It returns Success only if all of its children return Success.
- Failure: It stops and returns Failure the moment any child returns Failure.
- Use case: A strict list of tasks. (e.g., Check Ammo -> Aim -> Fire). If the agent has no ammo, the sequence fails and stops immediately.
π Selector
The Selector node executes its children from left to right.
- Success: It stops and returns Success the moment any child returns Success.
- Failure: It returns Failure only if all of its children return Failure.
- Use case: Prioritizing actions. (e.g., Try to take cover -> Try to shoot -> Try to run away). It attempts the first option; if that fails, it tries the next.
π² Random Sequence & Random Selector
These function exactly like their standard counterparts, but instead of evaluating children strictly from left to right, they evaluate them in a random, non-repeating order.
- Use case: Making AI feel less predictable (e.g., choosing a random patrol point or picking a random taunt animation).
βΈ Parallel
The Parallel node executes all of its connected children simultaneously on the same frame.
When you select the Parallel node, you can configure its Policy in the settings panel:
- Require All Success: The Parallel node returns Success only when every running child finishes with Success. If any child fails, the Parallel node immediately returns Failure and aborts the other children.
- Require One Success: The Parallel node returns Success the moment any child finishes with Success, instantly aborting the remaining running children.
- Use case: Moving while shooting, or monitoring for threats while performing a task.
π Decorators
Decorator nodes only ever have one child. They sit above that child and modify its behavior, alter its result, or restrict its execution.
π Repeater
Executes its child node multiple times before returning a result to the parent.
- Configuration: You define the number of loops in the Node Inspector. If set to 0, it loops infinitely.
- Use case: "Chop Tree 5 times."
π Retry
Similar to the Repeater, but it only restarts the child if the child returns Failure.
- Configuration: You define the maximum number of retry attempts. If all attempts fail, the Retry node returns Failure to the parent.
- Use case: "Try to unlock the door up to 3 times."
β³ Cooldown
Prevents its child node from being executed again until a specific real-world time duration has passed. If evaluated while the cooldown is active, it instantly returns Failure.
- Configuration: Set the cooldown time (in seconds).
- Use case: Preventing an enemy from spamming a heavy attack.
π« Inverter
Reverses the result of its child node.
- If the child returns Success, the Inverter returns Failure.
- If the child returns Failure, the Inverter returns Success.
- Use case: "NOT in range." (Checks if in range, then inverts the success).
β Force Success / β Force Failure
These decorators ignore the actual result of their child node and override the output.
- Force Success: Always returns Success, even if the child failed.
- Force Failure: Always returns Failure, even if the child succeeded.
- Use case: Running a non-critical animation sequence that you don't care if it gets interrupted or fails, ensuring the main branch continues.
π Queries & Transactions
These nodes interact directly with the DOTS ECS world, allowing you to query, spawn, and modify Entities visually without writing any code.
π Find Node
Executes a highly optimized spatial hash query to locate other Entities in the world.
- Configuration:
- Max Distance: The radius to search.
- Spatial Cell Size: The chunk size of the internal grid (adjust for performance tuning).
- Find Method:
First (fastest),Nearest (checks distances),Random. - Components Setup: You must add at least one Component requirement. (e.g., Find an entity that has a
PlayerTag). You can also add specific value conditions (e.g., Find aHealthComponentwhereValue > 50).
- Outputs: This node provides two Horizontal Data Ports. It outputs the found Entity ID and its float3 position. You can plug these into other nodes.
π£ Create Entity Node
Instantiates a Unity Prefab dynamically at runtime.
- Configuration:
- Entity: Drag and drop a Unity
GameObjectPrefab here. (The framework will ensure it is baked into the DOTS registry). - Min/Max Radius: Spawns the entity at a random point within this radius around the target position.
- Position: You can type a static
x,y,zposition, or connect a float3Data Portto spawn the entity dynamically at a runtime location.
- Entity: Drag and drop a Unity
π Change Entity Node
Allows you to visually alter the component data of a specific Entity.
- Configuration:
- Input Entity (Data Port): By default, this node modifies the Agent executing the tree (Self). If you connect an Entity
Data Port(e.g., from aFind node), it will modify that targeted Entity instead. - Components Setup: Add the component you wish to modify.
- Transactions Tab: Configure the math operators to apply to the component's data fields (
Set,Inc,Dec).
- Input Entity (Data Port): By default, this node modifies the Agent executing the tree (Self). If you connect an Entity
π» Creating Your First Custom Node
While AI Behavior Architect provides powerful built-in nodes, the true strength of the framework is how easily you can write custom DOTS logic.
You write standard C# structs, and the frameworkβs Code Generator automatically writes the ECS boilerplate, creates the ISystem, queries the chunks, and schedules the multithreaded Burst jobs for you.
Step-by-Step: Creating an Action
- Create a new C# script in your project (e.g.,
LogMessageAction.cs). - Define a partial struct (this is required because the code generator will create the other half of the struct).
- Add the
[BtCustom]attribute above the struct. This registers it in the visual editor. - Create a private
NodeStatus Process()method. This is where your logic executes.
using SnivelerCode.AiBehavior.Runtime.Attributes;
using SnivelerCode.AiBehavior.Runtime.Components;
namespace MyGame.AI
{
// The string dictates the path in the Editor's Node Search Window
[BtCustom("Debug/Log Action")]
public partial struct LogMessageAction
{
// The generator automatically calls this Process method inside a Burst job
private NodeStatus Process()
{
// Note: Because this runs in a Burst job, you must use Unity.Burst logging,
// not UnityEngine.Debug.Log!
SnivelerCode.AiBehavior.Runtime.Utils.BtLogger.BurstLog()
.Append("Hello from Burst!")
.Log();
return NodeStatus.Success;
}
}
}
- Save the script.
- Open the
Behavior Editor, right-click, findCustomActionand search forDebug/Log Action. It is now a fully functional node!
π· API Attributes Reference
To pass data into and out of your Process method, you use specific attributes on your method parameters and struct fields. The code generator reads these attributes and wires up the UI and the ECS backend.
[BtParam]
Marks a parameter as a static value. This exposes a field in the Node Inspector inside the Unity Editor, allowing designers to tweak the value.
- Supported types: int, float, bool, and enum.
private NodeStatus Process([BtParam] float moveSpeed) { ... }
[BtInput]
Retrieves a dynamic variable from the Agent's Blackboard at runtime. In the editor, this generates a Horizontal Input Port on the left side of the node.
private NodeStatus Process([BtInput] Unity.Mathematics.float3 targetPosition) { ... }
[BtOutput]
Writes a dynamic variable back to the Agent's Blackboard. In the editor, this generates a Horizontal Output Port on the right side of the node.
private NodeStatus Process([BtOutput] out Unity.Mathematics.float3 currentPosition) { ... }
[BtDeltaTime]
Applied to a struct field (not a method parameter). The code generator will automatically inject SystemAPI.Time.DeltaTime into this field before the job runs.
[BtDeltaTime] public float DeltaTime;
π Querying and Modifying ECS Components
You do not need to write SystemAPI.Query or manual ComponentLookup code to access ECS components.
If you pass any standard IComponentData into your Process method, the code generator automatically adds it to the internal EntityQuery and provides it to you.
Reading Data (in)
Use the in keyword to read component data without modifying it. This is highly optimized as it creates a read-only access path in the job.
private NodeStatus Process(in LocalTransform transform)
{
float3 currentPos = transform.Position;
return NodeStatus.Success;
}
Writing Data (ref)
Use the ref keyword to read and modify component data.
private NodeStatus Process(ref LocalTransform transform, [BtParam] float speed)
{
// Move the entity up along the Y axis
transform.Position.y += speed * DeltaTime;
return NodeStatus.Running;
}
Note: If you are iterating over a buffer (IBufferElementData), simply pass DynamicBuffer<T> myBuffer without ref or in.
π Using Entity Command Buffers (Structural Changes)
In DOTS, you cannot add components, remove components, or destroy entities on the main thread while jobs are iterating over chunks. You must use an EntityCommandBuffer (ECB).
To use an ECB in your custom action, declare a struct field with the [BtCommandBuffer] attribute and specify which System Group should execute the buffer (usually EndSimulationEntityCommandBufferSystem).
using SnivelerCode.AiBehavior.Runtime.Attributes;
using SnivelerCode.AiBehavior.Runtime.Components;
using Unity.Entities;
namespace MyGame.AI
{
[BtCustom("Combat/Destroy Self")]
public partial struct DestroySelfAction
{
// 1. Declare the ECB Parallel Writer
[BtCommandBuffer(typeof(EndSimulationEntityCommandBufferSystem))]
public EntityCommandBuffer.ParallelWriter CommandBuffer;
private NodeStatus Process([BtEntityIndex] int queryIndex, in Entity selfEntity)
{
// 3. Queue the destruction safely
CommandBuffer.DestroyEntity(queryIndex, selfEntity);
return NodeStatus.Success;
}
}
}
β οΈ Critical ECB Rule: Always use a deterministic sort key
[BtEntityIndex]as the first argument in CommandBuffer methods when writing multithreaded DOTS code