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

How the sample games are built

The shared architecture under Platformer, Dungeon Heist, and Racing Line, so the assets, the runner, and the composition root each sample cites make sense before you copy the pattern.

The three GameObject games share one way of organizing a game, and every chapter leans on it: “the authorities”, “the composition root”, “the adapter fills the reference slots”, “the runner initializes”. This page is that architecture in one place. It comes from the Bit Quirky Core library, which ships with every sample package as BitQuirky.Core, so it is yours to use as well as to read. Horde Survival runs on the entity path with ECS systems and does not use the runner.

A game logic asset is a ScriptableObject that owns one area of the rules and the state that goes with them: Racing Line’s Pacing owns the rubber-band pressure, Platformer’s Collectibles owns which coins are taken, Dungeon Heist’s Guards owns every guard’s suspicion timing. It lives in the project rather than in a scene, so the rule survives a scene reload and can be read by anything the game hands it to. The samples call these authorities. Each one derives from Core’s GameLogic class and is created from a Game Logic entry in the Create Asset menu.

The game logic list is one asset per project, GameLogicList.asset, holding every authority in initialization order. The order comes from a [GameLogic(priority)] attribute on each class, lower first, then by name; the list sorts itself when refreshed, so nothing is ordered by hand.

The runner is one GameLogicRunner component in the gameplay scene. It reads the list and drives a lifecycle on each authority by method name, so an authority declares only the methods it needs. The three gameplay scenes each hold exactly one, and each project’s architecture tests assert that count.

The data object index is one asset per project registering every Core data asset by name: authorities, game events, variables. The runner rebuilds it in the editor on every Awake, and uses it to run GameAwake on data assets that are not authorities.

The composition root is a scene MonoBehaviour, PlatformerComposition, DungeonHeistComposition, or RacingLineComposition, that holds serialized references to the exact authority assets and hands them to each consumer through an explicit Configure(...) call. There is no singleton, no FindObjectOfType, and no static state. The root never runs an authority; the runner does that.

The runner looks for these methods on each authority, private or public, and calls the ones it finds:

Method When What the samples do in it
GameAwake Runner Awake, in list order Assert injected dependencies are non-null and already Initialized, clear registrations, set Initialized = true
GameStart Runner Start Unused by the samples
GameReset When the project’s reset game event is raised Clear the session: rosters, scores, timers, phase
GameUpdate(float deltaTime) Every frame Platformer Players advances the slam wind-up; Dungeon Heist Guards, Alarms, and Coins age their own timers
GameFixedUpdate, GameLateUpdate Physics and late frame Unused by the samples
GameDestroy After the runner is destroyed, following a short delay Release events, clear state, set Initialized = false

Initialized is the contract between the pieces. An authority sets it at the end of its own GameAwake, and everything that depends on the authority asserts it before use. Racing Line’s order shows why the priority matters: TrackLogic at -100, then GameMaster at -50, Drivers at -40, Driving at -39, Pacing at -38. Each GameAwake down that chain asserts the one above it is initialized, so a wrong order fails at the first frame instead of somewhere in a race.

The delayed GameDestroy exists because authorities are shared assets: after a scene reload, a new runner may already have claimed them, so a dying runner only tears down what it still owns.

A graph never reaches an authority on its own. The host that composes the agent gets the authority from the composition root, checks it is initialized, and fills the graph’s reference slot with it:

  • Racing Line’s CpuBehaviorGraphDriver.Configure asserts pacing.Initialized and the same for drivers, driving, and gameMaster, then builds one BehaviorMemberReference per slot key and assigns the array to BehaviorAgent.MemberReferences before InitializeNow().
  • Dungeon Heist’s GuardBehaviorActor fills guards, gameMaster, and the guard’s patrol-speed clone the same way.
  • Platformer’s GraphEnemyPatrol fills the enemies slot from its own serialized Enemies field, which has to be the same project asset PlatformerComposition holds.

That is why a sample graph shows assign a … in its Reference target while the editor is in Static mode and still reports Bindings valid: the slot is filled at runtime by code that has already proven the authority is ready. Targets and resolve describes the slot itself.

Time-based rules stay in the authority. Dungeon Heist’s Guards.GameUpdate ages every graph guard’s decision timer, so the graph’s condition reads a number the authority owns rather than counting frames itself. When a chapter says a threshold “is game data, not a graph edit”, this is where that data lives.

Project Authorities Composition root Runner
Platformer Assets/Platformer/Runtime/GameLogic/ (GameMaster, Players, Enemies, Collectibles, Scoring) PlatformerComposition GameLogicRunner object in Platformer.unity
Dungeon Heist Assets/_Project/Runtime/GameLogic/ (GameMaster, Guards, Alarms, Coins, Players) DungeonHeistComposition, then GuardBehaviorActor on Guard_2 GameLogicRunner object in DungeonHeist.unity
Racing Line Assets/_Project/Runtime/GameLogic/ (GameMaster, Drivers, Driving, Pacing, TrackLogic) RacingLineComposition, then CpuBehaviorGraphDriver per car GameLogicRunner object in RacingLine.unity

The asset instances sit beside the code under GameLogic/ in each project, with the project’s single GameLogicList.asset and DataObjectIndex.asset in the same tree.

Core ships with the samples, so you can adopt the runner and the authorities as they are, or keep the shape with your own plumbing. What a graph needs from the game is the part this architecture makes explicit: one instance of each shared rule object, initialized before any agent that reads it, handed to the agent by the code that owns it. A plain ScriptableObject with your own initialization order, a service container, or a bootstrap scene that loads everything before spawning agents all satisfy that. The Reference slot does not care how the instance got there, only that the host passed the one instance the rest of your game uses.

Full-size image