Skip to content
Bit Quirky Behavior Trees and
State Machines

Search documentation

All topics and APIs

Close returns focus to Search.

What are you looking for?

Try a common topic or enter a complete API identifier.

Browse the reference instead

Esc closes · Tab reaches results · Enter opens the focused link

v0.6.0
Menu

Entity bindings

Read and write ECS component data from a graph that ticks inside a Burst job, and know which member kinds that path offers.

Horde Survival’s enemies are entities, and the graph that decides for them ticks in Burst jobs of its own, scheduled beside your simulation’s systems: it reads and writes your component fields where they live.

Horde Survival’s enemies are entities. Their health, attack cooldown, and movement intent are IComponentData, and their systems run in jobs. A graph that wanted a MonoBehaviour in the middle would undo the reason that data is in components.

EnemyBrain.bqbehavior binds the components directly. Health.Current and ContactAttack.CooldownRemaining are field reads. AttackIntent.Ready is a two-way bool. MovementIntent.Mode and TargetPosition are writes the movement system consumes. DeadFade is watched through its enabled state, SurvivorTarget is read as a singleton, and the distance itself comes from Unity.Mathematics.math.distancesq, called as a static member.

  • The decision inputs are already component fields, and you want the graph reading them where they live.
  • One shared entity holds the answer for everybody, which is what a singleton target is for.
  • The math you need is a public static function, in your code or in Unity.Mathematics.
The Horde enemy graph attached Live to one running entity, with the entity binding rows in the rail
Figure 1. The graph ticks in a job and the editor still follows one entity: this World instance, this entity index and version, this graph instance.
  1. Set the graph’s Runtime path to Entity. The Target card’s Mode control changes to the entity modes.

  2. Choose the mode: Self for the entity this graph instance runs on, Handle for exactly one hop to another entity, Singleton for the one entity carrying a component, Static for a declaring type with no entity at all. The three cards below are EnemyBrain.bqbehavior’s own.

  3. Pick the member. A component offers its fields, its direct properties, and its enabled state; a static type offers its static methods and fields.

  4. Set the direction or the result as usual, and read Validation.

  5. Reimport the graph file so the consumer assembly regenerates its binding set, then let Unity compile.

  6. Register the graph with the generated binding set and its scheduler together, then run an entity carrying the components you bound.

The entity Target card with Mode Self and Component Health, with the help line This graph’s own entity; the component is read from it
Figure 2. Enemy Alive reads Health.Current off the enemy itself. Self has one row, Component, because there is nothing to look up.
The entity Target card with Mode Singleton and Component SurvivorTarget, plus an In the world row tagged RESOLVED
Figure 3. Has Target watches the survivor through Singleton. The In the world row states the contract: exactly one entity carries this component, and zero or several is a failure on that evaluation, never a guess.
The entity Target card with Mode Static and Type math from Unity.Mathematics
Figure 4. Distance Squared calls math.distancesq through Static: the declaring type alone, on the same card the GameObject path shows.

Handle adds a Read from control with two choices: Blackboard, which reads a surfaced entity value, or Field on self, which reads an Entity field off one of this entity’s own components. Either way the entry names one entity and the Component row names what to read on it. One hop, never a chain.

Target Resolves to
Self The entity that owns this graph instance
Handle Exactly one Entity hop, from a surfaced entity variable or an entity-typed field on Self
Singleton The one entity carrying the chosen component, with exactly one required
Static The static declaring type, with no entity lookup

A handle takes one hop and never chains from another handle. A destroyed handle, a missing component, the wrong singleton cardinality, or a missing buffer entry fails that one evaluation and performs no access, so a stale entity index is never read.

Container Available Refused
Component Field, direct property, enabled state, member condition on those Method and message, because a component has no call site
Buffer element Field on one addressed entry Property, method, message
Static type Method, message, field Property
Managed component Nothing Everything, for lack of entity storage

An event condition on this path observes an enableable component’s state or one retained event-buffer entry. A C# event, a UnityEvent, and a variable-change notification are refused, because there is no managed subscription inside a job.

A static method’s parameters pass by value or by ref. A ref parameter gets the same Read-write parameters row the GameObject path shows, with the same read-before, write-after rule, including on a void method bound as a member condition. An out, in, or params parameter is refused, and the refusal names that modifier.

A coroutine or awaitable result is refused too, with the reason stated plainly: waiting on a call needs a managed continuation this path does not have. When a member is unavailable the strip reads no entity call site and validation tags it, offering two ways out: pick another member, or run this graph on the GameObject path.

Choose an entry that already exists, By index with a non-negative constant, or By field with one exact identity field and the value to match. The accessor never adds, removes, resizes, or reorders the buffer, and an out-of-range index, no match, or several matches fails that evaluation without touching anything.

Result

Success
The access or the static call completed and its mapped outcome was succeeded.
Failure
The mapped outcome was failed, or a live target failure ended that evaluation.
Running
An authored outcome maps to running, or an entity event condition has not observed its occurrence yet.
  • Carried values are exact: bool, the signed and unsigned integrals, char, float, double, enums with those underlyings, float2, float3, float4, plus the entity handle and request identifier storage. Every one fits in sixteen bytes.
  • Conversions are the ordinary accepted set narrowed to those kinds, with reference assignability dropped.
  • Generated access is a struct the job can carry: typed component and buffer handles, typed lookups, and a table of access rows recording read-only or read-write intent per component. Nothing about it is a managed reference.
  • Member calls run inside the tick, on the job thread, through a constrained generic call, and every request completes in the same evaluation that raised it. Results are copied into the surfaced blackboard slot as bytes, and ref values commit before the result is chosen.
  • Reads that feed the blackboard sample before the core tick and writes push after it, as extra stages of the same Burst carrier.
  • The event condition is the one entity family that waits: it reports Running until the occurrence is observed, consumes it once, and reports the authored result. It rearms only after a tick sees the opposite state or the entry gone.

When it goes wrong

SymptomCheckFix
Your component method is missing from the picker.Read the refusal: a component has no entity call site for a method.Move the logic into a public static function and bind that, or keep the data in fields the graph reads.
An event you rely on cannot be bound.C# events are a GameObject-path shape.Observe an enableable component, or write a retained event buffer entry the graph can watch.
The graph refuses to register at runtime.A binding set without its scheduler, or the reverse, is rejected.Supply the generated pair together, from the generated factory.
Validation reports the singleton cardinality.Zero or several entities carry that component.Make it genuinely single, or bind through Self or a Handle.
A buffer read fails on some entities.The addressed entry has to exist already.Have your producer retain the entry with a stable identity field, then address it by field.
The build fails on a stale generated binding set.The compiled fingerprint differs from the expected one.Reimport the graph file and let the consumer assembly recompile. A player build never generates it for the first time.
Full-size image