mirror of
https://github.com/Keyslam-Group/Concord.git
synced 2025-09-02 04:13:58 -04:00
Streamline entity lifetime
This commit is contained in:
parent
bc47eaa651
commit
038111d558
7 changed files with 117 additions and 163 deletions
37
main.lua
37
main.lua
|
@ -22,20 +22,49 @@ local test_comp_3 = Concord.component("test_comp_3", function(e, b)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local test_system = Concord.system({Component.test_comp_1})
|
local test_system = Concord.system({Component.test_comp_1})
|
||||||
function test_system:update(dt)
|
|
||||||
print(#self.pool)
|
function onEntityAdded(e)
|
||||||
|
print("Added")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function onEntityRemoved(e)
|
||||||
|
print("Removed")
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_system:init()
|
||||||
|
self.pool.onEntityAdded = onEntityAdded
|
||||||
|
self.pool.onEntityRemoved = onEntityRemoved
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_system:update(dt)
|
||||||
|
--print(#self.pool)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local world = Concord.world()
|
local world = Concord.world()
|
||||||
|
|
||||||
local entity = Concord.entity()
|
local entity = Concord.entity()
|
||||||
entity:give(Component.test_comp_2, 100, 100)
|
entity:give(Component.test_comp_1, 100, 100)
|
||||||
entity:apply()
|
|
||||||
|
|
||||||
world:addEntity(entity)
|
world:addEntity(entity)
|
||||||
|
|
||||||
world:addSystem(test_system(), "update")
|
world:addSystem(test_system(), "update")
|
||||||
|
|
||||||
function love.update(dt)
|
function love.update(dt)
|
||||||
|
world:flush()
|
||||||
|
|
||||||
world:emit("update", dt)
|
world:emit("update", dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.keypressed(key)
|
||||||
|
if key == "q" then
|
||||||
|
entity:remove(Component.test_comp_1)
|
||||||
|
end
|
||||||
|
if key == "w" then
|
||||||
|
entity:give(Component.test_comp_1)
|
||||||
|
end
|
||||||
|
if key == "e" then
|
||||||
|
world:removeEntity(entity)
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -51,4 +51,4 @@ return setmetatable(Component, {
|
||||||
__call = function(_, ...)
|
__call = function(_, ...)
|
||||||
return Component.new(...)
|
return Component.new(...)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
|
@ -3,7 +3,6 @@
|
||||||
local PATH = (...):gsub('%.[^%.]+$', '')
|
local PATH = (...):gsub('%.[^%.]+$', '')
|
||||||
|
|
||||||
local Type = require(PATH..".type")
|
local Type = require(PATH..".type")
|
||||||
local List = require(PATH..".list")
|
|
||||||
|
|
||||||
local Entity = {}
|
local Entity = {}
|
||||||
Entity.__index = Entity
|
Entity.__index = Entity
|
||||||
|
@ -12,8 +11,11 @@ Entity.__index = Entity
|
||||||
-- @return A new Entity
|
-- @return A new Entity
|
||||||
function Entity.new()
|
function Entity.new()
|
||||||
local e = setmetatable({
|
local e = setmetatable({
|
||||||
removed = {},
|
world = nil,
|
||||||
worlds = List(),
|
|
||||||
|
__isDirty = true,
|
||||||
|
__wasAdded = false,
|
||||||
|
__wasRemoved = false,
|
||||||
|
|
||||||
__isEntity = true,
|
__isEntity = true,
|
||||||
}, Entity)
|
}, Entity)
|
||||||
|
@ -25,7 +27,13 @@ local function give(e, component, ...)
|
||||||
local comp = component:__initialize(...)
|
local comp = component:__initialize(...)
|
||||||
e[component] = comp
|
e[component] = comp
|
||||||
|
|
||||||
e:mark()
|
e.__isDirty = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function remove(e, component)
|
||||||
|
e[component] = nil
|
||||||
|
|
||||||
|
e.__isDirty = true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gives an Entity a component with values.
|
--- Gives an Entity a component with values.
|
||||||
|
@ -37,10 +45,6 @@ function Entity:give(component, ...)
|
||||||
error("bad argument #1 to 'Entity:give' (Component expected, got "..type(component)..")", 2)
|
error("bad argument #1 to 'Entity:give' (Component expected, got "..type(component)..")", 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
if self[component] then
|
|
||||||
self:remove(component):apply()
|
|
||||||
end
|
|
||||||
|
|
||||||
give(self, component, ...)
|
give(self, component, ...)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@ -68,9 +72,7 @@ function Entity:remove(component)
|
||||||
error("bad argument #1 to 'Entity:remove' (Component expected, got "..type(component)..")")
|
error("bad argument #1 to 'Entity:remove' (Component expected, got "..type(component)..")")
|
||||||
end
|
end
|
||||||
|
|
||||||
self.removed[#self.removed + 1] = component
|
remove(self, component)
|
||||||
|
|
||||||
self:mark()
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
@ -85,28 +87,11 @@ function Entity:assemble(assemblage, ...)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function Entity:mark()
|
|
||||||
for i = 1, self.worlds.size do
|
|
||||||
self.worlds:get(i):markEntity(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Entity:apply()
|
|
||||||
for i = 1, #self.removed do
|
|
||||||
local component = self.removed[i]
|
|
||||||
|
|
||||||
self[component] = nil
|
|
||||||
self.removed[i] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Destroys the Entity.
|
--- Destroys the Entity.
|
||||||
-- @return self
|
-- @return self
|
||||||
function Entity:destroy()
|
function Entity:destroy()
|
||||||
for i = 1, self.worlds.size do
|
if self.world then
|
||||||
self.worlds:get(i):removeEntity(self)
|
self.world:removeEntity(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
|
@ -73,7 +73,7 @@ end
|
||||||
-- @param obj The object to search for
|
-- @param obj The object to search for
|
||||||
-- true if the list has the object, false otherwise
|
-- true if the list has the object, false otherwise
|
||||||
function List:has(obj)
|
function List:has(obj)
|
||||||
return self[obj] and true
|
return self[obj] and true or false
|
||||||
end
|
end
|
||||||
|
|
||||||
return setmetatable(List, {
|
return setmetatable(List, {
|
||||||
|
|
27
src/pool.lua
27
src/pool.lua
|
@ -14,9 +14,6 @@ Pool.__index = Pool
|
||||||
function Pool.new(name, filter)
|
function Pool.new(name, filter)
|
||||||
local pool = setmetatable(List(), Pool)
|
local pool = setmetatable(List(), Pool)
|
||||||
|
|
||||||
pool.added = {}
|
|
||||||
pool.removed = {}
|
|
||||||
|
|
||||||
pool.name = name
|
pool.name = name
|
||||||
pool.filter = filter
|
pool.filter = filter
|
||||||
|
|
||||||
|
@ -25,18 +22,12 @@ function Pool.new(name, filter)
|
||||||
return pool
|
return pool
|
||||||
end
|
end
|
||||||
|
|
||||||
function Pool:flush()
|
|
||||||
for i = 1, math.max(#self.added, #self.removed) do
|
|
||||||
self.added[i], self.removed[i] = nil, nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Checks if an Entity is eligible for the Pool.
|
--- Checks if an Entity is eligible for the Pool.
|
||||||
-- @param e The Entity to check
|
-- @param e The Entity to check
|
||||||
-- @return True if the entity is eligible, false otherwise
|
-- @return True if the entity is eligible, false otherwise
|
||||||
function Pool:eligible(e)
|
function Pool:eligible(e)
|
||||||
for _, component in ipairs(self.filter) do
|
for _, component in ipairs(self.filter) do
|
||||||
if not e[component] or e.removed[component] then
|
if not e[component] then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -44,6 +35,22 @@ function Pool:eligible(e)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Pool:add(e)
|
||||||
|
List.add(self, e)
|
||||||
|
self:onEntityAdded(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pool:remove(e)
|
||||||
|
List.remove(self, e)
|
||||||
|
self:onEntityRemoved(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pool:onEntityAdded(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pool:onEntityRemoved(e)
|
||||||
|
end
|
||||||
|
|
||||||
return setmetatable(Pool, {
|
return setmetatable(Pool, {
|
||||||
__index = List,
|
__index = List,
|
||||||
__call = function(_, ...)
|
__call = function(_, ...)
|
||||||
|
|
|
@ -9,7 +9,6 @@ System.mt = {
|
||||||
__index = System,
|
__index = System,
|
||||||
__call = function(systemProto, ...)
|
__call = function(systemProto, ...)
|
||||||
local system = setmetatable({
|
local system = setmetatable({
|
||||||
__all = {},
|
|
||||||
__pools = {},
|
__pools = {},
|
||||||
__world = nil,
|
__world = nil,
|
||||||
|
|
||||||
|
@ -63,22 +62,15 @@ end
|
||||||
|
|
||||||
--- Checks and applies an Entity to the System's pools.
|
--- Checks and applies an Entity to the System's pools.
|
||||||
-- @param e The Entity to check
|
-- @param e The Entity to check
|
||||||
-- @return True if the Entity was added, false if it was removed. Nil if nothing happend
|
function System:__evaluate(e)
|
||||||
function System:__check(e)
|
|
||||||
for _, pool in ipairs(self.__pools) do
|
for _, pool in ipairs(self.__pools) do
|
||||||
local poolHas = pool:has(e)
|
local has = pool:has(e)
|
||||||
local eligible = pool:eligible(e)
|
local eligible = pool:eligible(e)
|
||||||
|
|
||||||
if not poolHas and eligible then
|
if not has and eligible then
|
||||||
pool:add(e)
|
pool:add(e)
|
||||||
pool.added[#pool.added + 1] = e
|
elseif has and not eligible then
|
||||||
|
|
||||||
self:__tryAdd(e)
|
|
||||||
elseif poolHas and not eligible then
|
|
||||||
pool:remove(e)
|
pool:remove(e)
|
||||||
pool.removed[#pool.removed + 1] = e
|
|
||||||
|
|
||||||
self:__tryRemove(e)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -86,46 +78,16 @@ end
|
||||||
--- Remove an Entity from the System.
|
--- Remove an Entity from the System.
|
||||||
-- @param e The Entity to remove
|
-- @param e The Entity to remove
|
||||||
function System:__remove(e)
|
function System:__remove(e)
|
||||||
if self.__all[e] then
|
for _, pool in ipairs(self.__pools) do
|
||||||
for _, pool in ipairs(self.__pools) do
|
if pool:has(e) then
|
||||||
if pool:has(e) then
|
pool:remove(e)
|
||||||
pool:remove(e)
|
|
||||||
pool.removed[#pool.removed + 1] = e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.__all[e] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Tries to add an Entity to the System.
|
|
||||||
-- @param e The Entity to add
|
|
||||||
function System:__tryAdd(e)
|
|
||||||
if not self.__all[e] then
|
|
||||||
self.__all[e] = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
self.__all[e] = self.__all[e] + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Tries to remove an Entity from the System.
|
|
||||||
-- @param e The Entity to remove
|
|
||||||
function System:__tryRemove(e)
|
|
||||||
if self.__all[e] then
|
|
||||||
self.__all[e] = self.__all[e] - 1
|
|
||||||
|
|
||||||
if self.__all[e] == 0 then
|
|
||||||
self.__all[e] = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function System:flush() -- luacheck: ignore
|
|
||||||
end
|
|
||||||
|
|
||||||
function System:clear()
|
function System:clear()
|
||||||
for i = 1, #self.__pools do
|
for i = 1, #self.__pools do
|
||||||
self.__pools[i]:flush()
|
self.__pools[i]:clear()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -142,7 +104,7 @@ end
|
||||||
|
|
||||||
-- Default callback for when the System is added to an World.
|
-- Default callback for when the System is added to an World.
|
||||||
-- @param world The World the System was added to
|
-- @param world The World the System was added to
|
||||||
function System:addedTo(World) -- luacheck: ignore
|
function System:addedTo(world) -- luacheck: ignore
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Default callback for when a System's callback is enabled.
|
-- Default callback for when a System's callback is enabled.
|
||||||
|
|
111
src/world.lua
111
src/world.lua
|
@ -16,8 +16,8 @@ function World.new()
|
||||||
systems = List(),
|
systems = List(),
|
||||||
events = {},
|
events = {},
|
||||||
|
|
||||||
marked = {},
|
__added = {},
|
||||||
removed = {},
|
__removed = {},
|
||||||
|
|
||||||
__isWorld = true,
|
__isWorld = true,
|
||||||
}, World)
|
}, World)
|
||||||
|
@ -33,16 +33,19 @@ function World:addEntity(e)
|
||||||
error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2)
|
error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
self:onEntityAdded(e)
|
if e.world then
|
||||||
|
error("bad argument #1 to 'World:addEntity' (Entity was already added to a world)", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
e.world = self
|
||||||
|
e.__wasAdded = true
|
||||||
|
|
||||||
e.worlds:add(self)
|
|
||||||
self.entities:add(e)
|
self.entities:add(e)
|
||||||
self:checkEntity(e)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Marks an Entity as removed from the World.
|
--- Removes an entity from the World.
|
||||||
-- @param e The Entity to mark
|
-- @param e The Entity to mark
|
||||||
-- @return self
|
-- @return self
|
||||||
function World:removeEntity(e)
|
function World:removeEntity(e)
|
||||||
|
@ -50,73 +53,46 @@ function World:removeEntity(e)
|
||||||
error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2)
|
error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
self.removed[#self.removed + 1] = e
|
e.__wasRemoved = true
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function World:markEntity(e)
|
|
||||||
if not Type.isEntity(e) then
|
|
||||||
error("bad argument #1 to 'World:markEntity' (Entity expected, got "..type(e)..")", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.marked[#self.marked + 1] = e
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Checks an Entity against all the systems in the World.
|
|
||||||
-- @param e The Entity to check
|
|
||||||
-- @return self
|
|
||||||
function World:checkEntity(e)
|
|
||||||
if not Type.isEntity(e) then
|
|
||||||
error("bad argument #1 to 'World:checkEntity' (Entity expected, got "..type(e)..")", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
for i = 1, self.systems.size do
|
|
||||||
self.systems:get(i):__check(e)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Completely removes all marked Entities in the World.
|
|
||||||
-- @return self
|
-- @return self
|
||||||
function World:flush()
|
function World:flush()
|
||||||
while #self.marked > 0 do
|
local e
|
||||||
local marked = self.removed
|
for i = 1, self.entities.size do
|
||||||
self.removed = {}
|
e = self.entities:get(i)
|
||||||
|
|
||||||
for i = 1, #marked do
|
if e.__wasAdded then
|
||||||
local e = marked[i]
|
e.__wasAdded = false
|
||||||
|
e.__isDirty = false
|
||||||
|
|
||||||
e.Worlds:apply()
|
for i = 1, self.systems.size do
|
||||||
e.Worlds:checkEntity(e)
|
self.systems:get(i):__evaluate(e)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
while #self.removed > 0 do
|
|
||||||
local removed = self.removed
|
|
||||||
self.removed = {}
|
|
||||||
|
|
||||||
for i = 1, #removed do
|
|
||||||
local e = removed[i]
|
|
||||||
|
|
||||||
e.worlds:remove(self)
|
|
||||||
self.entities:remove(e)
|
|
||||||
|
|
||||||
for j = 1, self.systems.size do
|
|
||||||
self.systems:get(j):__remove(e)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self:onEntityRemoved(e)
|
self:onEntityAdded(e)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
for i = 1, self.systems.size do
|
if e.__wasRemoved then
|
||||||
local system = self.systems:get(i)
|
e.world = nil
|
||||||
system:flush()
|
self.entities:remove(e)
|
||||||
system:clear()
|
|
||||||
|
for i = 1, self.systems.size do
|
||||||
|
self.systems:get(i):__remove(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
e.__wasRemoved = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if e.__isDirty then
|
||||||
|
for i = 1, self.systems.size do
|
||||||
|
self.systems:get(i):__evaluate(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
e.__isDirty = false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@ -142,6 +118,10 @@ function World:addSystem(system, eventName, callback, enabled)
|
||||||
system.__World = self
|
system.__World = self
|
||||||
|
|
||||||
system:addedTo(self)
|
system:addedTo(self)
|
||||||
|
|
||||||
|
for i = 1, self.entities.size do
|
||||||
|
system:__evaluate(self.entities:get(i))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if eventName then
|
if eventName then
|
||||||
|
@ -159,13 +139,6 @@ function World:addSystem(system, eventName, callback, enabled)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local e
|
|
||||||
for i = 1, self.entities.size do
|
|
||||||
e = self.entities:get(i)
|
|
||||||
|
|
||||||
self:checkEntity(e)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -242,8 +215,6 @@ function World:emit(eventName, ...)
|
||||||
error("bad argument #1 to 'World:emit' (String expected, got "..type(eventName)..")")
|
error("bad argument #1 to 'World:emit' (String expected, got "..type(eventName)..")")
|
||||||
end
|
end
|
||||||
|
|
||||||
self:flush()
|
|
||||||
|
|
||||||
local listeners = self.events[eventName]
|
local listeners = self.events[eventName]
|
||||||
|
|
||||||
if listeners then
|
if listeners then
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue