ecs: add modules concept
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
5c4c2d3850
commit
3fddb687bc
2 changed files with 136 additions and 38 deletions
|
|
@ -30,6 +30,8 @@ pub const Entities = @import("entities.zig").Entities;
|
||||||
|
|
||||||
pub const Adapter = @import("systems.zig").Adapter;
|
pub const Adapter = @import("systems.zig").Adapter;
|
||||||
pub const System = @import("systems.zig").System;
|
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;
|
pub const World = @import("systems.zig").World;
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
|
|
@ -46,50 +48,57 @@ test "inclusion" {
|
||||||
test "example" {
|
test "example" {
|
||||||
const allocator = testing.allocator;
|
const allocator = testing.allocator;
|
||||||
|
|
||||||
const all_components = .{
|
const modules = Modules(.{
|
||||||
.example = .{
|
.physics = Module(.{
|
||||||
.physics = u16,
|
.components = .{
|
||||||
.geometry = u16,
|
.id = u16,
|
||||||
},
|
},
|
||||||
};
|
}),
|
||||||
|
.geometry = Module(.{
|
||||||
|
.components = .{
|
||||||
|
.id = u16,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
// Create a world.
|
// Create a world.
|
||||||
var world = try World(all_components).init(allocator);
|
var world = try World(modules).init(allocator);
|
||||||
defer world.deinit();
|
defer world.deinit();
|
||||||
|
|
||||||
const player1 = try world.entities.new();
|
const player1 = try world.entities.new();
|
||||||
const player2 = try world.entities.new();
|
const player2 = try world.entities.new();
|
||||||
const player3 = try world.entities.new();
|
const player3 = try world.entities.new();
|
||||||
try world.entities.setComponent(player1, .example, .physics, 1234);
|
try world.entities.setComponent(player1, .physics, .id, 1234);
|
||||||
try world.entities.setComponent(player1, .example, .geometry, 1234);
|
try world.entities.setComponent(player1, .geometry, .id, 1234);
|
||||||
|
|
||||||
try world.entities.setComponent(player2, .example, .physics, 1234);
|
try world.entities.setComponent(player2, .physics, .id, 1234);
|
||||||
try world.entities.setComponent(player3, .example, .physics, 1234);
|
try world.entities.setComponent(player3, .physics, .id, 1234);
|
||||||
|
|
||||||
const physics = (struct {
|
// TODO: integrate systems with modules.
|
||||||
pub fn physics(adapter: *Adapter(all_components)) void {
|
// const physics = (struct {
|
||||||
var iter = adapter.query(&.{"physics"});
|
// pub fn physics(adapter: *Adapter(modules)) void {
|
||||||
std.debug.print("\nphysics ran\n", .{});
|
// var iter = adapter.query(&.{"physics"});
|
||||||
while (iter.next()) |row| {
|
// std.debug.print("\nphysics ran\n", .{});
|
||||||
std.debug.print("found entity: {}\n", .{row.entity});
|
// while (iter.next()) |row| {
|
||||||
defer row.unlock();
|
// std.debug.print("found entity: {}\n", .{row.entity});
|
||||||
}
|
// defer row.unlock();
|
||||||
}
|
// }
|
||||||
}).physics;
|
// }
|
||||||
try world.register("physics", physics);
|
// }).physics;
|
||||||
|
// try world.register("physics", physics);
|
||||||
|
|
||||||
const rendering = (struct {
|
// const rendering = (struct {
|
||||||
pub fn rendering(adapter: *Adapter(all_components)) void {
|
// pub fn rendering(adapter: *Adapter(modules)) void {
|
||||||
var iter = adapter.query(&.{"geometry"});
|
// var iter = adapter.query(&.{"geometry"});
|
||||||
std.debug.print("\nrendering ran\n", .{});
|
// std.debug.print("\nrendering ran\n", .{});
|
||||||
while (iter.next()) |row| {
|
// while (iter.next()) |row| {
|
||||||
std.debug.print("found entity: {}\n", .{row.entity});
|
// std.debug.print("found entity: {}\n", .{row.entity});
|
||||||
defer row.unlock();
|
// defer row.unlock();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}).rendering;
|
// }).rendering;
|
||||||
try world.register("rendering", rendering);
|
// try world.register("rendering", rendering);
|
||||||
|
|
||||||
world.tick();
|
world.tick();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@ const std = @import("std");
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
const StructField = std.builtin.Type.StructField;
|
||||||
|
|
||||||
const Entities = @import("entities.zig").Entities;
|
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 {
|
return struct {
|
||||||
world: *World(all_components),
|
world: *World(modules),
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub const Iterator = Entities(all_components).Iterator;
|
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 {
|
return struct {
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
systems: std.StringArrayHashMapUnmanaged(System) = .{},
|
systems: std.StringArrayHashMapUnmanaged(System) = .{},
|
||||||
entities: Entities(all_components),
|
entities: Entities(all_components),
|
||||||
|
|
||||||
const Self = @This();
|
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 {
|
pub fn init(allocator: Allocator) !Self {
|
||||||
return Self{
|
return Self{
|
||||||
|
|
@ -52,7 +141,7 @@ pub fn World(all_components: anytype) type {
|
||||||
while (i < world.systems.count()) : (i += 1) {
|
while (i < world.systems.count()) : (i += 1) {
|
||||||
const system = world.systems.entries.get(i).value;
|
const system = world.systems.entries.get(i).value;
|
||||||
|
|
||||||
var adapter = Adapter(all_components){
|
var adapter = Adapter(modules){
|
||||||
.world = world,
|
.world = world,
|
||||||
};
|
};
|
||||||
system(&adapter);
|
system(&adapter);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue