summaryrefslogtreecommitdiff
path: root/src/ecs/components.zig
blob: bb333c90280436bc2494d088757d0b072da5b7eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
const std = @import("std");
const Allocator = std.mem.Allocator;

pub const Position = struct {
    x: f32,
    y: f32,
    z: f32,
};

pub const Speed = struct {
    speed: f32,
};

pub const Pool = struct {
    comptime sets_map: std.StringHashMap([]const u8) = std.StringHashMap([]const u8).init(allocator),
    speed_set: SparseSet(Speed),
    allocator: Allocator,
    free_ids: std.ArrayList(usize),
    entities: usize,

    pub fn init(allocator: Allocator) !Pool {
        try sets_map.put(@typeName(Speed), "speed_set");

        return Pool{
            .speed_set = try SparseSet(Speed).init(allocator),
            .allocator = allocator,
            .free_ids = try std.ArrayList(usize).initCapacity(allocator, 100),
            .entities = 0,
        };
    }

    pub fn deinit(self: *Pool) void {
        self.sets_map.deinit();
        self.speed_set.deinit();
    }

    pub fn addComponent(self: *Pool, comptime T: type, id: usize, component: T) void {
        var set = @field(self, try self.sets_map.get(@typeName(T)));

        set.insert(id, component);
    }

    pub fn insert(self: *Pool) !usize {
        const id = self.free_ids.pop() orelse self.entities;

        self.entities += 1;
        return id;
    }

    pub fn remove(self: *Pool, id: usize) !usize {
        if (self.speed_set.hasComponent(id)) {
            self.speed_set.remove(id);
        }

        self.entities -= 1;
        self.free_ids.append(id);
    }
};

pub fn SparseSet(comptime T: type) type {
    return struct {
        sparse: std.ArrayList(usize),
        dense: std.ArrayList(usize),
        components: std.ArrayList(T),

        pub fn init(allocator: Allocator) !@This() {
            return @This(){
                .sparse = try std.ArrayList(usize).initCapacity(allocator, 10),
                .dense = try std.ArrayList(usize).initCapacity(allocator, 10),
                .components = try std.ArrayList(T).initCapacity(allocator, 10),
            };
        }

        pub fn deinit(self: *@This()) void {
            self.sparse.deinit();
            self.dense.deinit();
            self.components.deinit();
        }

        pub fn hasComponent(self: *@This(), id: usize) bool {
            return self.dense.items[self.sparse.items[id]] == id;
        }

        pub fn insert(self: *@This(), id: usize, component: T) !void {
            const dense_index = self.dense.items.len;
            try self.dense.append(id);
            try self.components.append(component);
            try self.sparse.append(dense_index);
        }

        pub fn remove(self: *@This(), id: usize) !void {
            const index = self.sparse.items[id];
            const last = self.dense.getLast();
            self.sparse.items[last] = index;
            _ = self.dense.swapRemove(index);
            _ = self.components.swapRemove(index);
        }
    };
}

pub fn test_sparse() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    var pool = try Pool.init(allocator);
    defer pool.deinit();

    const entity = try pool.insert();
    std.debug.print("new entity: {d}\n", .{entity});
    pool.addComponent(Speed, entity, .{ .speed = 5.0 });
}