Skip to content

Shareable Components Neofox: hug_duck_heart ​

two fennecs happily holding a huge cardboard box together

Neofox: thumbsup Shared State Made Simple

Need the same data across multiple entities? Shareable components let you use reference types to share a single instance between entities. One update, everyone sees it!

What is a Shareable? ​

A Shareable component is simply a reference type (class or record) used as a component. Multiple entities can hold references to the same instance, enabling efficient shared state.

cs
public class SharedData
{
    public int Value;
}

var sharedData = new SharedData { Value = 42 };

var entity1 = world.Spawn().Add(sharedData);
var entity2 = world.Spawn().Add(sharedData);

// Both entities reference the same instance!
sharedData.Value = 100;  // Both see Value = 100

When to Use Shareables ​

ScenarioWhy Shareable?
Heavyweight objectsExpensive to create or copy
Global configurationSettings shared across entities
External referencesGame engine nodes, textures, etc.
Shared counters/stateSynchronized values
Large data structuresDon't want copies everywhere

Usage Examples ​

Basic Sharing ​

cs
public class TeamConfig
{
    public string Name;
    public int Score;
}

var blueTeam = new TeamConfig 
{ 
    Name = "Blue", 
    Score = 0 
};

// All blue team members share the same config
foreach (var entity in blueTeamEntities)
{
    entity.Add(blueTeam);
}

// Update score once, all members see it
blueTeam.Score += 10;

Processing with Streams ​

cs
// A mutable record (or class)
record SharedData(int Value)
{
    public int Value { get; set; } = Value;
}
cs
using var world = new World();
var stream = world.Query<SharedData>().Stream();

var sharedData = new SharedData(42);
world.Template().Add(sharedData).Spawn(5); // 5 entities share this

stream.For((ref data) =>
{
    data.Value++;  // Increments once per entity!
    Console.WriteLine(data.ToString());
});

sharedData.Value++;  // Increment outside of runner
Console.WriteLine();

stream.For((ref data) =>
{
    Console.WriteLine(data.ToString());
});
plaintext
SharedData { Value = 43 }
SharedData { Value = 44 }
SharedData { Value = 45 }
SharedData { Value = 46 }
SharedData { Value = 47 }

SharedData { Value = 48 }
SharedData { Value = 48 }
SharedData { Value = 48 }
SharedData { Value = 48 }
SharedData { Value = 48 }

With EntityTemplate ​

cs
var sharedTexture = LoadTexture("enemy.png");

// Bulk spawn entities with shared reference
world.Template()
    .Add(new Position())
    .Add(new Health { Value = 100 })
    .Add(sharedTexture)  // All share this texture
    .Add<Enemy>()
    .Spawn(1000);
ShareableObject Link
Plain componentRelation to object
One per type per entityMultiple of same type allowed
entity.Add(obj)entity.Add(Link.With(obj))
Groups by archetype normallyGroups entities by linked object
cs
Bank chase = new("Chase");

// Shareable: bob has chase as his plain Bank component
bob.Add(chase);

// Object Link: bob has a Bank relation TO chase
bob.Add(Link.With(chase));
// bob can also have another Bank link!
bob.Add(Link.With(targo));

See Object Links for more on relation-style object references.

Performance Considerations Neofox: think ​

Neofox: science The Trade-offs

  1. Reduced memory – One instance instead of N copies
  2. Instant updates – Change once, affects all entities
  3. Indirection cost – Each access requires a pointer dereference
  4. Cache implications – Data may not be contiguous in memory

Neofox: owo Iteration Performance

Queries that include reference components in their Stream Types will have slightly slower iteration due to memory indirection. However, queries that don't include the shareable as a Stream Type won't be affected!

cs
// Slower: iterating through shared references
stream.For((ref data) => { ... });

// Not affected: SharedData not in stream types
var stream = world.Query<Position>()
    .Has<SharedData>()  // Just filtering, not streaming
    .Stream();

Best Practices ​

  1. Use for truly shared state – If each entity needs different values, use value types
  2. Be mindful of mutation – Changes affect all entities immediately
  3. Consider lifecycle – Ensure shared objects outlive the entities using them
  4. Don't over-share – Not everything needs to be shared

Quick Reference ​

AspectShareables
TypeReference types (class, record)
StorageHeap, referenced by entities
SharingSame instance across entities
MemoryOne allocation, many references
Best ForExpensive objects, shared state

Constraints ​

  • Must be a reference type (class or record, not struct)
  • Must be notnull
  • Entity can only have one plain component of each type
  • For multiple of same type, use Object Links