mirror of
https://github.com/Ukendio/jecs.git
synced 2025-06-20 08:19:18 +00:00
Improve deletion
This commit is contained in:
parent
cc7daa6a06
commit
44534b7c81
5 changed files with 199 additions and 34 deletions
67
demo/src/StarterPlayer/StarterPlayerScripts/systems/lol.luau
Normal file
67
demo/src/StarterPlayer/StarterPlayerScripts/systems/lol.luau
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
--!optimize 2
|
||||||
|
--!native
|
||||||
|
--!strict
|
||||||
|
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
|
local __ = jecs.Wildcard
|
||||||
|
local std = ReplicatedStorage.std
|
||||||
|
|
||||||
|
local world = require(std.world)
|
||||||
|
|
||||||
|
local Position = world:component() :: jecs.Entity<vector>
|
||||||
|
local Previous = jecs.Rest
|
||||||
|
local pre = jecs.pair(Position, Previous)
|
||||||
|
|
||||||
|
local added = world
|
||||||
|
:query(Position)
|
||||||
|
:without(pre)
|
||||||
|
:cached()
|
||||||
|
local changed = world
|
||||||
|
:query(Position, pre)
|
||||||
|
:cached()
|
||||||
|
local removed = world
|
||||||
|
:query(pre)
|
||||||
|
:without(Position)
|
||||||
|
:cached()
|
||||||
|
|
||||||
|
local children = {}
|
||||||
|
for i = 1, 10 do
|
||||||
|
local e = world:entity()
|
||||||
|
world:set(e, Position, vector.create(i, i, i))
|
||||||
|
table.insert(children, e)
|
||||||
|
end
|
||||||
|
local function flip()
|
||||||
|
return math.random() > 0.5
|
||||||
|
end
|
||||||
|
local function system()
|
||||||
|
for i, child in children do
|
||||||
|
world:set(child, Position, vector.create(i,i,i))
|
||||||
|
end
|
||||||
|
for e, p in added:iter() do
|
||||||
|
world:set(e, pre, p)
|
||||||
|
end
|
||||||
|
for i, child in children do
|
||||||
|
if flip() then
|
||||||
|
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for e, new, old in changed:iter() do
|
||||||
|
if new ~= old then
|
||||||
|
world:set(e, pre, new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i, child in children do
|
||||||
|
world:remove(child, Position)
|
||||||
|
end
|
||||||
|
|
||||||
|
for e in removed:iter() do
|
||||||
|
world:remove(e, pre)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local scheduler = require(std.scheduler)
|
||||||
|
|
||||||
|
scheduler.SYSTEM(system)
|
||||||
|
|
||||||
|
return 0
|
|
@ -0,0 +1,90 @@
|
||||||
|
--!optimize 2
|
||||||
|
--!native
|
||||||
|
--!strict
|
||||||
|
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local jecs = require(ReplicatedStorage.ecs)
|
||||||
|
local __ = jecs.Wildcard
|
||||||
|
local std = ReplicatedStorage.std
|
||||||
|
|
||||||
|
local world = require(std.world)
|
||||||
|
|
||||||
|
local Position = world:component() :: jecs.Entity<vector>
|
||||||
|
local Previous = jecs.Rest
|
||||||
|
local pre = jecs.pair(Position, Previous)
|
||||||
|
|
||||||
|
local added = world
|
||||||
|
:query(Position)
|
||||||
|
:without(pre)
|
||||||
|
:cached()
|
||||||
|
local changed = world
|
||||||
|
:query(Position, pre)
|
||||||
|
:cached()
|
||||||
|
local removed = world
|
||||||
|
:query(pre)
|
||||||
|
:without(Position)
|
||||||
|
:cached()
|
||||||
|
|
||||||
|
local children = {}
|
||||||
|
for i = 1, 10 do
|
||||||
|
local e = world:entity()
|
||||||
|
world:set(e, Position, vector.create(i, i, i))
|
||||||
|
table.insert(children, e)
|
||||||
|
end
|
||||||
|
local function flip()
|
||||||
|
return math.random() > 0.5
|
||||||
|
end
|
||||||
|
local entity_index = world.entity_index
|
||||||
|
local function copy(archetypes, id)
|
||||||
|
for _, archetype in archetypes do
|
||||||
|
|
||||||
|
local to = jecs.archetype_traverse_add(world, pre, archetype)
|
||||||
|
local columns = to.columns
|
||||||
|
local records = to.records
|
||||||
|
local old = columns[records[pre].column]
|
||||||
|
local new = columns[records[id].column]
|
||||||
|
|
||||||
|
if to ~= archetype then
|
||||||
|
for _, entity in archetype.entities do
|
||||||
|
local r = jecs.entity_index_try_get_fast(entity_index, entity)
|
||||||
|
jecs.entity_move(entity_index, entity, r, to)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.move(new, 1, #new, 1, old)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function system2()
|
||||||
|
for i, child in children do
|
||||||
|
world:set(child, Position, vector.create(i,i,i))
|
||||||
|
end
|
||||||
|
for e, p in added:iter() do
|
||||||
|
end
|
||||||
|
copy(added:archetypes(), Position)
|
||||||
|
for i, child in children do
|
||||||
|
if flip() then
|
||||||
|
world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for e, new, old in changed:iter() do
|
||||||
|
if new ~= old then
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
copy(changed:archetypes(), Position)
|
||||||
|
|
||||||
|
for i, child in children do
|
||||||
|
world:remove(child, Position)
|
||||||
|
end
|
||||||
|
|
||||||
|
for e in removed:iter() do
|
||||||
|
world:remove(e, pre)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local scheduler = require(std.scheduler)
|
||||||
|
|
||||||
|
scheduler.SYSTEM(system2)
|
||||||
|
|
||||||
|
return 0
|
|
@ -1,4 +1,5 @@
|
||||||
local jecs = require("@jecs")
|
local jecs = require("@jecs")
|
||||||
|
local pair = jecs.pair
|
||||||
|
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
local Name = world:component()
|
local Name = world:component()
|
||||||
|
@ -12,39 +13,36 @@ local function name(e)
|
||||||
return world:get(e, Name)
|
return world:get(e, Name)
|
||||||
end
|
end
|
||||||
|
|
||||||
local Position = named(world.component, "Position") :: jecs.Entity<Vector3>
|
local Position = named(world.component, "Position") :: jecs.Entity<vector>
|
||||||
local Previous = jecs.Rest
|
local Previous = jecs.Rest
|
||||||
local PreviousPosition = jecs.pair(Previous, Position)
|
|
||||||
|
|
||||||
local added = world
|
local added = world
|
||||||
:query(Position)
|
:query(Position)
|
||||||
:without(PreviousPosition)
|
:without(pair(Previous, Position))
|
||||||
:cached()
|
:cached()
|
||||||
local changed = world
|
local changed = world
|
||||||
:query(Position, PreviousPosition)
|
:query(Position, pair(Previous, Position))
|
||||||
:cached()
|
:cached()
|
||||||
local removed = world
|
local removed = world
|
||||||
:query(PreviousPosition)
|
:query(pair(Previous, Position))
|
||||||
:without(Position)
|
:without(Position)
|
||||||
:cached()
|
:cached()
|
||||||
|
|
||||||
local e1 = named(world.entity, "e1")
|
local e1 = named(world.entity, "e1")
|
||||||
world:set(e1, Position, Vector3.new(10, 20, 30))
|
world:set(e1, Position, vector.create(10, 20, 30))
|
||||||
|
|
||||||
local e2 = named(world.entity, "e2")
|
local e2 = named(world.entity, "e2")
|
||||||
world:set(e2, Position, Vector3.new(10, 20, 30))
|
world:set(e2, Position, vector.create(10, 20, 30))
|
||||||
|
for entity, p in added do
|
||||||
for e, p in added:iter() do
|
print(`Added {name(entity)}: \{{p.x}, {p.y}, {p.z}}`)
|
||||||
print(`Added {e}: \{{p.X}, {p.Y}, {p.Z}}`)
|
world:set(entity, pair(Previous, Position), p)
|
||||||
world:set(e, PreviousPosition, p)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
world:set(e1, Position, "")
|
world:set(e1, Position, vector.create(999, 999, 1998))
|
||||||
|
|
||||||
for e, new, old in changed:iter() do
|
for e, new, old in changed do
|
||||||
if new ~= old then
|
if new ~= old then
|
||||||
print(`{name(new)}'s Position changed from \{{old.X}, {old.Y}, {old.Z}\} to \{{new.X}, {new.Y}, {new.Z}\}`)
|
print(`{name(e)}'s Position changed from \{{old.x}, {old.y}, {old.z}\} to \{{new.x}, {new.y}, {new.z}\}`)
|
||||||
world:set(e, PreviousPosition, new)
|
world:set(e, pair(Previous, Position), new)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,6 +50,7 @@ world:remove(e2, Position)
|
||||||
|
|
||||||
for e in removed:iter() do
|
for e in removed:iter() do
|
||||||
print(`Position was removed from {name(e)}`)
|
print(`Position was removed from {name(e)}`)
|
||||||
|
world:remove(e, pair(Previous, Position))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Output:
|
-- Output:
|
||||||
|
|
26
jecs.luau
26
jecs.luau
|
@ -1144,6 +1144,9 @@ do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local sparse_array = entity_index.sparse_array
|
||||||
|
local dense_array = entity_index.dense_array
|
||||||
|
|
||||||
if idr_t then
|
if idr_t then
|
||||||
for archetype_id in idr_t.cache do
|
for archetype_id in idr_t.cache do
|
||||||
local children = {}
|
local children = {}
|
||||||
|
@ -1174,24 +1177,14 @@ do
|
||||||
else
|
else
|
||||||
local on_remove = id_record.hooks.on_remove
|
local on_remove = id_record.hooks.on_remove
|
||||||
local to = archetype_traverse_remove(world, id, idr_t_archetype)
|
local to = archetype_traverse_remove(world, id, idr_t_archetype)
|
||||||
|
local empty = #to.types == 0
|
||||||
|
for i = n, 1, -1 do
|
||||||
|
local child = children[i]
|
||||||
if on_remove then
|
if on_remove then
|
||||||
if to then
|
|
||||||
for i = n, 1, -1 do
|
|
||||||
local child = children[i]
|
|
||||||
on_remove(children[i])
|
|
||||||
local r = entity_index_try_get_fast(entity_index, child) :: Record
|
|
||||||
entity_move(entity_index, child, r, to)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for i = n, 1, -1 do
|
|
||||||
local child = children[i]
|
|
||||||
on_remove(child)
|
on_remove(child)
|
||||||
end
|
end
|
||||||
end
|
local r = sparse_array[ECS_ENTITY_T_LO(child)]
|
||||||
elseif to then
|
if not empty then
|
||||||
for i = n, 1, -1 do
|
|
||||||
local child = children[i]
|
|
||||||
local r = entity_index_try_get_fast(entity_index, child) :: Record
|
|
||||||
entity_move(entity_index, child, r, to)
|
entity_move(entity_index, child, r, to)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1203,7 +1196,6 @@ do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local dense_array = entity_index.dense_array
|
|
||||||
local index_of_deleted_entity = record.dense
|
local index_of_deleted_entity = record.dense
|
||||||
local index_of_last_alive_entity = entity_index.alive_count
|
local index_of_last_alive_entity = entity_index.alive_count
|
||||||
entity_index.alive_count = index_of_last_alive_entity - 1
|
entity_index.alive_count = index_of_last_alive_entity - 1
|
||||||
|
@ -2362,6 +2354,8 @@ return {
|
||||||
archetype_traverse_add = archetype_traverse_add,
|
archetype_traverse_add = archetype_traverse_add,
|
||||||
archetype_traverse_remove = archetype_traverse_remove,
|
archetype_traverse_remove = archetype_traverse_remove,
|
||||||
|
|
||||||
|
entity_move = entity_move,
|
||||||
|
|
||||||
entity_index_try_get = entity_index_try_get,
|
entity_index_try_get = entity_index_try_get,
|
||||||
entity_index_try_get_any = entity_index_try_get_any,
|
entity_index_try_get_any = entity_index_try_get_any,
|
||||||
entity_index_try_get_fast = entity_index_try_get_fast,
|
entity_index_try_get_fast = entity_index_try_get_fast,
|
||||||
|
|
|
@ -1045,6 +1045,21 @@ TEST("world:component()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:delete", function()
|
TEST("world:delete", function()
|
||||||
|
do CASE "delete recycled entity id used as component"
|
||||||
|
local world = world_new()
|
||||||
|
local id = world:entity()
|
||||||
|
world:add(id, jecs.Component)
|
||||||
|
|
||||||
|
local e = world:entity()
|
||||||
|
world:set(e, id, 1)
|
||||||
|
CHECK(world:get(e, id) == 1)
|
||||||
|
world:delete(id)
|
||||||
|
local recycled = world:entity()
|
||||||
|
world:add(recycled, jecs.Component)
|
||||||
|
world:set(e, recycled, 1)
|
||||||
|
CHECK(world:has(recycled, jecs.Component))
|
||||||
|
CHECK(world:get(e, recycled) == 1)
|
||||||
|
end
|
||||||
do
|
do
|
||||||
CASE("bug: Empty entity does not respect cleanup policy")
|
CASE("bug: Empty entity does not respect cleanup policy")
|
||||||
local world = world_new()
|
local world = world_new()
|
||||||
|
|
Loading…
Reference in a new issue