Added assemblages

This commit is contained in:
Justin van der Leij 2018-11-11 19:42:44 +01:00
parent 11255fd722
commit f7a394f057
12 changed files with 204 additions and 93 deletions

1
.luacheckrc Normal file
View file

@ -0,0 +1 @@
std="love+luajit"

11
TODO
View file

@ -3,6 +3,13 @@
[x] Add Entity:ensure (maybe?)
[ ] Put pools in the Instance and invert dependency.
[ ] Share pools between systems
[ ] Remove System callbacks
[x] Remove System callbacks
[x] Put .added and .removed in pools so they can be iterated over
[ ] Implement assemblages
[x] Implement assemblages
[x] Do :apply automatically with marked entities
[x] Remove Entity.components. Use Entity[Component] instead
[ ] Add missing documentation
[ ] Fix current documentation
[ ] Write unit tests
[ ] Write examples

View file

@ -0,0 +1,67 @@
local Concord = require("lib").init({
useEvents = true
})
local Entity = Concord.entity
local Component = Concord.component
local System = Concord.system
local Assemblage = Concord.assemblage
local Game = Concord.instance()
Concord.addInstance(Game)
local Legs = Component(function(e, legCount)
e.legCount = legCount or 0
end)
local Instinct = Component(function(e) -- luacheck: ignore
end)
local Cool = Component(function(e, coolness)
e.coolness = coolness
end)
local Wings = Component(function(e)
e.wingCount = 2
end)
local Animal = Assemblage(function(e, legCount)
e
:give(Legs, legCount)
:give(Instinct)
print("Animal")
end)
local Lion = Assemblage(function(e, coolness)
e
:assemble(Animal, 4)
:give(Cool, coolness)
print("Lion")
end)
local Eagle = Assemblage(function(e)
e
:assemble(Animal, 2)
:give(Wings)
print("Eagle")
end)
local Griffin = Assemblage(function(e, coolness)
e
:assemble(Animal, 4)
:assemble(Lion, coolness * 2)
:assemble(Eagle)
end)
local myAnimal = Entity()
:assemble(Griffin, 5)
--:apply()
print(myAnimal:has(Legs))
print(myAnimal:has(Instinct))
print(myAnimal:has(Cool))
print(myAnimal:has(Wings))

View file

@ -78,7 +78,7 @@ end
function RandomRemover:update(dt)
self.time = self.time + dt
if self.time >= 0.5 then
if self.time >= 0.05 then
self.time = 0
if self.pool.size > 0 then
@ -95,7 +95,7 @@ Game:addSystem(RandomRemover(), "update")
Game:addSystem(RectangleRenderer(), "draw")
Game:addSystem(CircleRenderer(), "draw")
for i = 1, 100 do
for _ = 1, 100 do
local e = Entity()
e:give(Position, love.math.random(0, 700), love.math.random(0, 700))
e:give(Rectangle, love.math.random(5, 20), love.math.random(5, 20))
@ -107,7 +107,7 @@ for i = 1, 100 do
Game:addEntity(e)
end
for i = 1, 100 do
for _ = 1, 100 do
local e = Entity()
e:give(Position, love.math.random(0, 700), love.math.random(0, 700))
e:give(Circle, love.math.random(5, 20))

26
lib/assemblage.lua Normal file
View file

@ -0,0 +1,26 @@
--- Assemblage
local Assemblage = {}
Assemblage.__index = Assemblage
function Assemblage.new(assemble)
local assemblage = setmetatable({
__assemble = assemble,
__isAssemblage = true,
}, Assemblage)
Assemblage.__mt = {__index = assemblage}
return assemblage
end
function Assemblage:assemble(e, ...)
self.__assemble(e, ...)
return self
end
return setmetatable(Assemblage, {
__call = function(_, ...) return Assemblage.new(...) end,
})

View file

@ -12,7 +12,6 @@ Entity.__index = Entity
-- @return A new Entity
function Entity.new()
local e = setmetatable({
components = {},
removed = {},
instances = List(),
@ -24,8 +23,9 @@ end
local function give(e, component, ...)
local comp = component:__initialize(...)
e.components[component] = comp
e[component] = comp
e:mark()
end
--- Gives an Entity a component with values.
@ -68,22 +68,36 @@ function Entity:remove(component)
error("bad argument #1 to 'Entity:remove' (Component expected, got "..type(component)..")")
end
self.removed[component] = true
self.removed[#self.removed + 1] = component
self:mark()
return self
end
--- Checks the Entity against the pools again.
-- @return self
function Entity:apply()
for i = 1, self.instances.size do
self.instances:get(i):checkEntity(self)
function Entity:assemble(assemblage, ...)
if not Type.isAssemblage(assemblage) then
error("bad argument #1 to 'Entity:assemble' (Assemblage expected, got "..type(assemblage)..")")
end
for component, _ in pairs(self.removed) do
self.components[component] = nil
assemblage:assemble(self, ...)
return self
end
function Entity:mark()
for i = 1, self.instances.size do
self.instances: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[component] = nil
self.removed[i] = nil
end
return self
@ -107,7 +121,7 @@ function Entity:get(component)
error("bad argument #1 to 'Entity:get' (Component expected, got "..type(component)..")")
end
return self.components[component]
return self[component]
end
--- Returns true if the Entity has the Component.
@ -118,7 +132,7 @@ function Entity:has(component)
error("bad argument #1 to 'Entity:has' (Component expected, got "..type(component)..")")
end
return self.components[component] ~= nil
return self[component] ~= nil
end
return setmetatable(Entity, {

View file

@ -43,6 +43,7 @@ function Concord.init(settings)
Concord.component = require(PATH..".component")
Concord.system = require(PATH..".system")
Concord.instance = require(PATH..".instance")
Concord.assemblage = require(PATH..".assemblage")
if settings and settings.useEvents then
Concord.instances = {}

View file

@ -2,8 +2,6 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local Entity = require(PATH..".entity")
local System = require(PATH..".system")
local Type = require(PATH..".type")
local List = require(PATH..".list")
@ -17,8 +15,9 @@ function Instance.new()
entities = List(),
systems = List(),
events = {},
marked = {},
removed = {},
toRemove = nil,
__isInstance = true,
}, Instance)
@ -43,6 +42,29 @@ function Instance:addEntity(e)
return self
end
--- Marks an Entity as removed from the Instance.
-- @param e The Entity to mark
-- @return self
function Instance:removeEntity(e)
if not Type.isEntity(e) then
error("bad argument #1 to 'Instance:removeEntity' (Entity expected, got "..type(e)..")", 2)
end
self.removed[#self.removed + 1] = e
return self
end
function Instance:markEntity(e)
if not Type.isEntity(e) then
error("bad argument #1 to 'Instance: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 Instance.
-- @param e The Entity to check
-- @return self
@ -58,34 +80,33 @@ function Instance:checkEntity(e)
return self
end
--- Marks an Entity as removed from the Instance.
-- @param e The Entity to mark
-- @return self
function Instance:removeEntity(e)
if not Type.isEntity(e) then
error("bad argument #1 to 'Instance:removeEntity' (Entity expected, got "..type(e)..")", 2)
end
self.removed[#self.removed + 1] = e
return self
end
--- Completely removes all marked Entities in the Instance.
-- @return self
function Instance:flush()
while #self.removed > 0 do
self.toRemove = self.removed
while #self.marked > 0 do
local marked = self.removed
self.removed = {}
for i = 1, #self.toRemove do
local e = self.toRemove[i]
for i = 1, #marked do
local e = marked[i]
e.instances:apply()
e.instances: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.instances:remove(self)
self.entities:remove(e)
for i = 1, self.systems.size do
self.systems:get(i):__remove(e)
for j = 1, self.systems.size do
self.systems:get(j):__remove(e)
end
self:onEntityRemoved(e)
@ -93,7 +114,9 @@ function Instance:flush()
end
for i = 1, self.systems.size do
self.systems:get(i):flush()
local system = self.systems:get(i)
system:flush()
system:clear()
end
return self
@ -250,12 +273,12 @@ end
--- Default callback for adding an Entity.
-- @param e The Entity that was added
function Instance:onEntityAdded(e)
function Instance:onEntityAdded(e) -- luacheck: ignore
end
--- Default callback for removing an Entity.
-- @param e The Entity that was removed
function Instance:onEntityRemoved(e)
function Instance:onEntityRemoved(e) -- luacheck: ignore
end
return setmetatable(Instance, {

View file

@ -36,7 +36,7 @@ end
-- @return True if the entity is eligible, false otherwise
function Pool:eligible(e)
for _, component in ipairs(self.filter) do
if not e.components[component] or e.removed[component] then
if not e[component] or e.removed[component] then
return false
end
end

View file

@ -2,7 +2,6 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local Component = require(PATH..".component")
local Pool = require(PATH..".pool")
local System = {}
@ -47,11 +46,11 @@ end
--- Builds a Pool for the System.
-- @param baseFilter The 'raw' Filter
-- @return A new Pool
function System:__buildPool(baseFilter)
function System:__buildPool(baseFilter) -- luacheck: ignore
local name = "pool"
local filter = {}
for i, v in ipairs(baseFilter) do
for _, v in ipairs(baseFilter) do
if type(v) == "table" then
filter[#filter + 1] = v
elseif type(v) == "string" then
@ -66,8 +65,6 @@ end
-- @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)
local systemHas = self.__all[e]
for _, pool in ipairs(self.__pools) do
local poolHas = pool:has(e)
local eligible = pool:eligible(e)
@ -76,13 +73,11 @@ function System:__check(e)
pool:add(e)
pool.added[#pool.added + 1] = e
self:entityAddedTo(e, pool)
self:__tryAdd(e)
elseif poolHas and not eligible then
pool:remove(e)
pool.removed[#pool.removed + 1] = e
self:entityRemovedFrom(e, pool)
self:__tryRemove(e)
end
end
@ -96,12 +91,10 @@ function System:__remove(e)
if pool:has(e) then
pool:remove(e)
pool.removed[#pool.removed + 1] = e
self:entityRemovedFrom(e, pool)
end
end
self.__all[e] = nil
self:entityRemoved(e)
end
end
@ -110,7 +103,6 @@ end
function System:__tryAdd(e)
if not self.__all[e] then
self.__all[e] = 0
self:entityAdded(e)
end
self.__all[e] = self.__all[e] + 1
@ -124,13 +116,11 @@ function System:__tryRemove(e)
if self.__all[e] == 0 then
self.__all[e] = nil
self:entityRemoved(e)
end
end
end
function System:flush()
self:clear()
function System:flush() -- luacheck: ignore
end
function System:clear()
@ -147,44 +137,22 @@ end
--- Default callback for system initialization.
-- @param ... Varags
function System:init(...)
end
--- Default callback for adding an Entity.
-- @param e The Entity that was added
function System:entityAdded(e)
end
--- Default callback for adding an Entity to a pool.
-- @param e The Entity that was added
-- @param pool The pool the Entity was added to
function System:entityAddedTo(e, pool)
end
--- Default callback for removing an Entity.
-- @param e The Entity that was removed
function System:entityRemoved(e)
end
--- Default callback for removing an Entity from a pool.
-- @param e The Entity that was removed
-- @param pool The pool the Entity was removed from
function System:entityRemovedFrom(e, pool)
function System:init(...) -- luacheck: ignore
end
-- Default callback for when the System is added to an Instance.
-- @param instance The Instance the System was added to
function System:addedTo(instance)
function System:addedTo(instance) -- luacheck: ignore
end
-- Default callback for when a System's callback is enabled.
-- @param callbackName The name of the callback that was enabled
function System:enabledCallback(callbackName)
function System:enabledCallback(callbackName) -- luacheck: ignore
end
-- Default callback for when a System's callback is disabled.
-- @param callbackName The name of the callback that was disabled
function System:disabledCallback(callbackName)
function System:disabledCallback(callbackName) -- luacheck: ignore
end
return setmetatable(System, {

View file

@ -16,4 +16,8 @@ function Type.isInstance(t)
return type(t) == "table" and t.__isInstance
end
function Type.isAssemblage(t)
return type(t) == "table" and t.__isAssemblage
end
return Type