diff --git a/src/components.zig b/src/components.zig new file mode 100644 index 0000000..9b94985 --- /dev/null +++ b/src/components.zig @@ -0,0 +1,89 @@ +const std = @import("std"); +const err = @import("error.zig"); +const Game = @import("game.zig"); + +pub const StorageOptions = struct { + min_capacity: usize = 32, +}; + +pub fn Storage(comptime T: type, comptime options: StorageOptions) type { + return struct { + pub const Key = packed struct { + cell: u32, + version: u32, + }; + pub const MIN_CAPACITY = options.min_capacity; + + const Array = std.ArrayListUnmanaged; + const Cell = struct { + component: T, + version: u32, + count: u32, + }; + + components: Array(Cell), + + const Self = @This(); + pub fn init() Self { + return .{ + .components = Array(Cell).empty, + }; + } + pub fn deinit(self: *Self) void { + self.components.deinit(Game.alloc); + } + pub fn add(self: *Self, component: T) Key { + for (0.., self.components.items) |i, *cell| { + if (cell.count == 0) { + return populateCell(cell, i, component); + } + } + const cell = self.components.addOne(Game.alloc) catch err.oom(); + return populateCell(cell, self.components.items.len - 1, component); + } + pub fn lock(self: *Self, key: Key) void { + self.components.items[key.cell].count += 1; + } + pub fn get(self: *Self, key: Key) ?*T { + const cell = &self.components.items[key.cell]; + if (cell.version == key.version) { + return &cell.component; + } + return null; + } + pub fn free(self: *Self, key: Key) bool { + self.components.items[key.cell].count -= 1; + } + pub fn iter(self: *Self) Iterator { + return .{ + .array = self.components.items, + .current = 0, + }; + } + + fn populateCell(cell: *Cell, index: usize, component: T) Key { + cell.version += 1; + cell.count = 1; + cell.component = component; + return .{ + .cell = @intCast(index), + .version = cell.version, + }; + } + + pub const Iterator = struct { + array: []Cell, + current: usize, + + pub fn next(self: *@This()) ?*T { + while (self.current < self.array.len) { + defer self.current += 1; + if (self.array[self.current].count != 0) { + return &self.array[self.current].component; + } + } + return null; + } + }; + }; +} diff --git a/src/game.zig b/src/game.zig index 44bd26c..bbacff8 100644 --- a/src/game.zig +++ b/src/game.zig @@ -13,7 +13,7 @@ const Time = struct { now: sdl.Time, }; -var alloc: std.mem.Allocator = undefined; +pub var alloc: std.mem.Allocator = undefined; var running: bool = false; var time: Time = .{ .delta = 0, .now = 0 }; diff --git a/src/world.zig b/src/world.zig index 1570fd2..60dae8b 100644 --- a/src/world.zig +++ b/src/world.zig @@ -1,10 +1,11 @@ const Graphics = @import("graphics.zig"); const Entity = @import("entity.zig"); const Time = @import("time.zig"); +const comp = @import("components.zig"); pub var time: Time = undefined; var next_stop: Time = undefined; -var entities: [16]?Entity = undefined; +var entities: comp.Storage(Entity, .{}) = undefined; pub var plane_mesh: Graphics.Mesh = undefined; pub var cube_mesh: Graphics.Mesh = undefined; @@ -12,25 +13,25 @@ pub var texture: Graphics.Texture = undefined; const World = @This(); pub fn initDebug() void { - entities = .{null} ** 16; - entities[0] = .{ + entities = comp.Storage(Entity, .{}).init(); + _ = entities.add(.{ .position = .{ 0, 0 }, .player = true, - }; - entities[1] = .{ + }); + _ = entities.add(.{ .position = .{ 2, 0 }, .enemy = true, .controller = .{ .move_units = 0.25, }, - }; - entities[2] = .{ + }); + _ = entities.add(.{ .position = .{ 3, 0 }, .enemy = true, .controller = .{ .move_units = 0.25, }, - }; + }); time = Time.ZERO; World.plane_mesh = Graphics.loadMesh(@ptrCast(&PLANE_MESH_DATA)); World.cube_mesh = Graphics.loadMesh(@ptrCast(&CUBE_MESH_DATA)); @@ -41,6 +42,7 @@ pub fn deinit() void { Graphics.unloadMesh(World.plane_mesh); Graphics.unloadMesh(World.cube_mesh); Graphics.unloadTexture(World.texture); + World.entities.deinit(); } pub fn updateReal(delta: f32) void { @@ -49,16 +51,18 @@ pub fn updateReal(delta: f32) void { const current = Time.earliest(World.next_stop, update_until); defer World.time = current; - for (&World.entities) |*entity| { - if (entity.*) |*e| e.update(); + var iter = World.entities.iter(); + while (iter.next()) |entity| { + entity.update(); } } } pub fn draw(delta: f32) void { Graphics.drawMesh(World.plane_mesh, World.texture, .{ .scale = @splat(5) }); - for (&World.entities) |*entity| { - if (entity.*) |*e| e.draw(delta); + var iter = World.entities.iter(); + while (iter.next()) |entity| { + entity.draw(delta); } } @@ -67,11 +71,10 @@ pub fn requestUpdate(at: Time) void { } pub fn entityAt(position: @Vector(2, i32)) ?*Entity { - for (&World.entities) |*maybe_entity| { - if (maybe_entity.*) |*entity| { - if (@reduce(.And, entity.position == position)) - return entity; - } + var iter = World.entities.iter(); + while (iter.next()) |entity| { + if (@reduce(.And, entity.position == position)) + return entity; } return null; } @@ -81,11 +84,10 @@ pub fn isFree(position: @Vector(2, i32)) bool { } pub fn getPlayer() ?*Entity { - for (&World.entities) |*maybe_entity| { - if (maybe_entity.*) |*entity| { - if (entity.player) - return entity; - } + var iter = World.entities.iter(); + while (iter.next()) |entity| { + if (entity.player) + return entity; } return null; }