πŸ‘‹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-oriented MonoBehaviour workflows. 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?

You get the rapid iteration of visual scripting with the extreme runtime performance of pure DOTS code.

✨ Core Features at a Glance


🧠 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:

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

πŸ’‘ 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

  1. Open your Unity Project.
  2. Ensure you have installed the Entities package via the Unity Package Manager.
  3. Import the AI Behavior Architect .unitypackage into your project.
  4. 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

  1. In the top menu bar, navigate to Edit > Project Settings.
  2. Select the AI Behavior Architect tab on the left-hand menu.
  3. Locate the Generated Directory field.
  4. Choose or create a folder where the framework will safely output the auto-generated DOTS systems.
    • Default path: Assets/SnivelerCode/AiBehavior/Generated
  5. 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

  1. Right-click anywhere in your Project window.
  2. Go to Create > Entities > Sniveler Code > AI Graph and name it MyFirstAgent.
  3. Open the editor by going to Window/Sniveler Code/AI Behavior Architect.
  4. Drag and drop your new MyFirstAgent asset into the Active Tree slot in the editor toolbar.

Step 2: Add Logic Nodes

  1. Right-click in the empty grid area (or press Spacebar) to open the Node Search Window.
  2. Search for and add a Sequence node.
  3. Right-click again and add an Action Wait node.
  4. Connect the bottom output port of the Sequence node to the top input port of the Wait node.

Step 3: Compile the Tree

  1. On the left-hand Settings Panel, click the Compile button.
  2. 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

  1. Open a Sub-Scene in your Unity project (required for DOTS baking).
  2. Create a new empty GameObject and name it AI Agent.
  3. Add the BtAgentAuthoring component to this GameObject.
  4. Drag your compiled MyFirstAgent Graph Asset into the Tree field of the authoring component.
  5. (Optional) Add the BtSettingsAuthoring component if you wish to configure the maximum evaluation iterations per frame.

Step 5: Bake and Play!

  1. Ensure your Sub-Scene is closed/baked.
  2. Press Play in the Unity Editor.
  3. While in Play Mode, select your AI Agent Entity using the Entity Hierarchy window.
  4. 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.

πŸŽ› 2. The Settings Panel (Left)

This side-panel contains the contextual settings for your graph and nodes.

πŸ—Ί 3. The Graph Canvas (Right)

This is your infinite workspace.




πŸ—‚ 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

  1. Create a new GraphAsset in your project folder (e.g., Sub_MeleeAttack).
  2. Open your main behavior tree in the Behavior Editor.
  3. Add a Composites > Sub-Tree node.
  4. Select the Sub-Tree node. In the left-hand Node Tab, assign your Sub_MeleeAttack asset to the field.

🧭 Navigating Sub-Trees

⚠️ 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

  1. Enter Play Mode in the Unity Editor.
  2. Open the Entity Hierarchy window (provided by Unity's Entities package).
  3. Select any Entity that possesses a BtAgent component.
  4. If your Behavior Editor window is open, it will immediately snap to the Graph Asset currently 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:

πŸ”’ 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 a DynamicBuffer. This guarantees rapid, cache-friendly memory access during Burst jobs with zero allocations.

βž• Creating Variables

  1. Open the Behavior Editor and select your Graph Asset.
  2. In the left-hand Settings Panel, click the Variables tab.
  3. Click the Add Variable button.
  4. Configure your variable:
    • Name: Give it a readable name (e.g., TargetPosition or Health).
    • Type: Select between Float, Int, or Bool. (Note: For advanced DOTS data like Entity or float3, you will pass these via Data Ports, which we cover below).
    • Value: Set the default starting value.

πŸ”Œ 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).

  1. Left-click and drag from a Data Port to another compatible Data Port to create a link.
  2. The framework will automatically assign a hidden Blackboard hash to 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:

  1. Add a Blackboard Condition node via the Search Window (Blackboard/Blackboard Condition).
  2. Select the node.
  3. 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.

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:

  1. Add a Blackboard Modify node (Blackboard/Blackboard Modify).
  2. Select the node.
  3. 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.

πŸ”€ Selector

The Selector node executes its children from left to right.

🎲 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.

⏸ 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:


πŸŽ€ 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.

πŸ”„ Retry

Similar to the Repeater, but it only restarts the child if the child returns Failure.

⏳ 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.

🚫 Inverter

Reverses the result of its child node.

βœ… Force Success / ❌ Force Failure

These decorators ignore the actual result of their child node and override the output.


πŸ”Ž 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.

🐣 Create Entity Node

Instantiates a Unity Prefab dynamically at runtime.

πŸ›  Change Entity Node

Allows you to visually alter the component data of a specific 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

  1. Create a new C# script in your project (e.g., LogMessageAction.cs).
  2. Define a partial struct (this is required because the code generator will create the other half of the struct).
  3. Add the [BtCustom] attribute above the struct. This registers it in the visual editor.
  4. 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;
        }
    }
}
  1. Save the script.
  2. Open the Behavior Editor, right-click, find CustomAction and search for Debug/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.

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


Edit on GitHub v1.0.0