Streamline entity lifetime

This commit is contained in:
Tjakka5 2019-12-19 08:47:38 +01:00
parent bc47eaa651
commit 038111d558
7 changed files with 117 additions and 163 deletions

View file

@ -22,20 +22,49 @@ local test_comp_3 = Concord.component("test_comp_3", function(e, b)
end)
local test_system = Concord.system({Component.test_comp_1})
function test_system:update(dt)
print(#self.pool)
function onEntityAdded(e)
print("Added")
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 entity = Concord.entity()
entity:give(Component.test_comp_2, 100, 100)
entity:apply()
entity:give(Component.test_comp_1, 100, 100)
world:addEntity(entity)
world:addSystem(test_system(), "update")
function love.update(dt)
world:flush()
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

View file

@ -51,4 +51,4 @@ return setmetatable(Component, {
__call = function(_, ...)
return Component.new(...)
end,
})
})

View file

@ -3,7 +3,6 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local Type = require(PATH..".type")
local List = require(PATH..".list")
local Entity = {}
Entity.__index = Entity
@ -12,8 +11,11 @@ Entity.__index = Entity
-- @return A new Entity
function Entity.new()
local e = setmetatable({
removed = {},
worlds = List(),
world = nil,
__isDirty = true,
__wasAdded = false,
__wasRemoved = false,
__isEntity = true,
}, Entity)
@ -25,7 +27,13 @@ local function give(e, component, ...)
local comp = component:__initialize(...)
e[component] = comp
e:mark()
e.__isDirty = true
end
local function remove(e, component)
e[component] = nil
e.__isDirty = true
end
--- 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)
end
if self[component] then
self:remove(component):apply()
end
give(self, component, ...)
return self
@ -68,9 +72,7 @@ function Entity:remove(component)
error("bad argument #1 to 'Entity:remove' (Component expected, got "..type(component)..")")
end
self.removed[#self.removed + 1] = component
self:mark()
remove(self, component)
return self
end
@ -85,28 +87,11 @@ function Entity:assemble(assemblage, ...)
return self
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.
-- @return self
function Entity:destroy()
for i = 1, self.worlds.size do
self.worlds:get(i):removeEntity(self)
if self.world then
self.world:removeEntity(self)
end
return self

View file

@ -73,7 +73,7 @@ end
-- @param obj The object to search for
-- true if the list has the object, false otherwise
function List:has(obj)
return self[obj] and true
return self[obj] and true or false
end
return setmetatable(List, {

View file

@ -14,9 +14,6 @@ Pool.__index = Pool
function Pool.new(name, filter)
local pool = setmetatable(List(), Pool)
pool.added = {}
pool.removed = {}
pool.name = name
pool.filter = filter
@ -25,18 +22,12 @@ function Pool.new(name, filter)
return pool
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.
-- @param e The Entity to check
-- @return True if the entity is eligible, false otherwise
function Pool:eligible(e)
for _, component in ipairs(self.filter) do
if not e[component] or e.removed[component] then
if not e[component] then
return false
end
end
@ -44,6 +35,22 @@ function Pool:eligible(e)
return true
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, {
__index = List,
__call = function(_, ...)

View file

@ -9,7 +9,6 @@ System.mt = {
__index = System,
__call = function(systemProto, ...)
local system = setmetatable({
__all = {},
__pools = {},
__world = nil,
@ -63,22 +62,15 @@ end
--- Checks and applies an Entity to the System's pools.
-- @param e The Entity to check
-- @return True if the Entity was added, false if it was removed. Nil if nothing happend
function System:__check(e)
function System:__evaluate(e)
for _, pool in ipairs(self.__pools) do
local poolHas = pool:has(e)
local has = pool:has(e)
local eligible = pool:eligible(e)
if not poolHas and eligible then
if not has and eligible then
pool:add(e)
pool.added[#pool.added + 1] = e
self:__tryAdd(e)
elseif poolHas and not eligible then
elseif has and not eligible then
pool:remove(e)
pool.removed[#pool.removed + 1] = e
self:__tryRemove(e)
end
end
end
@ -86,46 +78,16 @@ end
--- Remove an Entity from the System.
-- @param e The Entity to remove
function System:__remove(e)
if self.__all[e] then
for _, pool in ipairs(self.__pools) do
if pool:has(e) then
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
for _, pool in ipairs(self.__pools) do
if pool:has(e) then
pool:remove(e)
end
end
end
function System:flush() -- luacheck: ignore
end
function System:clear()
for i = 1, #self.__pools do
self.__pools[i]:flush()
self.__pools[i]:clear()
end
end
@ -142,7 +104,7 @@ end
-- Default callback for when the System is added to an World.
-- @param world The World the System was added to
function System:addedTo(World) -- luacheck: ignore
function System:addedTo(world) -- luacheck: ignore
end
-- Default callback for when a System's callback is enabled.

View file

@ -16,8 +16,8 @@ function World.new()
systems = List(),
events = {},
marked = {},
removed = {},
__added = {},
__removed = {},
__isWorld = true,
}, World)
@ -33,16 +33,19 @@ function World:addEntity(e)
error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2)
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:checkEntity(e)
return self
end
--- Marks an Entity as removed from the World.
--- Removes an entity from the World.
-- @param e The Entity to mark
-- @return self
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)
end
self.removed[#self.removed + 1] = e
e.__wasRemoved = true
return self
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
function World:flush()
while #self.marked > 0 do
local marked = self.removed
self.removed = {}
local e
for i = 1, self.entities.size do
e = self.entities:get(i)
for i = 1, #marked do
local e = marked[i]
if e.__wasAdded then
e.__wasAdded = false
e.__isDirty = false
e.Worlds:apply()
e.Worlds:checkEntity(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)
for i = 1, self.systems.size do
self.systems:get(i):__evaluate(e)
end
self:onEntityRemoved(e)
self:onEntityAdded(e)
end
end
for i = 1, self.systems.size do
local system = self.systems:get(i)
system:flush()
system:clear()
if e.__wasRemoved then
e.world = nil
self.entities:remove(e)
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
return self
@ -142,6 +118,10 @@ function World:addSystem(system, eventName, callback, enabled)
system.__World = self
system:addedTo(self)
for i = 1, self.entities.size do
system:__evaluate(self.entities:get(i))
end
end
if eventName then
@ -159,13 +139,6 @@ function World:addSystem(system, eventName, callback, enabled)
end
end
local e
for i = 1, self.entities.size do
e = self.entities:get(i)
self:checkEntity(e)
end
return self
end
@ -242,8 +215,6 @@ function World:emit(eventName, ...)
error("bad argument #1 to 'World:emit' (String expected, got "..type(eventName)..")")
end
self:flush()
local listeners = self.events[eventName]
if listeners then