diff --git a/main.lua b/main.lua index cd13075..dad60e1 100644 --- a/main.lua +++ b/main.lua @@ -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 \ No newline at end of file diff --git a/src/component.lua b/src/component.lua index 5777743..102e722 100644 --- a/src/component.lua +++ b/src/component.lua @@ -51,4 +51,4 @@ return setmetatable(Component, { __call = function(_, ...) return Component.new(...) end, -}) +}) \ No newline at end of file diff --git a/src/entity.lua b/src/entity.lua index 62d64c4..a6c6b29 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -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 diff --git a/src/list.lua b/src/list.lua index a79d20e..16819da 100644 --- a/src/list.lua +++ b/src/list.lua @@ -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, { diff --git a/src/pool.lua b/src/pool.lua index 779efd9..e82735a 100644 --- a/src/pool.lua +++ b/src/pool.lua @@ -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(_, ...) diff --git a/src/system.lua b/src/system.lua index 10729da..b078a50 100644 --- a/src/system.lua +++ b/src/system.lua @@ -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. diff --git a/src/world.lua b/src/world.lua index 59305fa..2712cb6 100644 --- a/src/world.lua +++ b/src/world.lua @@ -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