ecs: make [set/remove/get]Component globally type safe
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
1f7ea529f4
commit
25afe5ccf4
2 changed files with 51 additions and 28 deletions
|
|
@ -497,7 +497,14 @@ pub fn Entities(all_components: anytype) type {
|
|||
/// Sets the named component to the specified value for the given entity,
|
||||
/// moving the entity from it's current archetype table to the new archetype
|
||||
/// table if required.
|
||||
pub fn setComponent(entities: *Self, entity: EntityID, comptime name: []const u8, component: anytype) !void {
|
||||
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)),
|
||||
) !void {
|
||||
const name = std.meta.tagName(component_name);
|
||||
|
||||
var archetype = entities.archetypeByID(entity);
|
||||
|
||||
// Determine the old hash for the archetype.
|
||||
|
|
@ -595,7 +602,13 @@ pub fn Entities(all_components: anytype) type {
|
|||
|
||||
/// gets the named component of the given type (which must be correct, otherwise undefined
|
||||
/// behavior will occur). Returns null if the component does not exist on the entity.
|
||||
pub fn getComponent(entities: *Self, entity: EntityID, name: []const u8, comptime Component: type) ?Component {
|
||||
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);
|
||||
var archetype = entities.archetypeByID(entity);
|
||||
|
||||
const ptr = entities.entities.get(entity).?;
|
||||
|
|
@ -603,7 +616,12 @@ pub fn Entities(all_components: anytype) type {
|
|||
}
|
||||
|
||||
/// Removes the named component from the entity, or noop if it doesn't have such a component.
|
||||
pub fn removeComponent(entities: *Self, entity: EntityID, name: []const u8) !void {
|
||||
pub fn removeComponent(
|
||||
entities: *Self,
|
||||
entity: EntityID,
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(all_components)),
|
||||
) !void {
|
||||
const name = std.meta.tagName(component_name);
|
||||
var archetype = entities.archetypeByID(entity);
|
||||
if (!archetype.hasComponent(name)) return;
|
||||
|
||||
|
|
@ -712,45 +730,47 @@ test "entity ID size" {
|
|||
test "example" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
const all_components = .{};
|
||||
const Location = struct {
|
||||
x: f32 = 0,
|
||||
y: f32 = 0,
|
||||
z: f32 = 0,
|
||||
};
|
||||
|
||||
const Rotation = struct { degrees: f32 };
|
||||
|
||||
const all_components = .{
|
||||
.location = Location,
|
||||
.name = []const u8,
|
||||
.rotation = Rotation,
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Create a world.
|
||||
var world = try Entities(all_components).init(allocator);
|
||||
defer world.deinit();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Define component types, any Zig type will do!
|
||||
// A location component.
|
||||
const Location = struct {
|
||||
x: f32 = 0,
|
||||
y: f32 = 0,
|
||||
z: f32 = 0,
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Create first player entity.
|
||||
var player1 = try world.new();
|
||||
try world.setComponent(player1, "name", "jane"); // add Name component
|
||||
try world.setComponent(player1, "location", Location{}); // add Location component
|
||||
try world.setComponent(player1, .name, "jane"); // add Name component
|
||||
try world.setComponent(player1, .location, .{}); // add Location component
|
||||
|
||||
// Create second player entity.
|
||||
var player2 = try world.new();
|
||||
try testing.expect(world.getComponent(player2, "location", Location) == null);
|
||||
try testing.expect(world.getComponent(player2, "name", []const u8) == null);
|
||||
try testing.expect(world.getComponent(player2, .location) == null);
|
||||
try testing.expect(world.getComponent(player2, .name) == null);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// We can add new components at will.
|
||||
const Rotation = struct { degrees: f32 };
|
||||
try world.setComponent(player2, "rotation", Rotation{ .degrees = 90 });
|
||||
try testing.expect(world.getComponent(player1, "rotation", Rotation) == null); // player1 has no rotation
|
||||
try world.setComponent(player2, .rotation, .{ .degrees = 90 });
|
||||
try testing.expect(world.getComponent(player1, .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, .name);
|
||||
try world.removeComponent(player1, .location);
|
||||
try world.removeComponent(player1, .location); // doesn't exist? no problem.
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Introspect things.
|
||||
|
|
|
|||
|
|
@ -46,7 +46,10 @@ test "inclusion" {
|
|||
test "example" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
const all_components = .{};
|
||||
const all_components = .{
|
||||
.physics = u16,
|
||||
.geometry = u16,
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Create a world.
|
||||
|
|
@ -56,11 +59,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", @as(u16, 1234));
|
||||
try world.entities.setComponent(player1, "geometry", @as(u16, 1234));
|
||||
try world.entities.setComponent(player1, .physics, 1234);
|
||||
try world.entities.setComponent(player1, .geometry, 1234);
|
||||
|
||||
try world.entities.setComponent(player2, "physics", @as(u16, 1234));
|
||||
try world.entities.setComponent(player3, "physics", @as(u16, 1234));
|
||||
try world.entities.setComponent(player2, .physics, 1234);
|
||||
try world.entities.setComponent(player3, .physics, 1234);
|
||||
|
||||
const physics = (struct {
|
||||
pub fn physics(adapter: *Adapter(all_components)) void {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue