ecs: add modules concept

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2022-06-25 13:40:46 -07:00 committed by Stephen Gutekanst
parent 5c4c2d3850
commit 3fddb687bc
2 changed files with 136 additions and 38 deletions

View file

@ -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);