Shareable Components

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 = 100When to Use Shareables
| Scenario | Why Shareable? |
|---|---|
| Heavyweight objects | Expensive to create or copy |
| Global configuration | Settings shared across entities |
| External references | Game engine nodes, textures, etc. |
| Shared counters/state | Synchronized values |
| Large data structures | Don'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.Entity().Add(sharedData).Spawn(5); // 5 entities share this
stream.For((ref SharedData data) =>
{
data.Value++; // Increments once per entity!
Console.WriteLine(data.ToString());
});
sharedData.Value++; // Increment outside of runner
Console.WriteLine();
stream.For((ref SharedData 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 EntitySpawner
cs
var sharedTexture = LoadTexture("enemy.png");
// Bulk spawn entities with shared reference
world.Entity()
.Add(new Position())
.Add(new Health { Value = 100 })
.Add(sharedTexture) // All share this texture
.Add<Enemy>()
.Spawn(1000);Shareables vs Object Links
| Shareable | Object Link |
|---|---|
| Plain component | Relation to object |
| One per type per entity | Multiple of same type allowed |
entity.Add(obj) | entity.Add(Link.With(obj)) |
| Groups by archetype normally | Groups 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
The Trade-offs
- Reduced memory – One instance instead of N copies
- Instant updates – Change once, affects all entities
- Indirection cost – Each access requires a pointer dereference
- Cache implications – Data may not be contiguous in memory
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 SharedData data) => { ... });
// Not affected: SharedData not in stream types
var stream = world.Query<Position>()
.Has<SharedData>() // Just filtering, not streaming
.Stream();Best Practices
- Use for truly shared state – If each entity needs different values, use value types
- Be mindful of mutation – Changes affect all entities immediately
- Consider lifecycle – Ensure shared objects outlive the entities using them
- Don't over-share – Not everything needs to be shared
Quick Reference
| Aspect | Shareables |
|---|---|
| Type | Reference types (class, record) |
| Storage | Heap, referenced by entities |
| Sharing | Same instance across entities |
| Memory | One allocation, many references |
| Best For | Expensive objects, shared state |
Constraints
- Must be a reference type (
classorrecord, notstruct) - Must be
notnull - Entity can only have one plain component of each type
- For multiple of same type, use Object Links