Essay

Ownership vs Belonging as a New Paradigm for State Management in Unity

  • Unity
  • ScriptableObject
  • State Management
  • ECS
  • Architecture
  • Game Development

This article explores ReactiveEntitySet, a feature coming in v2.1.0 of Reactive SO — a ScriptableObject-based reactive architecture for Unity.

Unity Asset StoreReactive SO | Game ToolkitsScriptableObject-based reactive architecture for Unity. Variables, Event Channels, Runtime Sets, GPU Sync, Reactive Entity Sets, and dedicated debugging windows.

When I first explained ReactiveEntitySet to other developers, most of them responded with:

“Oh, so it’s like VariableSO but as an array?”

This is a fundamental misunderstanding. The surface-level similarities — ScriptableObject-based, state management, event firing — obscure what’s actually a paradigm shift in how we think about game state.

The Instancer Approach — “Ownership”

Unity Atoms’ Instancer creates runtime copies of a base VariableSO, giving each instance its own independent state.

Three enemies, each with an arrow down to its own separate HealthVariableSO copy, with no connection between the copies

This is the ownership paradigm. Each object “owns” its state. It’s a natural extension of OOP thinking — objects encapsulate data and behavior.

The Payroll Analogy

Imagine each employee carrying their own payroll slip.

  • Want to see everyone’s salary? You have to ask each person individually.
  • Want to process salaries in bulk? You need to collect all the data and convert it into an array first.

The ReactiveEntitySet Approach — “Belonging”

ReactiveEntitySet manages data centrally. Each instance accesses its state via an ID.

Three enemies, each carrying only an ID, pointing into adjacent cells of one contiguous EnemySet array

This is the belonging paradigm. Entities “register” with a set. State “exists” in the set, not in the object.

The Accounting Department Analogy

The accounting department manages all employee salary data centrally.

  • Want to see everyone’s salary? Just look at the accounting spreadsheet.
  • Want to process in bulk? Hand over the spreadsheet as-is.
  • Each employee accesses their data via their employee ID.

The Correct Lineage

The VariableSO lineage dead-ends after Instancer, while the RuntimeSet lineage continues into ReactiveEntitySet; the path between the two lineages is crossed out as a discontinuity

ReactiveEntitySet is not an extension of VariableSO. It’s an extension of RuntimeSet.

RuntimeSet manages “who exists.” ReactiveEntitySet manages “who exists + what they have.” This is a continuous evolution.

The jump from VariableSO to ReactiveEntitySet is a discontinuity. The paradigm is different.

Physical Memory Layout Matters

This isn’t just an abstract conceptual difference. It’s a physical memory layout difference.

Two memory strips: Instancer copies sit at unrelated positions with other allocations between them, while ReactiveEntitySet holds the same values in one adjacent run

This physical arrangement determines everything.

Optimizations Enabled by Central Aggregation

The paradigm shift from “ownership” to “belonging” enables optimizations that are simply impossible with Instancer:

OptimizationInstancerRESWhy
PaginationNoYesRequires central ID→Index management
Slice-based detectionNoYesCan’t slice without contiguous array
Double bufferingNoYesBuffer concept doesn’t exist
Backward iterationNoYesNo central array to iterate

Try implementing any of these with Instancer. You can’t.

Slice-based Change Detection

Using bitmasks to detect changes in 64-entity slices. Without a contiguous array, the concept of “slice” doesn’t exist.

A contiguous array split into four 64-entity slices, each with one bit above it; the two slices whose bit is set are marked scan and the rest are skipped

The Core Question

“Why does each object need to own its own state?”

OOP’s answer:

“Because objects encapsulate data and behavior.”

ReactiveEntitySet’s counter:

“Data is more efficient when managed centrally. Objects only need behavior. State is not an object’s ‘property’ — it’s an ‘entry’ in a set.”

Relationship with ECS

ReactiveEntitySet is a hybrid that selectively adopts ECS concepts.

ECS PrincipleRESNotes
EntityYesMonoBehaviour.GetInstanceID
ComponentYesTData struct
SystemPartialNo explicit System class
Data-orientedYesContiguous array

But there’s a crucial difference:

  • ECS: Full scan every frame (Query)
  • RES: Notify only on change (Reactive)

Where ECS forces “all or nothing,” RES lets you get data-oriented benefits while keeping your GameObject workflow.

A Note on Performance

When updates are infrequent — which is common for things like UI state, damage events, or AI decisions — the Reactive approach can be efficient since it only processes entities that actually changed.

The combination of contiguous memory layout and change-based processing seems to work well for these scenarios, though your mileage may vary depending on the specific use case.

Wrap-up

You don’t have to choose between “abandon everything for data-oriented” or “stay with OOP.” ReactiveEntitySet offers a gradual adoption path while keeping your GameObject workflow.

The technical decisions behind this design — why Sparse Set storage, what the reactive change detection borrows from existing ECS frameworks — are covered in the companion post below.


References

PostBringing ECS Insights to GameObject Workflows with ReactiveEntitySetNotes from researching EnTT, Entitas, Flecs, Bevy, Svelto, and DOTS — and how ReactiveEntitySet combines Sparse Set storage with reactive change detection to give Unity GameObject projects a pragmatic data-oriented middle ground.