ecs: pass an all_components parameter to everything

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2022-06-10 17:13:03 -07:00 committed by Stephen Gutekanst
parent 858c14bbae
commit 1f7ea529f4
3 changed files with 383 additions and 367 deletions

View file

@ -340,7 +340,9 @@ pub const void_archetype_hash = std.math.maxInt(u64);
/// row index, enabling entities to "move" from one archetype table to another seamlessly and /// row index, enabling entities to "move" from one archetype table to another seamlessly and
/// making lookup by entity ID a few cheap array indexing operations. /// making lookup by entity ID a few cheap array indexing operations.
/// * ComponentStorage(T) is a column of data within a table for a single type of component `T`. /// * ComponentStorage(T) is a column of data within a table for a single type of component `T`.
pub const Entities = struct { pub fn Entities(all_components: anytype) type {
_ = all_components;
return struct {
allocator: Allocator, allocator: Allocator,
/// TODO! /// TODO!
@ -355,6 +357,8 @@ pub const Entities = struct {
/// Database equivalent: table name -> tables representing entities. /// Database equivalent: table name -> tables representing entities.
archetypes: std.AutoArrayHashMapUnmanaged(u64, ArchetypeStorage) = .{}, archetypes: std.AutoArrayHashMapUnmanaged(u64, ArchetypeStorage) = .{},
const Self = @This();
/// Points to where an entity is stored, specifically in which archetype table and in which row /// Points to where an entity is stored, specifically in which archetype table and in which row
/// of that table. That is, the entity's component values are stored at: /// of that table. That is, the entity's component values are stored at:
/// ///
@ -368,7 +372,7 @@ pub const Entities = struct {
}; };
pub const Iterator = struct { pub const Iterator = struct {
entities: *Entities, entities: *Self,
components: []const []const u8, components: []const []const u8,
archetype_index: usize = 0, archetype_index: usize = 0,
row_index: u32 = 0, row_index: u32 = 0,
@ -402,15 +406,15 @@ pub const Entities = struct {
} }
}; };
pub fn query(entities: *Entities, components: []const []const u8) Iterator { pub fn query(entities: *Self, components: []const []const u8) Iterator {
return Iterator{ return Iterator{
.entities = entities, .entities = entities,
.components = components, .components = components,
}; };
} }
pub fn init(allocator: Allocator) !Entities { pub fn init(allocator: Allocator) !Self {
var entities = Entities{ .allocator = allocator }; var entities = Self{ .allocator = allocator };
const columns = try allocator.alloc(Column, 1); const columns = try allocator.alloc(Column, 1);
columns[0] = .{ columns[0] = .{
@ -433,7 +437,7 @@ pub const Entities = struct {
return entities; return entities;
} }
pub fn deinit(entities: *Entities) void { pub fn deinit(entities: *Self) void {
entities.entities.deinit(entities.allocator); entities.entities.deinit(entities.allocator);
var iter = entities.archetypes.iterator(); var iter = entities.archetypes.iterator();
@ -445,7 +449,7 @@ pub const Entities = struct {
} }
/// Returns a new entity. /// Returns a new entity.
pub fn new(entities: *Entities) !EntityID { pub fn new(entities: *Self) !EntityID {
const new_id = entities.counter; const new_id = entities.counter;
entities.counter += 1; entities.counter += 1;
@ -464,7 +468,7 @@ pub const Entities = struct {
} }
/// Removes an entity. /// Removes an entity.
pub fn remove(entities: *Entities, entity: EntityID) !void { pub fn remove(entities: *Self, entity: EntityID) !void {
var archetype = entities.archetypeByID(entity); var archetype = entities.archetypeByID(entity);
const ptr = entities.entities.get(entity).?; const ptr = entities.entities.get(entity).?;
@ -485,7 +489,7 @@ pub const Entities = struct {
} }
/// Returns the archetype storage for the given entity. /// Returns the archetype storage for the given entity.
pub inline fn archetypeByID(entities: *Entities, entity: EntityID) *ArchetypeStorage { pub inline fn archetypeByID(entities: *Self, entity: EntityID) *ArchetypeStorage {
const ptr = entities.entities.get(entity).?; const ptr = entities.entities.get(entity).?;
return &entities.archetypes.values()[ptr.archetype_index]; return &entities.archetypes.values()[ptr.archetype_index];
} }
@ -493,7 +497,7 @@ pub const Entities = struct {
/// Sets the named component to the specified value for the given entity, /// Sets the named component to the specified value for the given entity,
/// moving the entity from it's current archetype table to the new archetype /// moving the entity from it's current archetype table to the new archetype
/// table if required. /// table if required.
pub fn setComponent(entities: *Entities, entity: EntityID, comptime name: []const u8, component: anytype) !void { pub fn setComponent(entities: *Self, entity: EntityID, comptime name: []const u8, component: anytype) !void {
var archetype = entities.archetypeByID(entity); var archetype = entities.archetypeByID(entity);
// Determine the old hash for the archetype. // Determine the old hash for the archetype.
@ -591,7 +595,7 @@ pub const Entities = struct {
/// gets the named component of the given type (which must be correct, otherwise undefined /// gets the named component of the given type (which must be correct, otherwise undefined
/// behavior will occur). Returns null if the component does not exist on the entity. /// behavior will occur). Returns null if the component does not exist on the entity.
pub fn getComponent(entities: *Entities, entity: EntityID, name: []const u8, comptime Component: type) ?Component { pub fn getComponent(entities: *Self, entity: EntityID, name: []const u8, comptime Component: type) ?Component {
var archetype = entities.archetypeByID(entity); var archetype = entities.archetypeByID(entity);
const ptr = entities.entities.get(entity).?; const ptr = entities.entities.get(entity).?;
@ -599,7 +603,7 @@ pub const Entities = struct {
} }
/// Removes the named component from the entity, or noop if it doesn't have such a component. /// Removes the named component from the entity, or noop if it doesn't have such a component.
pub fn removeComponent(entities: *Entities, entity: EntityID, name: []const u8) !void { pub fn removeComponent(entities: *Self, entity: EntityID, name: []const u8) !void {
var archetype = entities.archetypeByID(entity); var archetype = entities.archetypeByID(entity);
if (!archetype.hasComponent(name)) return; if (!archetype.hasComponent(name)) return;
@ -699,6 +703,7 @@ pub const Entities = struct {
// TODO: ability to remove archetype entirely, deleting all entities in it // TODO: ability to remove archetype entirely, deleting all entities in it
// TODO: ability to remove archetypes with no entities (garbage collection) // TODO: ability to remove archetypes with no entities (garbage collection)
}; };
}
test "entity ID size" { test "entity ID size" {
try testing.expectEqual(8, @sizeOf(EntityID)); try testing.expectEqual(8, @sizeOf(EntityID));
@ -707,9 +712,11 @@ test "entity ID size" {
test "example" { test "example" {
const allocator = testing.allocator; const allocator = testing.allocator;
const all_components = .{};
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Create a world. // Create a world.
var world = try Entities.init(allocator); var world = try Entities(all_components).init(allocator);
defer world.deinit(); defer world.deinit();
//------------------------------------------------------------------------- //-------------------------------------------------------------------------

View file

@ -46,9 +46,11 @@ test "inclusion" {
test "example" { test "example" {
const allocator = testing.allocator; const allocator = testing.allocator;
const all_components = .{};
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Create a world. // Create a world.
var world = try World.init(allocator); var world = try World(all_components).init(allocator);
defer world.deinit(); defer world.deinit();
const player1 = try world.entities.new(); const player1 = try world.entities.new();
@ -61,7 +63,7 @@ test "example" {
try world.entities.setComponent(player3, "physics", @as(u16, 1234)); try world.entities.setComponent(player3, "physics", @as(u16, 1234));
const physics = (struct { const physics = (struct {
pub fn physics(adapter: *Adapter) void { pub fn physics(adapter: *Adapter(all_components)) void {
var iter = adapter.query(&.{"physics"}); var iter = adapter.query(&.{"physics"});
std.debug.print("\nphysics ran\n", .{}); std.debug.print("\nphysics ran\n", .{});
while (iter.next()) |row| { while (iter.next()) |row| {
@ -73,7 +75,7 @@ test "example" {
try world.register("physics", physics); try world.register("physics", physics);
const rendering = (struct { const rendering = (struct {
pub fn rendering(adapter: *Adapter) void { pub fn rendering(adapter: *Adapter(all_components)) void {
var iter = adapter.query(&.{"geometry"}); var iter = adapter.query(&.{"geometry"});
std.debug.print("\nrendering ran\n", .{}); std.debug.print("\nrendering ran\n", .{});
while (iter.next()) |row| { while (iter.next()) |row| {

View file

@ -4,52 +4,59 @@ const Allocator = mem.Allocator;
const testing = std.testing; const testing = std.testing;
const Entities = @import("entities.zig").Entities; const Entities = @import("entities.zig").Entities;
const Iterator = Entities.Iterator;
pub const Adapter = struct { pub fn Adapter(all_components: anytype) type {
world: *World, return struct {
world: *World(all_components),
pub fn query(adapter: *Adapter, components: []const []const u8) Iterator { const Self = @This();
pub const Iterator = Entities(all_components).Iterator;
pub fn query(adapter: *Self, components: []const []const u8) Iterator {
return adapter.world.entities.query(components); return adapter.world.entities.query(components);
} }
}; };
}
pub const System = fn (adapter: *Adapter) void; pub fn World(all_components: anytype) type {
return struct {
pub const World = struct {
allocator: Allocator, allocator: Allocator,
systems: std.StringArrayHashMapUnmanaged(System) = .{}, systems: std.StringArrayHashMapUnmanaged(System) = .{},
entities: Entities, entities: Entities(all_components),
pub fn init(allocator: Allocator) !World { const Self = @This();
return World{ pub const System = fn (adapter: *Adapter(all_components)) void;
pub fn init(allocator: Allocator) !Self {
return Self{
.allocator = allocator, .allocator = allocator,
.entities = try Entities.init(allocator), .entities = try Entities(all_components).init(allocator),
}; };
} }
pub fn deinit(world: *World) void { pub fn deinit(world: *Self) void {
world.systems.deinit(world.allocator); world.systems.deinit(world.allocator);
world.entities.deinit(); world.entities.deinit();
} }
pub fn register(world: *World, name: []const u8, system: System) !void { pub fn register(world: *Self, name: []const u8, system: System) !void {
try world.systems.put(world.allocator, name, system); try world.systems.put(world.allocator, name, system);
} }
pub fn unregister(world: *World, name: []const u8) void { pub fn unregister(world: *Self, name: []const u8) void {
world.systems.orderedRemove(name); world.systems.orderedRemove(name);
} }
pub fn tick(world: *World) void { pub fn tick(world: *Self) void {
var i: usize = 0; var i: usize = 0;
while (i < world.systems.count()) : (i += 1) { while (i < world.systems.count()) : (i += 1) {
const system = world.systems.entries.get(i).value; const system = world.systems.entries.get(i).value;
var adapter = Adapter{ var adapter = Adapter(all_components){
.world = world, .world = world,
}; };
system(&adapter); system(&adapter);
} }
} }
}; };
}