ecs: pass an all_components parameter to everything
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
858c14bbae
commit
1f7ea529f4
3 changed files with 383 additions and 367 deletions
|
|
@ -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();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue