add basic entity component system

This commit is contained in:
jjanzen 2025-01-21 19:20:50 -06:00
parent 9f18b702b1
commit 8a20d05a7a
4 changed files with 127 additions and 47 deletions

39
src/ecs/component.zig Normal file
View file

@ -0,0 +1,39 @@
const std = @import("std");
pub const ComponentStub = struct {
pub fn init(args: ComponentStub) ComponentStub {
_ = args;
return .{};
}
pub fn deinit(self: *ComponentStub) void {
_ = self;
}
};
pub const ComponentType = enum(usize) {
component_stub = 0,
};
pub const Component = union(ComponentType) {
component_stub: ComponentStub,
pub fn init(t: ComponentType, args: anytype) Component {
switch (t) {
ComponentType.component_stub => return Component{
.component_stub = ComponentStub.init(args),
},
}
}
pub fn deinit(self: *Component) void {
switch (self.*) {
.component_stub => |*comp| comp.deinit(),
}
}
};
test "stub component" {
var stub = Component.init(ComponentType.component_stub, .{});
defer stub.deinit();
}

84
src/ecs/ecs.zig Normal file
View file

@ -0,0 +1,84 @@
const std = @import("std");
const component = @import("component.zig");
const ComponentList = struct {
entities: std.AutoHashMap(usize, component.Component),
};
pub const EntityComponentSystem = struct {
entities: std.ArrayList(usize),
components: std.AutoHashMap(usize, ComponentList),
available_ids: std.ArrayList(usize),
next_id: usize,
pub fn init(allocator: std.mem.Allocator) !EntityComponentSystem {
var ecs = EntityComponentSystem{
.entities = std.ArrayList(usize).init(allocator),
.components = std.AutoHashMap(usize, ComponentList).init(allocator),
.available_ids = std.ArrayList(usize).init(allocator),
.next_id = 0,
};
inline for (std.meta.fields(component.ComponentType)) |comp| {
try ecs.components.put(comp.value, ComponentList{
.entities = std.AutoHashMap(usize, component.Component).init(allocator),
});
}
return ecs;
}
pub fn deinit(self: *EntityComponentSystem) void {
self.entities.deinit();
self.available_ids.deinit();
var component_iterator = self.components.iterator();
while (component_iterator.next()) |component_entry| {
var comp = component_entry.value_ptr;
var entity_iterator = comp.entities.iterator();
while (entity_iterator.next()) |entity_entry| {
var entity = entity_entry.value_ptr;
entity.deinit();
}
comp.entities.deinit();
}
self.components.deinit();
}
pub fn createEntity(self: *EntityComponentSystem) !usize {
const reuse_id = self.available_ids.popOrNull();
if (reuse_id != null and reuse_id.? < self.entities.items.len) {
self.entities.items[reuse_id.?] = reuse_id.?;
return reuse_id.?;
}
const id = self.next_id;
try self.entities.append(id);
self.next_id += 1;
return id;
}
pub fn componentAddEntity(
self: *EntityComponentSystem,
comp: component.ComponentType,
entity: usize,
args: anytype,
) !void {
var comp_list = self.components.getPtr(@intFromEnum(comp)) orelse return;
try comp_list.entities.put(entity, component.Component.init(comp, args));
}
};
test "add entities to components" {
var ecs = try EntityComponentSystem.init(std.testing.allocator);
defer ecs.deinit();
const entity = try ecs.createEntity();
try std.testing.expectEqual(0, entity);
try ecs.componentAddEntity(component.ComponentType.component_stub, entity, .{});
}

View file

@ -1,45 +0,0 @@
const std = @import("std");
pub const Entity = struct {
id: usize,
};
pub const EntityCollection = struct {
allocator: std.mem.Allocator,
next_id: usize,
entities: std.AutoHashMap(usize, Entity),
pub fn addEntity(self: *EntityCollection) !usize {
try self.entities.put(self.next_id, .{
.id = self.next_id,
});
self.next_id += 1;
return self.next_id - 1;
}
pub fn init(allocator: std.mem.Allocator) EntityCollection {
return .{
.allocator = allocator,
.next_id = 0,
.entities = std.AutoHashMap(usize, Entity).init(allocator),
};
}
pub fn deinit(self: *EntityCollection) void {
self.entities.deinit();
}
};
test "create entities" {
try std.testing.expect(true);
var ec = EntityCollection.init(std.testing.allocator);
defer ec.deinit();
for (0..128) |i| {
const id = ec.addEntity();
try std.testing.expectEqual(i, id);
}
}

View file

@ -1,8 +1,10 @@
const std = @import("std"); const std = @import("std");
const testing = std.testing; const testing = std.testing;
const entity = @import("ecs/entity.zig"); const component = @import("ecs/component.zig");
const ecs = @import("ecs/ecs.zig");
test { test {
_ = entity; _ = component;
_ = ecs;
} }