everything iterates backwards
This commit is contained in:
parent
e32e9069d8
commit
896095fc15
@ -6,35 +6,14 @@ const Storage = @import("registry.zig").Storage;
|
|||||||
const SparseSet = @import("sparse_set.zig").SparseSet;
|
const SparseSet = @import("sparse_set.zig").SparseSet;
|
||||||
const Entity = @import("registry.zig").Entity;
|
const Entity = @import("registry.zig").Entity;
|
||||||
|
|
||||||
/// BasicGroups do not own any components
|
/// BasicGroups do not own any components. Internally, they keep a SparseSet that is always kept up-to-date with the matching
|
||||||
|
/// entities.
|
||||||
pub const BasicGroup = struct {
|
pub const BasicGroup = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
registry: *Registry,
|
registry: *Registry,
|
||||||
group_data: *Registry.GroupData,
|
group_data: *Registry.GroupData,
|
||||||
|
|
||||||
// TODO: do we even need an iterator for a group?
|
|
||||||
pub const Iterator = struct {
|
|
||||||
index: usize = 0,
|
|
||||||
entities: []const Entity, // TODO: should this be a pointer to the slice?
|
|
||||||
|
|
||||||
pub fn init(entities: []const Entity) Iterator {
|
|
||||||
return .{ .entities = entities };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next(it: *Iterator) ?Entity {
|
|
||||||
if (it.index >= it.entities.len) return null;
|
|
||||||
|
|
||||||
it.index += 1;
|
|
||||||
return it.entities[it.index - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the iterator to the initial index
|
|
||||||
pub fn reset(it: *Iterator) void {
|
|
||||||
it.index = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init(registry: *Registry, group_data: *Registry.GroupData) Self {
|
pub fn init(registry: *Registry, group_data: *Registry.GroupData) Self {
|
||||||
return Self{
|
return Self{
|
||||||
.registry = registry,
|
.registry = registry,
|
||||||
@ -59,8 +38,10 @@ pub const BasicGroup = struct {
|
|||||||
return self.registry.assure(T).getConst(entity);
|
return self.registry.assure(T).getConst(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iterator(self: *Self) Iterator {
|
/// iterates the matched entities backwards, so the current entity can always be removed safely
|
||||||
return Iterator.init(self.group_data.entity_set.data());
|
/// and newly added entities wont affect it.
|
||||||
|
pub fn iterator(self: Self) utils.ReverseSliceIterator(Entity) {
|
||||||
|
return self.group_data.entity_set.reverseIterator();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,10 +50,13 @@ pub const OwningGroup = struct {
|
|||||||
group_data: *Registry.GroupData,
|
group_data: *Registry.GroupData,
|
||||||
super: *usize,
|
super: *usize,
|
||||||
|
|
||||||
|
/// iterator the provides the data from all the requested owned components in a single struct. Access to the current Entity
|
||||||
|
/// being iterated is available via the entity() method, useful for accessing non-owned component data. The get() method can
|
||||||
|
/// also be used to fetch non-owned component data for the currently iterated Entity.
|
||||||
fn Iterator(comptime Components: var) type {
|
fn Iterator(comptime Components: var) type {
|
||||||
return struct {
|
return struct {
|
||||||
index: usize = 0,
|
|
||||||
group: OwningGroup,
|
group: OwningGroup,
|
||||||
|
index: usize,
|
||||||
storage: *Storage(u1),
|
storage: *Storage(u1),
|
||||||
component_ptrs: [@typeInfo(Components).Struct.fields.len][*]u8,
|
component_ptrs: [@typeInfo(Components).Struct.fields.len][*]u8,
|
||||||
|
|
||||||
@ -87,17 +71,18 @@ pub const OwningGroup = struct {
|
|||||||
|
|
||||||
return .{
|
return .{
|
||||||
.group = group,
|
.group = group,
|
||||||
|
.index = group.group_data.current,
|
||||||
.storage = group.firstOwnedStorage(),
|
.storage = group.firstOwnedStorage(),
|
||||||
.component_ptrs = component_ptrs,
|
.component_ptrs = component_ptrs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(it: *@This()) ?Components {
|
pub fn next(it: *@This()) ?Components {
|
||||||
if (it.index >= it.group.group_data.current) return null;
|
if (it.index == 0) return null;
|
||||||
|
it.index -= 1;
|
||||||
|
|
||||||
const ent = it.storage.set.dense.items[it.index];
|
const ent = it.storage.set.dense.items[it.index];
|
||||||
const entity_index = it.storage.set.index(ent);
|
const entity_index = it.storage.set.index(ent);
|
||||||
it.index += 1;
|
|
||||||
|
|
||||||
// fill and return the struct
|
// fill and return the struct
|
||||||
var comps: Components = undefined;
|
var comps: Components = undefined;
|
||||||
@ -109,13 +94,17 @@ pub const OwningGroup = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn entity(it: @This()) Entity {
|
pub fn entity(it: @This()) Entity {
|
||||||
std.debug.assert(it.index > 0 and it.index <= it.group.group_data.current);
|
std.debug.assert(it.index >= 0 and it.index < it.group.group_data.current);
|
||||||
return it.storage.set.dense.items[it.index - 1];
|
return it.storage.set.dense.items[it.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(it: @This(), comptime T: type) *T {
|
||||||
|
return it.group.registry.get(T, it.entity());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the iterator to the initial index
|
// Reset the iterator to the initial index
|
||||||
pub fn reset(it: *@This()) void {
|
pub fn reset(it: *@This()) void {
|
||||||
it.index = 0;
|
it.index = it.group.group_data.current;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -204,8 +193,11 @@ pub const OwningGroup = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var storage = self.firstOwnedStorage();
|
var storage = self.firstOwnedStorage();
|
||||||
var index: usize = 0;
|
var index: usize = self.group_data.current;
|
||||||
while (index < self.group_data.current) : (index += 1) {
|
while (true) {
|
||||||
|
if (index == 0) return;
|
||||||
|
index -= 1;
|
||||||
|
|
||||||
const ent = storage.set.dense.items[index];
|
const ent = storage.set.dense.items[index];
|
||||||
const entity_index = storage.set.index(ent);
|
const entity_index = storage.set.index(ent);
|
||||||
|
|
||||||
@ -236,12 +228,16 @@ pub const OwningGroup = struct {
|
|||||||
return self.group_data.super == self.group_data.size;
|
return self.group_data.super == self.group_data.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns an iterator with optimized access to the Components. Note that Components should be a struct with
|
/// returns an iterator with optimized access to the owend Components. Note that Components should be a struct with
|
||||||
/// fields that are pointers to the component types that you want to fetch. Only types that are owned are valid!
|
/// fields that are pointers to the component types that you want to fetch. Only types that are owned are valid!
|
||||||
pub fn iterator(self: OwningGroup, comptime Components: var) Iterator(Components) {
|
pub fn iterator(self: OwningGroup, comptime Components: var) Iterator(Components) {
|
||||||
self.validate(Components);
|
self.validate(Components);
|
||||||
return Iterator(Components).init(self);
|
return Iterator(Components).init(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn entityIterator(self: OwningGroup) utils.ReverseSliceIterator(Entity) {
|
||||||
|
return utils.ReverseSliceIterator(Entity).init(self.firstOwnedStorage().set.dense.items[0..self.group_data.current]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test "BasicGroup creation/iteration" {
|
test "BasicGroup creation/iteration" {
|
||||||
@ -344,7 +340,7 @@ test "OwningGroup add/remove" {
|
|||||||
reg.add(e0, @as(u32, 55));
|
reg.add(e0, @as(u32, 55));
|
||||||
std.testing.expectEqual(group.len(), 1);
|
std.testing.expectEqual(group.len(), 1);
|
||||||
|
|
||||||
reg.remove(i32, e0);
|
reg.remove(u32, e0);
|
||||||
std.testing.expectEqual(group.len(), 0);
|
std.testing.expectEqual(group.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,20 +351,24 @@ test "OwningGroup iterate" {
|
|||||||
var e0 = reg.create();
|
var e0 = reg.create();
|
||||||
reg.add(e0, @as(i32, 44));
|
reg.add(e0, @as(i32, 44));
|
||||||
reg.add(e0, @as(u32, 55));
|
reg.add(e0, @as(u32, 55));
|
||||||
|
reg.add(e0, @as(u8, 11));
|
||||||
|
|
||||||
var e1 = reg.create();
|
var e1 = reg.create();
|
||||||
reg.add(e1, @as(i32, 666));
|
reg.add(e1, @as(i32, 666));
|
||||||
reg.add(e1, @as(u32, 999));
|
reg.add(e1, @as(u32, 999));
|
||||||
|
reg.add(e1, @as(f32, 55.5));
|
||||||
|
|
||||||
var group = reg.group(.{ i32, u32 }, .{}, .{});
|
var group = reg.group(.{ i32, u32 }, .{}, .{});
|
||||||
var iter = group.iterator(struct { int: *i32, uint: *u32 });
|
var iter = group.iterator(struct { int: *i32, uint: *u32 });
|
||||||
while (iter.next()) |item| {
|
while (iter.next()) |item| {
|
||||||
if (iter.entity() == 0) {
|
if (iter.entity() == e0) {
|
||||||
std.testing.expectEqual(item.int.*, 44);
|
std.testing.expectEqual(item.int.*, 44);
|
||||||
std.testing.expectEqual(item.uint.*, 55);
|
std.testing.expectEqual(item.uint.*, 55);
|
||||||
|
std.testing.expectEqual(iter.get(u8).*, 11);
|
||||||
} else {
|
} else {
|
||||||
std.testing.expectEqual(item.int.*, 666);
|
std.testing.expectEqual(item.int.*, 666);
|
||||||
std.testing.expectEqual(item.uint.*, 999);
|
std.testing.expectEqual(item.uint.*, 999);
|
||||||
|
std.testing.expectEqual(iter.get(f32).*, 55.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,11 +421,11 @@ test "multiple OwningGroups" {
|
|||||||
var group3 = reg.group(.{Sprite}, .{Renderable}, .{});
|
var group3 = reg.group(.{Sprite}, .{Renderable}, .{});
|
||||||
var group4 = reg.group(.{ Sprite, Transform }, .{Renderable}, .{});
|
var group4 = reg.group(.{ Sprite, Transform }, .{Renderable}, .{});
|
||||||
|
|
||||||
|
// ensure groups are ordered correctly internally
|
||||||
var last_size: u8 = 0;
|
var last_size: u8 = 0;
|
||||||
for (reg.groups.items) |grp| {
|
for (reg.groups.items) |grp| {
|
||||||
std.testing.expect(last_size <= grp.size);
|
std.testing.expect(last_size <= grp.size);
|
||||||
last_size = grp.size;
|
last_size = grp.size;
|
||||||
std.debug.warn("grp: {}\n", .{grp.size});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std.testing.expect(!reg.sortable(Sprite));
|
std.testing.expect(!reg.sortable(Sprite));
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const warn = std.debug.warn;
|
const warn = std.debug.warn;
|
||||||
|
const ReverseSliceIterator = @import("utils.zig").ReverseSliceIterator;
|
||||||
|
|
||||||
// TODO: fix entity_mask. it should come from EntityTraitsDefinition.
|
// TODO: fix entity_mask. it should come from EntityTraitsDefinition.
|
||||||
pub fn SparseSet(comptime SparseT: type) type {
|
pub fn SparseSet(comptime SparseT: type) type {
|
||||||
@ -168,6 +169,10 @@ pub fn SparseSet(comptime SparseT: type) type {
|
|||||||
self.sparse.items.len = 0;
|
self.sparse.items.len = 0;
|
||||||
self.dense.items.len = 0;
|
self.dense.items.len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reverseIterator(self: *Self) ReverseSliceIterator(SparseT) {
|
||||||
|
return ReverseSliceIterator(SparseT).init(self.dense.items);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +250,24 @@ test "data() synced" {
|
|||||||
std.testing.expectEqual(set.len(), set.data().len);
|
std.testing.expectEqual(set.len(), set.data().len);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "respect" {
|
test "iterate" {
|
||||||
|
var set = SparseSet(u32).initPtr(std.testing.allocator);
|
||||||
|
defer set.deinit();
|
||||||
|
|
||||||
|
set.add(0);
|
||||||
|
set.add(1);
|
||||||
|
set.add(2);
|
||||||
|
set.add(3);
|
||||||
|
|
||||||
|
var i: u32 = @intCast(u32, set.len()) - 1;
|
||||||
|
var iter = set.reverseIterator();
|
||||||
|
while (iter.next()) |entity| {
|
||||||
|
std.testing.expectEqual(i, entity);
|
||||||
|
if (i > 0) i -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "respect 1" {
|
||||||
var set1 = SparseSet(u32).initPtr(std.testing.allocator);
|
var set1 = SparseSet(u32).initPtr(std.testing.allocator);
|
||||||
defer set1.deinit();
|
defer set1.deinit();
|
||||||
|
|
||||||
@ -268,7 +290,7 @@ test "respect" {
|
|||||||
std.testing.expectEqual(set1.dense.items[1], set2.dense.items[2]);
|
std.testing.expectEqual(set1.dense.items[1], set2.dense.items[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "respect" {
|
test "respect 2" {
|
||||||
var set = SparseSet(u32).initPtr(std.testing.allocator);
|
var set = SparseSet(u32).initPtr(std.testing.allocator);
|
||||||
defer set.deinit();
|
defer set.deinit();
|
||||||
|
|
||||||
|
@ -23,6 +23,31 @@ pub const ErasedPtr = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn ReverseSliceIterator(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
slice: []T,
|
||||||
|
index: usize,
|
||||||
|
|
||||||
|
pub fn init(slice: []T) @This() {
|
||||||
|
return .{
|
||||||
|
.slice = slice,
|
||||||
|
.index = slice.len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(self: *@This()) ?T {
|
||||||
|
if (self.index == 0) return null;
|
||||||
|
self.index -= 1;
|
||||||
|
|
||||||
|
return self.slice[self.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(self: *@This()) void {
|
||||||
|
self.index = slice.len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// sorts items using lessThan and keeps sub_items with the same sort
|
/// sorts items using lessThan and keeps sub_items with the same sort
|
||||||
pub fn sortSub(comptime T1: type, comptime T2: type, items: []T1, sub_items: []T2, lessThan: fn (lhs: T1, rhs: T1) bool) void {
|
pub fn sortSub(comptime T1: type, comptime T2: type, items: []T1, sub_items: []T2, lessThan: fn (lhs: T1, rhs: T1) bool) void {
|
||||||
var i: usize = 1;
|
var i: usize = 1;
|
||||||
@ -81,3 +106,19 @@ pub fn isComptime(comptime T: type) bool {
|
|||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "ReverseSliceIterator" {
|
||||||
|
var slice = std.testing.allocator.alloc(usize, 10) catch unreachable;
|
||||||
|
defer std.testing.allocator.free(slice);
|
||||||
|
|
||||||
|
for (slice) |*item, i| {
|
||||||
|
item.* = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
var iter = ReverseSliceIterator(usize).init(slice);
|
||||||
|
var i: usize = 9;
|
||||||
|
while (iter.next()) |val| {
|
||||||
|
std.testing.expectEqual(i, val);
|
||||||
|
if (i > 0) i -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,7 +5,9 @@ const Registry = @import("registry.zig").Registry;
|
|||||||
const Storage = @import("registry.zig").Storage;
|
const Storage = @import("registry.zig").Storage;
|
||||||
const Entity = @import("registry.zig").Entity;
|
const Entity = @import("registry.zig").Entity;
|
||||||
|
|
||||||
/// single item view. Iterating raw() directly is the fastest way to get at the data.
|
/// single item view. Iterating raw() directly is the fastest way to get at the data. An iterator is also available to iterate
|
||||||
|
/// either the Entities or the Components. If T is sorted note that raw() will be in the reverse order so it should be looped
|
||||||
|
/// backwards. The iterators will return data in the sorted order though.
|
||||||
pub fn BasicView(comptime T: type) type {
|
pub fn BasicView(comptime T: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
@ -37,9 +39,17 @@ pub fn BasicView(comptime T: type) type {
|
|||||||
return self.storage.get(entity);
|
return self.storage.get(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getConst(self: *Self, comptime T: type, entity: Entity) T {
|
pub fn getConst(self: *Self, entity: Entity) T {
|
||||||
return self.storage.getConst(entity);
|
return self.storage.getConst(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn entityIterator(self: Self) utils.ReverseSliceIterator(Entity) {
|
||||||
|
return self.storage.set.reverseIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn componentIterator(self: Self) utils.ReverseSliceIterator(T) {
|
||||||
|
return utils.ReverseSliceIterator(T).init(self.storage.instances.items);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,21 +63,24 @@ pub fn MultiView(comptime n_includes: usize, comptime n_excludes: usize) type {
|
|||||||
|
|
||||||
pub const Iterator = struct {
|
pub const Iterator = struct {
|
||||||
view: *Self,
|
view: *Self,
|
||||||
index: usize = 0,
|
index: usize,
|
||||||
entities: *const []Entity,
|
entities: *const []Entity,
|
||||||
|
|
||||||
pub fn init(view: *Self) Iterator {
|
pub fn init(view: *Self) Iterator {
|
||||||
const ptr = view.registry.components.getValue(view.type_ids[0]).?;
|
const ptr = view.registry.components.getValue(view.type_ids[0]).?;
|
||||||
|
const entities = @intToPtr(*Storage(u8), ptr).dataPtr();
|
||||||
return .{
|
return .{
|
||||||
.view = view,
|
.view = view,
|
||||||
.entities = @intToPtr(*Storage(u8), ptr).dataPtr(),
|
.index = entities.len,
|
||||||
|
.entities = entities,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(it: *Iterator) ?Entity {
|
pub fn next(it: *Iterator) ?Entity {
|
||||||
if (it.index >= it.entities.len) return null;
|
while (true) blk: {
|
||||||
|
if (it.index == 0) return null;
|
||||||
|
it.index -= 1;
|
||||||
|
|
||||||
blk: while (it.index < it.entities.len) : (it.index += 1) {
|
|
||||||
const entity = it.entities.*[it.index];
|
const entity = it.entities.*[it.index];
|
||||||
|
|
||||||
// entity must be in all other Storages
|
// entity must be in all other Storages
|
||||||
@ -86,16 +99,13 @@ pub fn MultiView(comptime n_includes: usize, comptime n_excludes: usize) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it.index += 1;
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the iterator to the initial index
|
// Reset the iterator to the initial index
|
||||||
pub fn reset(it: *Iterator) void {
|
pub fn reset(it: *Iterator) void {
|
||||||
it.index = 0;
|
it.index = it.entities.len;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -147,6 +157,28 @@ test "single basic view" {
|
|||||||
|
|
||||||
store.remove(7);
|
store.remove(7);
|
||||||
std.testing.expectEqual(view.len(), 2);
|
std.testing.expectEqual(view.len(), 2);
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
var iter = view.componentIterator();
|
||||||
|
while (iter.next()) |comp| {
|
||||||
|
if (i == 0) std.testing.expectEqual(comp, 50);
|
||||||
|
if (i == 1) std.testing.expectEqual(comp, 30);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
var entIter = view.entityIterator();
|
||||||
|
while (entIter.next()) |ent| {
|
||||||
|
if (i == 0) {
|
||||||
|
std.testing.expectEqual(ent, 5);
|
||||||
|
std.testing.expectEqual(view.getConst(ent), 50);
|
||||||
|
}
|
||||||
|
if (i == 1) {
|
||||||
|
std.testing.expectEqual(ent, 3);
|
||||||
|
std.testing.expectEqual(view.getConst(ent), 30);
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "single basic view data" {
|
test "single basic view data" {
|
||||||
|
@ -66,11 +66,11 @@ test "nested OwningGroups entity order" {
|
|||||||
|
|
||||||
var sprite_store = reg.assure(Sprite);
|
var sprite_store = reg.assure(Sprite);
|
||||||
var transform_store = reg.assure(Transform);
|
var transform_store = reg.assure(Transform);
|
||||||
printStore(sprite_store, "Sprite");
|
// printStore(sprite_store, "Sprite");
|
||||||
|
|
||||||
reg.add(1, Transform{.x = 1});
|
reg.add(1, Transform{.x = 1});
|
||||||
|
|
||||||
printStore(sprite_store, "Sprite");
|
// printStore(sprite_store, "Sprite");
|
||||||
printStore(transform_store, "Transform");
|
// printStore(transform_store, "Transform");
|
||||||
warn("group2.current: {}\n", .{group2.group_data.current});
|
// warn("group2.current: {}\n", .{group2.group_data.current});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user