Skip to content

Instantly share code, notes, and snippets.

@tauoverpi
Last active November 18, 2023 19:18
Show Gist options
  • Save tauoverpi/e97dd10d9dc7a95b40806256953ed346 to your computer and use it in GitHub Desktop.
Save tauoverpi/e97dd10d9dc7a95b40806256953ed346 to your computer and use it in GitHub Desktop.
ecs-mini.zig
data: Data = undefined,
len: Length,
systems: Systems = .{},
/// Run all systems for a single logical game update.
pub fn update(ecs: *EntityComponentSystem, dt: f64) void {
inline for (@typeInfo(Systems).Struct.fields) |field| {
@field(ecs.systems, field.name).run(&ecs.data, &ecs.len, dt);
}
}
pub fn run(ecs: *EntityComponentSystem, system: anytype, dt: f64) void {
system.run(&ecs.data, &ecs.len, dt);
}
pub const Entity = packed struct(u16) {
type: Entity.Type,
id: Int,
pub const Type = std.meta.FieldEnum(Data);
pub const Int = @Type(.{ .Int = .{
.signedness = .unsigned,
.bits = 16 - @bitSizeOf(Entity.Type),
} });
};
pub const Length = typ: {
const info = @typeInfo(Data).Struct;
var fields: [info.fields.len]Type.StructField = undefined;
for (&fields, info.fields) |*field, desc| {
field.* = .{
.name = desc.name,
.type = u16,
.alignment = 2,
.default_value = &@as(u16, 0),
.is_comptime = false,
};
}
break :typ @Type(.{ .Struct = .{
.layout = .Extern,
.fields = &fields,
.decls = &.{},
.is_tuple = false,
} });
};
pub fn Component(comptime entries: u16, comptime T: type) type {
const info = @typeInfo(T).Struct;
var fields: [info.fields.len]Type.StructField = undefined;
for (&fields, info.fields) |*field, desc| {
field.* = .{
.name = desc.name,
.type = [entries]desc.type,
.alignment = 0,
.default_value = null,
.is_comptime = false,
};
}
return @Type(.{ .Struct = .{
.layout = .Extern,
.fields = &fields,
.decls = &.{},
.is_tuple = false,
} });
}
pub const Guard = enum { skip, run };
pub fn Runner(
comptime System: type,
comptime kind: enum { pure, render },
comptime components: []const @Type(.EnumLiteral),
) type {
const Fn = @TypeOf(@field(System, "update"));
const params = @typeInfo(Fn).Fn.params;
const start = switch (kind) {
.pure => 2,
.render => 3,
};
const Offset = struct {
len: u16,
data: typ: {
var fields: [components.len]Type.StructField = undefined;
for (&fields, components) |*field, spec| {
const name = @tagName(spec);
field.* = .{
.name = name,
.type = u16,
.alignment = 2,
.default_value = null,
.is_comptime = false,
};
}
break :typ @Type(.{ .Struct = .{
.layout = .Extern,
.fields = &fields,
.decls = &.{},
.is_tuple = false,
} });
},
};
const offsets = off: {
var offsets: []const Offset = &.{};
loop: for (@typeInfo(Data).Struct.fields) |field| {
var offset: Offset = .{
.len = @offsetOf(Length, field.name) >> 1,
.data = undefined,
};
for (components, params[start..]) |spec, param| {
const name = @tagName(spec);
if (!@hasField(field.type, name)) continue :loop;
const FT = @typeInfo(@TypeOf(@field(@as(field.type, undefined), name))).Array.child;
const C = @typeInfo(param.type.?).Pointer.child;
if (C != FT) @compileError(@typeName(C) ++ " != " ++ @typeName(FT));
@field(offset.data, name) = //
@offsetOf(field.type, name) +
@offsetOf(Data, field.name);
}
offsets = offsets ++ &[_]Offset{offset};
}
break :off offsets;
};
return struct {
pub fn run(sys: *System, data: *Data, len: *Length, dt: f64) void {
const data_p: [*]u8 = @ptrCast(data);
const len_p: [*]u16 = @ptrCast(len);
if (@hasDecl(System, "guard")) switch (sys.guard(dt)) {
.run => {},
.skip => return,
};
if (@hasDecl(System, "begin")) {
sys.begin();
}
for (offsets) |offset| {
var args: std.meta.ArgsTuple(Fn) = undefined;
args[0] = sys;
args[1] = len_p[offset.len];
switch (kind) {
.pure => {},
.render => args[2] = dt,
}
comptime var index = start;
inline for (params[start..], components) |param, spec| if (param.type.? != void) {
const name = @tagName(spec);
const off = @field(offset.data, name);
args[index] = @ptrCast(@alignCast(data_p + off));
index += 1;
};
@call(.auto, System.update, args);
}
if (@hasDecl(System, "end")) {
sys.end();
}
}
};
}
const std = @import("std");
const Type = std.builtin.Type;
const EntityComponentSystem = @This();
pub const Data = @import("Data.zig");
pub const Systems = @import("Systems.zig");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment