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,9 +12,8 @@ Entity.__index = Entity
-- @return A new Entity
function Entity.new()
local e = setmetatable({
components = {},
removed = {},
instances = List(),
removed = {},
instances = List(),
__isEntity = true,
}, Entity)
@ -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

@ -22,7 +22,7 @@ local Concord = {
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
@ -39,10 +39,11 @@ local Concord = {
-- }
-- @return Concord
function Concord.init(settings)
Concord.entity = require(PATH..".entity")
Concord.component = require(PATH..".component")
Concord.system = require(PATH..".system")
Concord.instance = require(PATH..".instance")
Concord.entity = require(PATH..".entity")
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,8 +2,7 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local Component = require(PATH..".component")
local Pool = require(PATH..".pool")
local Pool = require(PATH..".pool")
local System = {}
System.mt = {
@ -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
return Type
function Type.isAssemblage(t)
return type(t) == "table" and t.__isAssemblage
end
return Type

View file

@ -1,4 +1,4 @@
local file = "examples.simpleDrawing"
-- local file = "examples.baseLayout.main"
require(file)
require(file)