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
/// 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`.
pub const Entities = struct {
pub fn Entities(all_components: anytype) type {
_ = all_components;
return struct {
allocator: Allocator,
/// TODO!
@ -355,6 +357,8 @@ pub const Entities = struct {
/// Database equivalent: table name -> tables representing entities.
archetypes: std.AutoArrayHashMapUnmanaged(u64, ArchetypeStorage) = .{},
const Self = @This();
/// 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:
///
@ -368,7 +372,7 @@ pub const Entities = struct {
};
pub const Iterator = struct {
entities: *Entities,
entities: *Self,
components: []const []const u8,
archetype_index: usize = 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{
.entities = entities,
.components = components,
};
}
pub fn init(allocator: Allocator) !Entities {
var entities = Entities{ .allocator = allocator };
pub fn init(allocator: Allocator) !Self {
var entities = Self{ .allocator = allocator };
const columns = try allocator.alloc(Column, 1);
columns[0] = .{
@ -433,7 +437,7 @@ pub const Entities = struct {
return entities;
}
pub fn deinit(entities: *Entities) void {
pub fn deinit(entities: *Self) void {
entities.entities.deinit(entities.allocator);
var iter = entities.archetypes.iterator();
@ -445,7 +449,7 @@ pub const Entities = struct {
}
/// Returns a new entity.
pub fn new(entities: *Entities) !EntityID {
pub fn new(entities: *Self) !EntityID {
const new_id = entities.counter;
entities.counter += 1;
@ -464,7 +468,7 @@ pub const Entities = struct {
}
/// Removes an entity.
pub fn remove(entities: *Entities, entity: EntityID) !void {
pub fn remove(entities: *Self, entity: EntityID) !void {
var archetype = entities.archetypeByID(entity);
const ptr = entities.entities.get(entity).?;
@ -485,7 +489,7 @@ pub const Entities = struct {
}
/// 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).?;
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,
/// moving the entity from it's current archetype table to the new archetype
/// 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);
// 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
/// 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);
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.
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);
if (!archetype.hasComponent(name)) return;
@ -698,7 +702,8 @@ pub const Entities = struct {
// TODO: ability to remove archetype entirely, deleting all entities in it
// TODO: ability to remove archetypes with no entities (garbage collection)
};
};
}
test "entity ID size" {
try testing.expectEqual(8, @sizeOf(EntityID));
@ -707,9 +712,11 @@ test "entity ID size" {
test "example" {
const allocator = testing.allocator;
const all_components = .{};
//-------------------------------------------------------------------------
// Create a world.
var world = try Entities.init(allocator);
var world = try Entities(all_components).init(allocator);
defer world.deinit();
//-------------------------------------------------------------------------

View file

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

View file

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