const std = @import("std"); const component = @import("component.zig"); const ComponentType = component.ComponentType; pub const EntityComponentSystem = struct { allocator: std.mem.Allocator, components: std.MultiArrayList(component.Components), available_ids: std.ArrayList(usize), next_id: usize, pub fn init(allocator: std.mem.Allocator) EntityComponentSystem { return EntityComponentSystem{ .allocator = allocator, .components = .{}, .available_ids = std.ArrayList(usize).init(allocator), .next_id = 0, }; } pub fn deinit(self: *EntityComponentSystem) void { self.available_ids.deinit(); self.components.deinit(self.allocator); } pub fn createEntity(self: *EntityComponentSystem) !usize { const reuse_id = self.available_ids.popOrNull(); if (reuse_id != null and reuse_id.? < self.components.items(.component_stub).len) { return reuse_id.?; } const id = self.next_id; try self.components.append(self.allocator, .{ .component_stub = null, }); self.next_id += 1; return id; } pub fn componentAddEntity( self: *EntityComponentSystem, comp: component.ComponentType, entity: usize, args: anytype, ) !void { switch (comp) { ComponentType.component_stub => self.components.items(.component_stub)[entity] = component.ComponentStub.init(args), } } pub fn deleteEntity(self: *EntityComponentSystem, entity: usize) !void { inline for (std.enums.valuesFromFields(ComponentType, std.meta.fields(ComponentType))) |comp| { self.componentRemoveEntity(comp, entity); } try self.available_ids.append(entity); } pub fn componentRemoveEntity( self: *EntityComponentSystem, comp: component.ComponentType, entity: usize, ) void { switch (comp) { ComponentType.component_stub => { var comp_opt = self.components.items(.component_stub)[entity]; if (comp_opt != null) comp_opt.?.deinit(); self.components.items(.component_stub)[entity] = null; }, } } }; test "add entities to components" { var ecs = EntityComponentSystem.init(std.testing.allocator); defer ecs.deinit(); for (0..100) |i| { const entity = try ecs.createEntity(); try std.testing.expectEqual(i, entity); try ecs.componentAddEntity(ComponentType.component_stub, entity, .{}); try std.testing.expect(ecs.components.items(.component_stub)[entity] != null); } } test "remove entities from components" { var ecs = EntityComponentSystem.init(std.testing.allocator); defer ecs.deinit(); const entity = try ecs.createEntity(); try ecs.componentAddEntity(ComponentType.component_stub, entity, .{}); const entity2 = try ecs.createEntity(); try ecs.componentAddEntity(ComponentType.component_stub, entity2, .{}); try std.testing.expect(ecs.components.items(.component_stub)[entity2] != null); ecs.componentRemoveEntity(ComponentType.component_stub, entity2); try std.testing.expect(ecs.components.items(.component_stub)[entity2] == null); } test "delete entities" { var ecs = EntityComponentSystem.init(std.testing.allocator); defer ecs.deinit(); const entity = try ecs.createEntity(); try ecs.componentAddEntity(ComponentType.component_stub, entity, .{}); const entity2 = try ecs.createEntity(); try ecs.componentAddEntity(ComponentType.component_stub, entity2, .{}); try std.testing.expect(ecs.components.items(.component_stub)[entity2] != null); try ecs.deleteEntity(entity2); try std.testing.expect(ecs.components.items(.component_stub)[entity2] == null); try std.testing.expectEqual(1, ecs.available_ids.items.len); } test "recycle entities" { var ecs = EntityComponentSystem.init(std.testing.allocator); defer ecs.deinit(); const entity = try ecs.createEntity(); try ecs.componentAddEntity(ComponentType.component_stub, entity, .{}); const entity2 = try ecs.createEntity(); try ecs.componentAddEntity(ComponentType.component_stub, entity2, .{}); try ecs.deleteEntity(entity2); try std.testing.expectEqual(1, ecs.available_ids.items.len); const entity3 = try ecs.createEntity(); try std.testing.expectEqual(1, entity3); try std.testing.expectEqual(0, ecs.available_ids.items.len); const entity4 = try ecs.createEntity(); try std.testing.expectEqual(2, entity4); try ecs.deleteEntity(entity); try std.testing.expectEqual(1, ecs.available_ids.items.len); const entity5 = try ecs.createEntity(); try std.testing.expectEqual(0, entity5); try std.testing.expectEqual(0, ecs.available_ids.items.len); try ecs.deleteEntity(entity5); try ecs.deleteEntity(entity4); try ecs.deleteEntity(entity3); try std.testing.expectEqual(3, ecs.available_ids.items.len); }