Addressables and builds
Get a graph into a player build with its generated access intact, and load one by address when your game wants that.
On this page
A graph that runs in the editor runs in a player when its generated access is current. Build validation checks that for you and stops the build when something is stale.
The problem
Section titled “The problem”The worst version of this bug ships. A member binding still resolves in the editor because the editor can see the declaration, then the IL2CPP player cannot find it, and a guard stands still in the build you sent to a playtester. Horde Survival is stricter again: its entity bindings have no managed fallback at all, so a missing generated accessor is a build that cannot work.
Build preprocessing answers both cases before the build finishes. It validates what Unity already imported and compiled, and it fails closed.
Before you build
Section titled “Before you build”-
Save the graph and let Unity finish importing it.
-
Clear every parse, composition, and bake diagnostic on the asset. A graph with no executable cache is refused at runtime instead of falling back to an older interpretation.
-
Let member generation finish, then let Unity compile the generated provider or the generated entity bindings.
-
Reimport after changing anything a binding names: a signature, an optional default, a component, a buffer, or a target identity.
-
Build. Validation compares the graph, the manifest, the generated source, the compiled provider, and the preservation metadata.
What counts as included
Section titled “What counts as included”Build preprocessing discovers graphs reachable from player scenes, preloaded assets, Resources, asset bundles, and the installed Addressables settings. Referenced subgraphs and referenced state machines are followed through their dependencies, and a directly named loading root is included too.
Moving an invalid graph out of one scene does not make it safe when another loading contract still reaches it.
Generated access, per host
Section titled “Generated access, per host”GameObject provider. A graph with member bindings produces a per-consumer-assembly manifest, a strongly typed provider, a managed blackboard companion where one is needed, and linker preservation metadata. At runtime, public declarations are direct generated calls; opted-in non-public members prepare their delegates or field handles when the provider initializes. No member is discovered per tick. The provider’s BindingSetFingerprint has to match the imported graph, and a mismatch blocks the build.
Entity bindings. An entity graph produces a generated binding set, direct accessor methods, declared ECS access rows, a typed carrier, and an IBehaviorEntityMemberScheduler entry point. The set and the scheduler are supplied together to RegisterGraph, which is what Horde Survival’s GraphEnemyBrainComposer does. Entity member bindings need Burst AOT, and an accessor that is missing, stale, or stronger than its declared access row blocks the build.
IL2CPP. Generated calls and generated preservation metadata carry bound declaring types, chosen generic arguments, and prepared field access into an AOT player. A broad hand-written preservation list is the wrong repair for stale generation: fix what the validator names. AOT and IL2CPP covers the binding side.
Loading a graph by address
Section titled “Loading a graph by address”Ordinary composition assigns an imported graph asset in a scene or prefab. Addressables is for the game that wants to own graph loading, and it lives in its own assembly, BitQuirky.Behavior.Addressables, compiled only when the Addressables package is present through its version define.
BehaviorLoadResult result = await BehaviorSourceAddressablesLoader.LoadAndBakeAsync( graphReference);
if (!result.Success){ // Present result.Diagnostics through your game's loading failure flow. return;}
using BakedBehaviorArtifact artifact = result.Artifact;An overload takes a string key. Either way the loader releases its Addressables handles after reading the text, resolves referenced graphs by address, then parses, composes, validates, and bakes before the task completes. That work happens outside Burst, results are not cached, and the returned artifact owns native memory that its caller disposes.
Use it deliberately. It avoids a synchronous fetch and gives you the artifact’s lifetime, and it makes no promise that every parse and bake step leaves the main thread. Measure your loading scene on the target player.
When it goes wrong
| Symptom | Check | Fix |
|---|---|---|
| The build fails naming a provider fingerprint. | The generated provider no longer matches the graph it was generated from. | Reimport the graph, let generation and compilation finish, then build again. |
| The build fails naming a missing entity accessor. | Confirm the generated binding set and scheduler compiled for the consuming assembly. | Regenerate from the current graph and component declarations. |
| The player starts and a bound branch never runs. | Read the player log for a member provider or preservation complaint. | Assign the generated provider on the agent and rebuild. |
| An Addressables load returns a failure result. | Read result.Diagnostics, then the key and its catalog inclusion. | Include the graph and everything it references in the built catalog. |
| Native memory grows across loading screens. | Confirm every artifact from LoadAndBakeAsync is disposed. | Dispose the artifact when its runtime is done with it. The loader caches nothing for you. |
- Graph files: what gets imported and what bake produces.
- AOT and IL2CPP: the generated provider contract.
- Entity agents: supplying the generated pair at registration.