rigorous ecs implementation
This commit is contained in:
parent
56bb712c67
commit
eb2e8d13fa
2 changed files with 105 additions and 65 deletions
|
@ -11,29 +11,10 @@ pub const ComponentStub = struct {
|
|||
}
|
||||
};
|
||||
|
||||
pub const ComponentType = enum(usize) {
|
||||
component_stub = 0,
|
||||
pub const ComponentType = enum {
|
||||
component_stub,
|
||||
};
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
pub const Components = struct {
|
||||
component_stub: ?ComponentStub,
|
||||
};
|
||||
|
||||
test "stub component" {
|
||||
var stub = Component.init(ComponentType.component_stub, .{});
|
||||
defer stub.deinit();
|
||||
}
|
||||
|
|
143
src/ecs/ecs.zig
143
src/ecs/ecs.zig
|
@ -1,62 +1,37 @@
|
|||
const std = @import("std");
|
||||
const component = @import("component.zig");
|
||||
|
||||
const ComponentList = struct {
|
||||
entities: std.AutoHashMap(usize, component.Component),
|
||||
};
|
||||
const ComponentType = component.ComponentType;
|
||||
|
||||
pub const EntityComponentSystem = struct {
|
||||
entities: std.ArrayList(usize),
|
||||
components: std.AutoHashMap(usize, ComponentList),
|
||||
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 {
|
||||
var ecs = EntityComponentSystem{
|
||||
.entities = std.ArrayList(usize).init(allocator),
|
||||
.components = std.AutoHashMap(usize, ComponentList).init(allocator),
|
||||
pub fn init(allocator: std.mem.Allocator) EntityComponentSystem {
|
||||
return EntityComponentSystem{
|
||||
.allocator = allocator,
|
||||
.components = .{},
|
||||
.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();
|
||||
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.entities.items.len) {
|
||||
self.entities.items[reuse_id.?] = reuse_id.?;
|
||||
if (reuse_id != null and reuse_id.? < self.components.items(.component_stub).len) {
|
||||
return reuse_id.?;
|
||||
}
|
||||
|
||||
const id = self.next_id;
|
||||
try self.entities.append(id);
|
||||
try self.components.append(self.allocator, .{
|
||||
.component_stub = null,
|
||||
});
|
||||
self.next_id += 1;
|
||||
return id;
|
||||
}
|
||||
|
@ -67,18 +42,102 @@ pub const EntityComponentSystem = struct {
|
|||
entity: usize,
|
||||
args: anytype,
|
||||
) !void {
|
||||
var comp_list = self.components.getPtr(@intFromEnum(comp)) orelse return;
|
||||
switch (comp) {
|
||||
ComponentType.component_stub => self.components.items(.component_stub)[entity] = component.ComponentStub.init(args),
|
||||
}
|
||||
}
|
||||
|
||||
try comp_list.entities.put(entity, component.Component.init(comp, 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 = try EntityComponentSystem.init(std.testing.allocator);
|
||||
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 std.testing.expectEqual(0, entity);
|
||||
try ecs.componentAddEntity(ComponentType.component_stub, entity, .{});
|
||||
const entity2 = try ecs.createEntity();
|
||||
try ecs.componentAddEntity(ComponentType.component_stub, entity2, .{});
|
||||
|
||||
try ecs.componentAddEntity(component.ComponentType.component_stub, entity, .{});
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue