From 3fddb687bcc4327673f5303c0075e7a2c2d5b952 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Sat, 25 Jun 2022 13:40:46 -0700 Subject: [PATCH] ecs: add modules concept Signed-off-by: Stephen Gutekanst --- ecs/src/main.zig | 75 +++++++++++++++++++--------------- ecs/src/systems.zig | 99 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 136 insertions(+), 38 deletions(-) diff --git a/ecs/src/main.zig b/ecs/src/main.zig index 95b31561..bc7b535e 100644 --- a/ecs/src/main.zig +++ b/ecs/src/main.zig @@ -30,6 +30,8 @@ pub const Entities = @import("entities.zig").Entities; pub const Adapter = @import("systems.zig").Adapter; pub const System = @import("systems.zig").System; +pub const Module = @import("systems.zig").Module; +pub const Modules = @import("systems.zig").Modules; pub const World = @import("systems.zig").World; // TODO: @@ -46,50 +48,57 @@ test "inclusion" { test "example" { const allocator = testing.allocator; - const all_components = .{ - .example = .{ - .physics = u16, - .geometry = u16, - }, - }; + const modules = Modules(.{ + .physics = Module(.{ + .components = .{ + .id = u16, + }, + }), + .geometry = Module(.{ + .components = .{ + .id = u16, + }, + }), + }); //------------------------------------------------------------------------- // Create a world. - var world = try World(all_components).init(allocator); + var world = try World(modules).init(allocator); defer world.deinit(); const player1 = try world.entities.new(); const player2 = try world.entities.new(); const player3 = try world.entities.new(); - try world.entities.setComponent(player1, .example, .physics, 1234); - try world.entities.setComponent(player1, .example, .geometry, 1234); + try world.entities.setComponent(player1, .physics, .id, 1234); + try world.entities.setComponent(player1, .geometry, .id, 1234); - try world.entities.setComponent(player2, .example, .physics, 1234); - try world.entities.setComponent(player3, .example, .physics, 1234); + try world.entities.setComponent(player2, .physics, .id, 1234); + try world.entities.setComponent(player3, .physics, .id, 1234); - const physics = (struct { - pub fn physics(adapter: *Adapter(all_components)) void { - var iter = adapter.query(&.{"physics"}); - std.debug.print("\nphysics ran\n", .{}); - while (iter.next()) |row| { - std.debug.print("found entity: {}\n", .{row.entity}); - defer row.unlock(); - } - } - }).physics; - try world.register("physics", physics); + // TODO: integrate systems with modules. + // const physics = (struct { + // pub fn physics(adapter: *Adapter(modules)) void { + // var iter = adapter.query(&.{"physics"}); + // std.debug.print("\nphysics ran\n", .{}); + // while (iter.next()) |row| { + // std.debug.print("found entity: {}\n", .{row.entity}); + // defer row.unlock(); + // } + // } + // }).physics; + // try world.register("physics", physics); - const rendering = (struct { - pub fn rendering(adapter: *Adapter(all_components)) void { - var iter = adapter.query(&.{"geometry"}); - std.debug.print("\nrendering ran\n", .{}); - while (iter.next()) |row| { - std.debug.print("found entity: {}\n", .{row.entity}); - defer row.unlock(); - } - } - }).rendering; - try world.register("rendering", rendering); + // const rendering = (struct { + // pub fn rendering(adapter: *Adapter(modules)) void { + // var iter = adapter.query(&.{"geometry"}); + // std.debug.print("\nrendering ran\n", .{}); + // while (iter.next()) |row| { + // std.debug.print("found entity: {}\n", .{row.entity}); + // defer row.unlock(); + // } + // } + // }).rendering; + // try world.register("rendering", rendering); world.tick(); } diff --git a/ecs/src/systems.zig b/ecs/src/systems.zig index ef806861..f0280551 100644 --- a/ecs/src/systems.zig +++ b/ecs/src/systems.zig @@ -2,12 +2,14 @@ const std = @import("std"); const mem = std.mem; const Allocator = mem.Allocator; const testing = std.testing; +const StructField = std.builtin.Type.StructField; const Entities = @import("entities.zig").Entities; -pub fn Adapter(all_components: anytype) type { +pub fn Adapter(modules: anytype) type { + const all_components = NamespacedComponents(modules); return struct { - world: *World(all_components), + world: *World(modules), const Self = @This(); pub const Iterator = Entities(all_components).Iterator; @@ -18,14 +20,101 @@ pub fn Adapter(all_components: anytype) type { }; } -pub fn World(all_components: anytype) type { +/// An ECS module can provide components, systems, and global values. +pub fn Module(comptime Params: anytype) @TypeOf(Params) { + // TODO: validate the type + return Params; +} + +/// Describes a set of ECS modules, each of which can provide components, systems, and more. +pub fn Modules(modules: anytype) @TypeOf(modules) { + // TODO: validate the type + return modules; +} + +/// Returns the namespaced components struct **type**. +// +/// Consult `namespacedComponents` for how a value of this type looks. +fn NamespacedComponents(comptime modules: anytype) type { + var fields: []const StructField = &[0]StructField{}; + inline for (std.meta.fields(@TypeOf(modules))) |module_field| { + const module = @field(modules, module_field.name); + if (@hasField(@TypeOf(module), "components")) { + fields = fields ++ [_]std.builtin.Type.StructField{.{ + .name = module_field.name, + .field_type = @TypeOf(module.components), + .default_value = null, + .is_comptime = false, + .alignment = @alignOf(@TypeOf(module.components)), + }}; + } + } + return @Type(.{ + .Struct = .{ + .layout = .Auto, + .is_tuple = false, + .fields = fields, + .decls = &[_]std.builtin.Type.Declaration{}, + }, + }); +} + +/// Extracts namespaces components from modules like this: +/// +/// ``` +/// .{ +/// .renderer = .{ +/// .components = .{ +/// .location = Vec3, +/// .rotation = Vec3, +/// }, +/// ... +/// }, +/// .physics2d = .{ +/// .components = .{ +/// .location = Vec2 +/// .velocity = Vec2, +/// }, +/// ... +/// }, +/// } +/// ``` +/// +/// Returning a namespaced components value like this: +/// +/// ``` +/// .{ +/// .renderer = .{ +/// .location = Vec3, +/// .rotation = Vec3, +/// }, +/// .physics2d = .{ +/// .location = Vec2 +/// .velocity = Vec2, +/// }, +/// } +/// ``` +/// +fn namespacedComponents(comptime modules: anytype) NamespacedComponents(modules) { + var x: NamespacedComponents(modules) = undefined; + inline for (std.meta.fields(@TypeOf(modules))) |module_field| { + const module = @field(modules, module_field.name); + if (@hasField(@TypeOf(module), "components")) { + @field(x, module_field.name) = module.components; + } + } + return x; +} + +pub fn World(comptime modules: anytype) type { + const all_components = namespacedComponents(modules); return struct { allocator: Allocator, systems: std.StringArrayHashMapUnmanaged(System) = .{}, entities: Entities(all_components), const Self = @This(); - pub const System = fn (adapter: *Adapter(all_components)) void; + pub const System = fn (adapter: *Adapter(modules)) void; pub fn init(allocator: Allocator) !Self { return Self{ @@ -52,7 +141,7 @@ pub fn World(all_components: anytype) type { while (i < world.systems.count()) : (i += 1) { const system = world.systems.entries.get(i).value; - var adapter = Adapter(all_components){ + var adapter = Adapter(modules){ .world = world, }; system(&adapter);