Fixed entity lifetime problems

This commit is contained in:
Tjakka5 2019-12-29 15:05:19 +01:00
parent a8bc92a951
commit 38d461f8d6
4 changed files with 110 additions and 129 deletions

View file

@ -20,77 +20,72 @@ local Entity = Concord.entity
local World = Concord.world local World = Concord.world
local Worlds = Concord.worlds local Worlds = Concord.worlds
Concord.loadComponents("test/components")
local test_comp_1 = Component(function(e, a)
e.a = a
end)
local test_comp_2 = Component(function(e, a) local test_comp_2 = Component(function(e, a)
e.a = a e.a = a
end) end)
Components.register("test_comp_2", test_comp_2)
local test_comp_3 = Component() local test_comp_3 = Component()
Components.register("test_comp_3", test_comp_3)
local test_system_1 = System({test_comp_1})
local test_system = System({Components.test_comp_1}) function test_system_1:init()
Systems.register("test_system", test_system) self.pool.onEntityAdded = function()
local function onEntityAdded(pool, e) -- luacheck: ignore print("Added to test_system 1")
print("Added")
end
local function onEntityRemoved(pool, e) -- luacheck: ignore
print("Removed")
end
function test_system:init()
self.pool.onEntityAdded = onEntityAdded
self.pool.onEntityRemoved = onEntityRemoved
end
function test_system:update(dt) -- luacheck: ignore
--[=[
for _, v in ipairs(self.pool) do
print(v)
end end
]=] self.pool.onEntityRemoved = function() print("Removed from test_system 1") end
end end
function test_system:update2(dt) -- luacheck: ignore function test_system_1:test()
--print(#self.pool) print("Running test_system_1 with: " ..#self.pool)
for _, e in ipairs(self.pool) do
local newE = Entity()
newE:give(test_comp_1)
self:getWorld():addEntity(newE)
e:give(test_comp_2)
end
end end
local test_system_2 = System({test_comp_2})
function test_system_2:init()
self.pool.onEntityAdded = function(pool, e) print("Added to test_system 2") e:remove(test_comp_1) end
self.pool.onEntityRemoved = function() print("Removed from test_system 2") end
end
function test_system_2:test()
print("Running test_system_2 with: " ..#self.pool)
for _, e in ipairs(self.pool) do
end
end
local world = World() local world = World()
Worlds.register("testWorld", world)
local entity = Entity() local entity = Entity()
entity entity:give(test_comp_1, 100, 100)
:give(Components.test_comp_1, 100, 100)
:remove(Components.test_comp_1)
:give(Components.test_comp_1, 200, 100)
:give(Components.test_comp_3, 200, 100)
Worlds.testWorld:addEntity(entity) world:addEntity(entity)
world:addSystem(Systems.test_system, "update") world:addSystem(test_system_1, "test")
world:addSystem(Systems.test_system, "update", "update2") world:addSystem(test_system_2, "test")
function love.update(dt) print("Iteration: 1")
world:flush() world:emit("test")
world:emit("update", dt) print("Iteration: 2")
end world:emit("test")
function love.keypressed(key) print("Iteration: 3")
if key == "q" then world:emit("test")
entity:remove(Components.test_comp_1)
end
if key == "w" then
entity:give(Components.test_comp_1)
end
if key == "e" then
world:removeEntity(entity)
end
end

View file

@ -13,10 +13,6 @@ function Entity.new()
local e = setmetatable({ local e = setmetatable({
__world = nil, __world = nil,
__addedComponents = {},
__removedComponents = {},
__operations = {},
__components = {}, __components = {},
__isDirty = false, __isDirty = false,
@ -29,32 +25,22 @@ function Entity.new()
return e return e
end end
local function giveOperation(e, component)
local baseComponent = component.__baseComponent
e[baseComponent] = component
e.__components[baseComponent] = component
end
local function removeOperation(e, baseComponent)
e[baseComponent] = nil
e.__components[baseComponent] = nil
end
local function give(e, baseComponent, ...) local function give(e, baseComponent, ...)
local component = baseComponent:__initialize(...) local component = baseComponent:__initialize(...)
e.__addedComponents[#e.__addedComponents + 1] = component e[baseComponent] = component
e.__operations[#e.__operations + 1] = giveOperation e.__components[baseComponent] = component
e.__isDirty = true e.__isDirty = true
e:__dirty()
end end
local function remove(e, baseComponent) local function remove(e, baseComponent)
e.__removedComponents[#e.__removedComponents + 1] = baseComponent e[baseComponent] = nil
e.__operations[#e.__operations + 1] = removeOperation e.__components[baseComponent] = nil
e.__isDirty = true e.__isDirty = true
e:__dirty()
end end
--- Gives an Entity a component with values. --- Gives an Entity a component with values.
@ -108,36 +94,22 @@ function Entity:assemble(assemblage, ...)
return self return self
end end
function Entity:__flush()
local addi, removei = 1, 1
for i = 1, #self.__operations do
local operation = self.__operations[i]
if (operation == giveOperation) then
operation(self, self.__addedComponents[addi])
self.__addedComponents[addi] = nil
addi = addi + 1
elseif (operation == removeOperation) then
operation(self, self.__removedComponents[removei])
self.__removedComponents[removei] = nil
removei = removei + 1
end
self.__operations[i] = nil
end
end
--- Destroys the Entity. --- Destroys the Entity.
-- @return self -- @return self
function Entity:destroy() function Entity:destroy()
if self.world then if self.__world then
self.world:removeEntity(self) self.__world:removeEntity(self)
end end
return self return self
end end
function Entity:__dirty()
if self.__world then
self.__world:__dirtyEntity(self)
end
end
--- Gets a Component from the Entity. --- Gets a Component from the Entity.
-- @param component The Component to get -- @param component The Component to get
-- @return The Bag from the Component -- @return The Bag from the Component

View file

@ -17,8 +17,9 @@ function World.new()
events = {}, events = {},
__added = {}, __added = List(), __backAdded = List(),
__removed = {}, __removed = List(), __backRemoved = List(),
__dirty = List(), __backDirty = List(),
__systemLookup = {}, __systemLookup = {},
@ -36,14 +37,16 @@ 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
if e.world then if e.__world then
error("bad argument #1 to 'World:addEntity' (Entity was already added to a world)", 2) error("bad argument #1 to 'World:addEntity' (Entity was already added to a world)", 2)
end end
e.world = self e.__world = self
e.__wasAdded = true e.__wasAdded = true
self.entities:add(e) self.__added:add(e)
--self.entities:add(e)
return self return self
end end
@ -58,50 +61,65 @@ function World:removeEntity(e)
e.__wasRemoved = true e.__wasRemoved = true
self.__removed:add(e)
return self return self
end end
function World:__dirtyEntity(e)
if not self.__dirty:has(e) then
self.__dirty:add(e)
end
end
-- @return self -- @return self
function World:flush() function World:__flush()
-- Switch buffers
self.__added, self.__backAdded = self.__backAdded, self.__added
self.__removed, self.__backRemoved = self.__backRemoved, self.__removed
self.__dirty, self.__backDirty = self.__backDirty, self.__dirty
local e local e
for i = 1, self.entities.size do
e = self.entities:get(i)
if e.__isDirty then -- Added
e:__flush() for i = 1, self.__backAdded.size do
e = self.__backAdded:get(i)
if (not e.__wasAdded) then -- The __wasAdded check below will handle this instead self.entities:add(e)
for j = 1, self.systems.size do
self.systems:get(j):__evaluate(e)
end
end
e.__isDirty = false for j = 1, self.systems.size do
self.systems:get(j):__evaluate(e)
end end
if e.__wasAdded then self:onEntityAdded(e)
e.__wasAdded = false end
self.__backAdded:clear()
for j = 1, self.systems.size do -- Removed
self.systems:get(j):__evaluate(e) for i = 1, self.__backRemoved.size do
end e = self.__backRemoved:get(i)
self:onEntityAdded(e) e.__world = nil
self.entities:remove(e)
for j = 1, self.systems.size do
self.systems:get(j):__remove(e)
end end
if e.__wasRemoved then self:onEntityRemoved(e)
e.world = nil end
self.entities:remove(e) self.__backRemoved:clear()
for j = 1, self.systems.size do -- Dirty
self.systems:get(j):__remove(e) for i = 1, self.__backDirty.size do
end e = self.__backDirty:get(i)
e.__wasRemoved = false for j = 1, self.systems.size do
self.systems:get(j):__evaluate(e)
self:onEntityRemoved(e)
end end
end end
self.__backDirty:clear()
return self return self
end end
@ -246,6 +264,8 @@ function World:emit(callbackName, ...)
local listener = listeners[i] local listener = listeners[i]
if listener.enabled then if listener.enabled then
self:__flush()
listener.system[listener.callback](listener.system, ...) listener.system[listener.callback](listener.system, ...)
end end
end end

View file

@ -1,6 +0,0 @@
local Component = require("src").component
return Component(function(e, x, y)
e.x = x
e.y = y
end)