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 Messages = @import("systems.zig").Messages;
|
||||||
pub const MessagesTag = @import("systems.zig").MessagesTag;
|
pub const MessagesTag = @import("systems.zig").MessagesTag;
|
||||||
pub const World = @import("systems.zig").World;
|
pub const World = @import("systems.zig").World;
|
||||||
pub const View = @import("systems.zig").View;
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// * Iteration
|
// * Iteration
|
||||||
|
|
@ -37,33 +36,34 @@ test "inclusion" {
|
||||||
test "example" {
|
test "example" {
|
||||||
const allocator = testing.allocator;
|
const allocator = testing.allocator;
|
||||||
|
|
||||||
const PhysicsMsg = Messages(.{
|
const Physics2D = Module(struct {
|
||||||
.tick = void,
|
pointer: u8,
|
||||||
});
|
|
||||||
const physicsUpdate = (struct {
|
pub const name = .physics;
|
||||||
pub fn physicsUpdate(msg: PhysicsMsg) void {
|
pub const components = .{
|
||||||
|
.id = u32,
|
||||||
|
};
|
||||||
|
pub const Message = .{
|
||||||
|
.tick = void,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn update(msg: Message) void {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
.tick => std.debug.print("\nphysics tick!\n", .{}),
|
.tick => std.debug.print("\nphysics tick!\n", .{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).physicsUpdate;
|
});
|
||||||
|
|
||||||
|
const Renderer = Module(struct {
|
||||||
|
pub const name = .renderer;
|
||||||
|
pub const components = .{
|
||||||
|
.id = u16,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const modules = Modules(.{
|
const modules = Modules(.{
|
||||||
.physics = Module(.{
|
Physics2D,
|
||||||
.components = .{
|
Renderer,
|
||||||
.id = u32,
|
|
||||||
},
|
|
||||||
.globals = struct {
|
|
||||||
pointer: u8,
|
|
||||||
},
|
|
||||||
.messages = PhysicsMsg,
|
|
||||||
.update = physicsUpdate,
|
|
||||||
}),
|
|
||||||
.renderer = Module(.{
|
|
||||||
.components = .{
|
|
||||||
.id = u16,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,25 @@ const UnionField = std.builtin.Type.UnionField;
|
||||||
|
|
||||||
const Entities = @import("entities.zig").Entities;
|
const Entities = @import("entities.zig").Entities;
|
||||||
|
|
||||||
/// An ECS module can provide components, systems, and global values.
|
/// Validates that a module matches the expected type layout.
|
||||||
pub fn Module(comptime Params: anytype) @TypeOf(Params) {
|
///
|
||||||
// TODO: validate the type
|
/// An ECS module has components, systems, global values & more.
|
||||||
return Params;
|
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) {
|
pub fn Modules(comptime modules: anytype) @TypeOf(modules) {
|
||||||
// TODO: validate the type
|
inline for (modules) |m| _ = Module(m);
|
||||||
return modules;
|
return modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,9 +105,10 @@ fn NamespacedComponents(comptime modules: anytype) type {
|
||||||
var fields: []const StructField = &[0]StructField{};
|
var fields: []const StructField = &[0]StructField{};
|
||||||
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
||||||
const module = @field(modules, module_field.name);
|
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{.{
|
fields = fields ++ [_]std.builtin.Type.StructField{.{
|
||||||
.name = module_field.name,
|
.name = module_name,
|
||||||
.type = @TypeOf(module.components),
|
.type = @TypeOf(module.components),
|
||||||
.default_value = null,
|
.default_value = null,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
|
|
@ -115,28 +126,9 @@ fn NamespacedComponents(comptime modules: anytype) type {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts namespaces components from modules 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.:
|
||||||
/// .{
|
|
||||||
/// .renderer = .{
|
|
||||||
/// .components = .{
|
|
||||||
/// .location = Vec3,
|
|
||||||
/// .rotation = Vec3,
|
|
||||||
/// },
|
|
||||||
/// ...
|
|
||||||
/// },
|
|
||||||
/// .physics2d = .{
|
|
||||||
/// .components = .{
|
|
||||||
/// .location = Vec2
|
|
||||||
/// .velocity = Vec2,
|
|
||||||
/// },
|
|
||||||
/// ...
|
|
||||||
/// },
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Returning a namespaced components value like this:
|
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// .{
|
/// .{
|
||||||
|
|
@ -155,34 +147,16 @@ fn namespacedComponents(comptime modules: anytype) NamespacedComponents(modules)
|
||||||
var x: NamespacedComponents(modules) = undefined;
|
var x: NamespacedComponents(modules) = undefined;
|
||||||
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
||||||
const module = @field(modules, module_field.name);
|
const module = @field(modules, module_field.name);
|
||||||
if (@hasField(@TypeOf(module), "components")) {
|
const module_name = @tagName(@field(module, "name"));
|
||||||
@field(x, module_field.name) = module.components;
|
if (@hasDecl(module, "components")) {
|
||||||
|
@field(x, module_name) = module.components;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts namespaced globals from modules 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.:
|
||||||
/// ```
|
|
||||||
/// .{
|
|
||||||
/// .renderer = .{
|
|
||||||
/// .globals = struct{
|
|
||||||
/// foo: *Bar,
|
|
||||||
/// baz: Bam,
|
|
||||||
/// },
|
|
||||||
/// ...
|
|
||||||
/// },
|
|
||||||
/// .physics2d = .{
|
|
||||||
/// .globals = struct{
|
|
||||||
/// foo: *Instance,
|
|
||||||
/// },
|
|
||||||
/// ...
|
|
||||||
/// },
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Into a namespaced global type like this:
|
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// struct{
|
/// struct{
|
||||||
|
|
@ -200,13 +174,23 @@ fn NamespacedGlobals(comptime modules: anytype) type {
|
||||||
var fields: []const StructField = &[0]StructField{};
|
var fields: []const StructField = &[0]StructField{};
|
||||||
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
||||||
const module = @field(modules, module_field.name);
|
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{.{
|
fields = fields ++ [_]std.builtin.Type.StructField{.{
|
||||||
.name = module_field.name,
|
.name = module_name,
|
||||||
.type = module.globals,
|
.type = Globals,
|
||||||
.default_value = null,
|
.default_value = null,
|
||||||
.is_comptime = false,
|
.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 {
|
pub fn send(world: *Self, comptime msg_tag: anytype) !void {
|
||||||
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
||||||
const module = @field(modules, module_field.name);
|
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);
|
if (@hasField(module.messages, @tagName(msg_tag))) try module.update(world, msg_tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue