aboutsummaryrefslogtreecommitdiff
path: root/src/ecs
diff options
context:
space:
mode:
authorjjanzen <jjanzen@jjanzen.ca>2025-01-21 19:20:50 -0600
committerjjanzen <jjanzen@jjanzen.ca>2025-01-21 19:20:50 -0600
commit8a20d05a7a8cf9f1339142240907ed158449dfa0 (patch)
treebabbf74a7bea83589b05f117a795235deb0e238c /src/ecs
parent9f18b702b15b4a26a016170a8a2dfb216ae15810 (diff)
add basic entity component system
Diffstat (limited to 'src/ecs')
-rw-r--r--src/ecs/component.zig39
-rw-r--r--src/ecs/ecs.zig84
-rw-r--r--src/ecs/entity.zig45
3 files changed, 123 insertions, 45 deletions
diff --git a/src/ecs/component.zig b/src/ecs/component.zig
new file mode 100644
index 0000000..4a10172
--- /dev/null
+++ b/src/ecs/component.zig
@@ -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();
+}
diff --git a/src/ecs/ecs.zig b/src/ecs/ecs.zig
new file mode 100644
index 0000000..542de34
--- /dev/null
+++ b/src/ecs/ecs.zig
@@ -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, .{});
+}
diff --git a/src/ecs/entity.zig b/src/ecs/entity.zig
deleted file mode 100644
index 2ad8556..0000000
--- a/src/ecs/entity.zig
+++ /dev/null
@@ -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);
- }
-}