Value Type Components
The Performance Champions
Value types are the bread and butter of fennecs! Stored directly in archetype memory, they provide excellent performance and cache locality. Most of your components will be value types.
What Makes Them Fast?
In fennecs, value type components are stored directly within the memory of each Archetype. Each entity has its own copy, and all copies are neatly arranged in contiguous memory – perfect for CPU cache efficiency!
var entity = world.Spawn()
.Add(new Position { X = 0, Y = 0 })
.Add(new Velocity { X = 1, Y = 1 });
Cache Locality FTW!
Since value type components are stored contiguously in memory, iterating over entities with the same component layout is extremely efficient. The CPU cache can prefetch and cache component data, minimizing costly RAM reads!
Defining Value Types
Recommended: Record Structs
C# 10's record struct is perfect for components – minimal boilerplate, value semantics, and nice ToString() output for debugging!
// Minimal Boilerplate™️ - it's what fennecs crave!
public record struct Position(float X, float Y);
public record struct Velocity(float X, float Y);
public record struct Health(int Value);// You can add explicit getters/setters if needed
public record struct Position(float X, float Y)
{
public float X { get; set; } = X;
public float Y { get; set; } = Y;
}// Classic structs work great too!
public struct Position
{
public float X;
public float Y;
}
public struct Velocity
{
public float X;
public float Y;
}Usage Examples
Adding to Entities
var player = world.Spawn()
.Add(new Position { X = 0, Y = 0 })
.Add(new Velocity { X = 5, Y = 0 })
.Add(new Health { Value = 100 });Processing with Streams
var stream = world.Query<Position, Velocity>().Stream();
stream.For((ref Position position, ref Velocity velocity) =>
{
position.X += velocity.X;
position.Y += velocity.Y;
});Modifying via Ref
ref var health = ref entity.Ref<Health>();
health.Value -= 10;
// Or inline
entity.Ref<Position>().X += 100;Tags: The Zero-Size Special Case
Empty structs use no memory at all – they carry meaning purely through their presence!
// Minimal!
public struct PlayerTag;// Still good, indents nicely with other records
public record struct PlayerTag;See Tags for more details on zero-size components.
Quick Reference
| Aspect | Value Types |
|---|---|
| Storage | Directly in archetype memory |
| Performance | Fastest to iterate |
| Sharing | Each entity has its own copy |
| Cache | Excellent locality |
| Best For | Most components! |
Value Types vs Reference Types
| Value Types | Reference Types |
|---|---|
| Stored contiguously | Stored on heap |
| No indirection | Requires pointer dereference |
| Best cache performance | May cause cache misses |
| Copied on archetype change | Reference copied |
| Cannot be shared | Can be shared |
When to Use Reference Types
Reference types require at least one indirect memory lookup when accessed. Use Shareables when you need:
- Shared state between entities
- Large, expensive-to-copy data
- Objects from external systems (game engine nodes, etc.)
Constraints
- Must be
notnull(no nullable types) - For
Add<C>()without value, must havenew()constraint - Copied when entity moves between archetypes