ecs: simplify how modules are written
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
85ffb37156
commit
4575080ca9
2 changed files with 64 additions and 80 deletions
|
|
@ -21,7 +21,6 @@ pub const Modules = @import("systems.zig").Modules;
|
|||
pub const Messages = @import("systems.zig").Messages;
|
||||
pub const MessagesTag = @import("systems.zig").MessagesTag;
|
||||
pub const World = @import("systems.zig").World;
|
||||
pub const View = @import("systems.zig").View;
|
||||
|
||||
// TODO:
|
||||
// * Iteration
|
||||
|
|
@ -37,33 +36,34 @@ test "inclusion" {
|
|||
test "example" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
const PhysicsMsg = Messages(.{
|
||||
.tick = void,
|
||||
});
|
||||
const physicsUpdate = (struct {
|
||||
pub fn physicsUpdate(msg: PhysicsMsg) void {
|
||||
const Physics2D = Module(struct {
|
||||
pointer: u8,
|
||||
|
||||
pub const name = .physics;
|
||||
pub const components = .{
|
||||
.id = u32,
|
||||
};
|
||||
pub const Message = .{
|
||||
.tick = void,
|
||||
};
|
||||
|
||||
pub fn update(msg: Message) void {
|
||||
switch (msg) {
|
||||
.tick => std.debug.print("\nphysics tick!\n", .{}),
|
||||
}
|
||||
}
|
||||
}).physicsUpdate;
|
||||
});
|
||||
|
||||
const Renderer = Module(struct {
|
||||
pub const name = .renderer;
|
||||
pub const components = .{
|
||||
.id = u16,
|
||||
};
|
||||
});
|
||||
|
||||
const modules = Modules(.{
|
||||
.physics = Module(.{
|
||||
.components = .{
|
||||
.id = u32,
|
||||
},
|
||||
.globals = struct {
|
||||
pointer: u8,
|
||||
},
|
||||
.messages = PhysicsMsg,
|
||||
.update = physicsUpdate,
|
||||
}),
|
||||
.renderer = Module(.{
|
||||
.components = .{
|
||||
.id = u16,
|
||||
},
|
||||
}),
|
||||
Physics2D,
|
||||
Renderer,
|
||||
});
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -9,15 +9,25 @@ const UnionField = std.builtin.Type.UnionField;
|
|||
|
||||
const Entities = @import("entities.zig").Entities;
|
||||
|
||||
/// An ECS module can provide components, systems, and global values.
|
||||
pub fn Module(comptime Params: anytype) @TypeOf(Params) {
|
||||
// TODO: validate the type
|
||||
return Params;
|
||||
/// Validates that a module matches the expected type layout.
|
||||
///
|
||||
/// An ECS module has components, systems, global values & more.
|
||||
pub fn Module(comptime M: anytype) type {
|
||||
if (@hasDecl(M, "name")) {
|
||||
_ = @tagName(M.name);
|
||||
} else @compileError("Module missing `pub const name = .foobar;`");
|
||||
if (@hasDecl(M, "Message")) _ = Messages(M.Message);
|
||||
|
||||
// TODO(ecs): validate M.components decl signature, if present.
|
||||
// TODO(ecs): validate M.update method signature, if present.
|
||||
return M;
|
||||
}
|
||||
|
||||
/// Describes a set of ECS modules, each of which can provide components, systems, and more.
|
||||
/// Validates that a list of module matches the expected type layout.
|
||||
///
|
||||
/// ECS modules have components, systems, global values & more.
|
||||
pub fn Modules(comptime modules: anytype) @TypeOf(modules) {
|
||||
// TODO: validate the type
|
||||
inline for (modules) |m| _ = Module(m);
|
||||
return modules;
|
||||
}
|
||||
|
||||
|
|
@ -95,9 +105,10 @@ 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")) {
|
||||
const module_name = @tagName(@field(module, "name"));
|
||||
if (@hasDecl(module, "components")) {
|
||||
fields = fields ++ [_]std.builtin.Type.StructField{.{
|
||||
.name = module_field.name,
|
||||
.name = module_name,
|
||||
.type = @TypeOf(module.components),
|
||||
.default_value = null,
|
||||
.is_comptime = false,
|
||||
|
|
@ -115,28 +126,9 @@ fn NamespacedComponents(comptime modules: anytype) type {
|
|||
});
|
||||
}
|
||||
|
||||
/// 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:
|
||||
/// Extracts namespaces components from modules like this. A module is said to have components if
|
||||
/// the struct has a `pub const components`. This function returns a namespaced components value
|
||||
/// like e.g.:
|
||||
///
|
||||
/// ```
|
||||
/// .{
|
||||
|
|
@ -155,34 +147,16 @@ 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;
|
||||
const module_name = @tagName(@field(module, "name"));
|
||||
if (@hasDecl(module, "components")) {
|
||||
@field(x, module_name) = module.components;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/// Extracts namespaced globals from modules like this:
|
||||
///
|
||||
/// ```
|
||||
/// .{
|
||||
/// .renderer = .{
|
||||
/// .globals = struct{
|
||||
/// foo: *Bar,
|
||||
/// baz: Bam,
|
||||
/// },
|
||||
/// ...
|
||||
/// },
|
||||
/// .physics2d = .{
|
||||
/// .globals = struct{
|
||||
/// foo: *Instance,
|
||||
/// },
|
||||
/// ...
|
||||
/// },
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Into a namespaced global type like this:
|
||||
/// Extracts namespaced globals from modules (a module is said to have globals if the struct has
|
||||
/// any fields), returning a type like e.g.:
|
||||
///
|
||||
/// ```
|
||||
/// struct{
|
||||
|
|
@ -200,13 +174,23 @@ fn NamespacedGlobals(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), "globals")) {
|
||||
const module_name = @tagName(@field(module, "name"));
|
||||
const global_fields = std.meta.fields(module);
|
||||
if (global_fields.len > 0) {
|
||||
const Globals = @Type(.{
|
||||
.Struct = .{
|
||||
.layout = .Auto,
|
||||
.is_tuple = false,
|
||||
.fields = global_fields,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
},
|
||||
});
|
||||
fields = fields ++ [_]std.builtin.Type.StructField{.{
|
||||
.name = module_field.name,
|
||||
.type = module.globals,
|
||||
.name = module_name,
|
||||
.type = Globals,
|
||||
.default_value = null,
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(module.globals),
|
||||
.alignment = @alignOf(Globals),
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
@ -272,7 +256,7 @@ pub fn World(comptime modules: anytype) type {
|
|||
pub fn send(world: *Self, comptime msg_tag: anytype) !void {
|
||||
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
||||
const module = @field(modules, module_field.name);
|
||||
if (@hasField(@TypeOf(module), "messages")) {
|
||||
if (@hasDecl(module, "messages")) {
|
||||
if (@hasField(module.messages, @tagName(msg_tag))) try module.update(world, msg_tag);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue