ecs: add namespaced components
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
87ecd8b29e
commit
5c4c2d3850
2 changed files with 52 additions and 35 deletions
|
|
@ -341,6 +341,7 @@ pub const void_archetype_hash = std.math.maxInt(u64);
|
|||
/// making lookup by entity ID a few cheap array indexing operations.
|
||||
/// * ComponentStorage(T) is a column of data within a table for a single type of component `T`.
|
||||
pub fn Entities(all_components: anytype) type {
|
||||
// TODO: validate all_components is a namespaced component set in the form we expect
|
||||
_ = all_components;
|
||||
return struct {
|
||||
allocator: Allocator,
|
||||
|
|
@ -500,10 +501,14 @@ pub fn Entities(all_components: anytype) type {
|
|||
pub fn setComponent(
|
||||
entities: *Self,
|
||||
entity: EntityID,
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(all_components)),
|
||||
component: @field(all_components, std.meta.tagName(component_name)),
|
||||
comptime namespace_name: std.meta.FieldEnum(@TypeOf(all_components)),
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(@field(all_components, @tagName(namespace_name)))),
|
||||
component: @field(
|
||||
@field(all_components, @tagName(namespace_name)),
|
||||
@tagName(component_name),
|
||||
),
|
||||
) !void {
|
||||
const name = std.meta.tagName(component_name);
|
||||
const name = @tagName(namespace_name) ++ "." ++ @tagName(component_name);
|
||||
|
||||
var archetype = entities.archetypeByID(entity);
|
||||
|
||||
|
|
@ -605,10 +610,17 @@ pub fn Entities(all_components: anytype) type {
|
|||
pub fn getComponent(
|
||||
entities: *Self,
|
||||
entity: EntityID,
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(all_components)),
|
||||
) ?@field(all_components, std.meta.tagName(component_name)) {
|
||||
const Component = comptime @field(all_components, std.meta.tagName(component_name));
|
||||
const name = std.meta.tagName(component_name);
|
||||
comptime namespace_name: std.meta.FieldEnum(@TypeOf(all_components)),
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(@field(all_components, @tagName(namespace_name)))),
|
||||
) ?@field(
|
||||
@field(all_components, @tagName(namespace_name)),
|
||||
@tagName(component_name),
|
||||
) {
|
||||
const Component = comptime @field(
|
||||
@field(all_components, @tagName(namespace_name)),
|
||||
@tagName(component_name),
|
||||
);
|
||||
const name = @tagName(namespace_name) ++ "." ++ @tagName(component_name);
|
||||
var archetype = entities.archetypeByID(entity);
|
||||
|
||||
const ptr = entities.entities.get(entity).?;
|
||||
|
|
@ -619,9 +631,10 @@ pub fn Entities(all_components: anytype) type {
|
|||
pub fn removeComponent(
|
||||
entities: *Self,
|
||||
entity: EntityID,
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(all_components)),
|
||||
comptime namespace_name: std.meta.FieldEnum(@TypeOf(all_components)),
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(@field(all_components, @tagName(namespace_name)))),
|
||||
) !void {
|
||||
const name = std.meta.tagName(component_name);
|
||||
const name = @tagName(namespace_name) ++ "." ++ @tagName(component_name);
|
||||
var archetype = entities.archetypeByID(entity);
|
||||
if (!archetype.hasComponent(name)) return;
|
||||
|
||||
|
|
@ -739,9 +752,11 @@ test "example" {
|
|||
const Rotation = struct { degrees: f32 };
|
||||
|
||||
const all_components = .{
|
||||
.location = Location,
|
||||
.name = []const u8,
|
||||
.rotation = Rotation,
|
||||
.game = .{
|
||||
.location = Location,
|
||||
.name = []const u8,
|
||||
.rotation = Rotation,
|
||||
},
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
@ -752,25 +767,25 @@ test "example" {
|
|||
//-------------------------------------------------------------------------
|
||||
// Create first player entity.
|
||||
var player1 = try world.new();
|
||||
try world.setComponent(player1, .name, "jane"); // add Name component
|
||||
try world.setComponent(player1, .location, .{}); // add Location component
|
||||
try world.setComponent(player1, .game, .name, "jane"); // add Name component
|
||||
try world.setComponent(player1, .game, .location, .{}); // add Location component
|
||||
|
||||
// Create second player entity.
|
||||
var player2 = try world.new();
|
||||
try testing.expect(world.getComponent(player2, .location) == null);
|
||||
try testing.expect(world.getComponent(player2, .name) == null);
|
||||
try testing.expect(world.getComponent(player2, .game, .location) == null);
|
||||
try testing.expect(world.getComponent(player2, .game, .name) == null);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// We can add new components at will.
|
||||
try world.setComponent(player2, .rotation, .{ .degrees = 90 });
|
||||
try testing.expect(world.getComponent(player1, .rotation) == null); // player1 has no rotation
|
||||
try world.setComponent(player2, .game, .rotation, .{ .degrees = 90 });
|
||||
try testing.expect(world.getComponent(player1, .game, .rotation) == null); // player1 has no rotation
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Remove a component from any entity at will.
|
||||
// TODO: add a way to "cleanup" truly unused archetypes
|
||||
try world.removeComponent(player1, .name);
|
||||
try world.removeComponent(player1, .location);
|
||||
try world.removeComponent(player1, .location); // doesn't exist? no problem.
|
||||
try world.removeComponent(player1, .game, .name);
|
||||
try world.removeComponent(player1, .game, .location);
|
||||
try world.removeComponent(player1, .game, .location); // doesn't exist? no problem.
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Introspect things.
|
||||
|
|
@ -780,10 +795,10 @@ test "example" {
|
|||
var archetypes = world.archetypes.keys();
|
||||
try testing.expectEqual(@as(usize, 6), archetypes.len);
|
||||
try testing.expectEqual(@as(u64, void_archetype_hash), archetypes[0]);
|
||||
try testing.expectEqual(@as(u64, 6893717443977936573), archetypes[1]);
|
||||
try testing.expectEqual(@as(u64, 6672640730301731073), archetypes[2]);
|
||||
try testing.expectEqual(@as(u64, 14420739110802803032), archetypes[3]);
|
||||
try testing.expectEqual(@as(u64, 18216325908396511299), archetypes[4]);
|
||||
try testing.expectEqual(@as(u64, 10567852867187873021), archetypes[1]);
|
||||
try testing.expectEqual(@as(u64, 14072552683119202344), archetypes[2]);
|
||||
try testing.expectEqual(@as(u64, 17945105277702244199), archetypes[3]);
|
||||
try testing.expectEqual(@as(u64, 12546098194442238762), archetypes[4]);
|
||||
try testing.expectEqual(@as(u64, 4457032469566706731), archetypes[5]);
|
||||
|
||||
// Number of (living) entities stored in an archetype table.
|
||||
|
|
@ -797,13 +812,13 @@ test "example" {
|
|||
// Components for a given archetype.
|
||||
var columns = world.archetypes.get(archetypes[2]).?.columns;
|
||||
try testing.expectEqual(@as(usize, 3), columns.len);
|
||||
try testing.expectEqualStrings("location", columns[0].name);
|
||||
try testing.expectEqualStrings("id", columns[1].name);
|
||||
try testing.expectEqualStrings("name", columns[2].name);
|
||||
try testing.expectEqualStrings("game.location", columns[0].name);
|
||||
try testing.expectEqualStrings("game.name", columns[1].name);
|
||||
try testing.expectEqualStrings("id", columns[2].name);
|
||||
|
||||
// Archetype resolved via entity ID
|
||||
var player2_archetype = world.archetypeByID(player2);
|
||||
try testing.expectEqual(@as(u64, 722178222806262412), player2_archetype.hash);
|
||||
try testing.expectEqual(@as(u64, 4263961864502127795), player2_archetype.hash);
|
||||
|
||||
// TODO: iterating components an entity has not currently supported.
|
||||
|
||||
|
|
|
|||
|
|
@ -47,8 +47,10 @@ test "example" {
|
|||
const allocator = testing.allocator;
|
||||
|
||||
const all_components = .{
|
||||
.physics = u16,
|
||||
.geometry = u16,
|
||||
.example = .{
|
||||
.physics = u16,
|
||||
.geometry = u16,
|
||||
},
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
@ -59,11 +61,11 @@ test "example" {
|
|||
const player1 = try world.entities.new();
|
||||
const player2 = try world.entities.new();
|
||||
const player3 = try world.entities.new();
|
||||
try world.entities.setComponent(player1, .physics, 1234);
|
||||
try world.entities.setComponent(player1, .geometry, 1234);
|
||||
try world.entities.setComponent(player1, .example, .physics, 1234);
|
||||
try world.entities.setComponent(player1, .example, .geometry, 1234);
|
||||
|
||||
try world.entities.setComponent(player2, .physics, 1234);
|
||||
try world.entities.setComponent(player3, .physics, 1234);
|
||||
try world.entities.setComponent(player2, .example, .physics, 1234);
|
||||
try world.entities.setComponent(player3, .example, .physics, 1234);
|
||||
|
||||
const physics = (struct {
|
||||
pub fn physics(adapter: *Adapter(all_components)) void {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue