mirror of
https://github.com/bakpakin/tiny-ecs.git
synced 2024-11-28 23:54:21 +00:00
Update to 1.0.0.
Change System creation API. Add functionality for reordering Systems.
This commit is contained in:
parent
283d034b21
commit
29ed0977a0
12
README.md
12
README.md
@ -38,13 +38,14 @@ only Entities that have all required components.
|
|||||||
```lua
|
```lua
|
||||||
local tiny = require("tiny")
|
local tiny = require("tiny")
|
||||||
|
|
||||||
local talkingSystem = tiny.processingSystem(
|
local talkingSystem = tiny.system()
|
||||||
tiny.requireAll("name", "mass", "phrase"),
|
talkingSystem.filter = tiny.requireAll("name", "mass", "phrase")
|
||||||
function (p, delta)
|
function talkingSystem:update(world, entities, dt)
|
||||||
p.mass = p.mass + delta * 3
|
for p in pairs(entities) do
|
||||||
|
p.mass = p.mass + dt * 3
|
||||||
print(p.name .. ", who weighs " .. p.mass .. " pounds, says, \"" .. p.phrase .. "\"")
|
print(p.name .. ", who weighs " .. p.mass .. " pounds, says, \"" .. p.phrase .. "\"")
|
||||||
end
|
end
|
||||||
)
|
end
|
||||||
|
|
||||||
local joe = {
|
local joe = {
|
||||||
name = "Joe",
|
name = "Joe",
|
||||||
@ -70,7 +71,6 @@ Documentation can be generated locally with [LDoc](http://stevedonovan.github.io
|
|||||||
|
|
||||||
## TODO ##
|
## TODO ##
|
||||||
|
|
||||||
* Dynamic reordering of Systems
|
|
||||||
* More testing
|
* More testing
|
||||||
* Performance testing / optimization
|
* Performance testing / optimization
|
||||||
* Improve Documentation
|
* Improve Documentation
|
||||||
|
@ -67,15 +67,15 @@ describe('tiny-ecs:', function()
|
|||||||
local fxform = tiny.requireAll("xform")
|
local fxform = tiny.requireAll("xform")
|
||||||
local fall = tiny.requireOne("spinalTap", "onlyTen", "littleMan")
|
local fall = tiny.requireOne("spinalTap", "onlyTen", "littleMan")
|
||||||
|
|
||||||
assert.truthy(fall(entity1))
|
assert.truthy(fall(nil, entity1))
|
||||||
assert.truthy(ftap(entity1))
|
assert.truthy(ftap(nil, entity1))
|
||||||
assert.falsy(ftap(entity2))
|
assert.falsy(ftap(nil, entity2))
|
||||||
assert.truthy(fxform(entity1))
|
assert.truthy(fxform(nil, entity1))
|
||||||
assert.truthy(fxform(entity2))
|
assert.truthy(fxform(nil, entity2))
|
||||||
|
|
||||||
assert.truthy(fall(entity1))
|
assert.truthy(fall(nil, entity1))
|
||||||
assert.truthy(fall(entity2))
|
assert.truthy(fall(nil, entity2))
|
||||||
assert.truthy(fall(entity3))
|
assert.truthy(fall(nil, entity3))
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -85,23 +85,23 @@ describe('tiny-ecs:', function()
|
|||||||
|
|
||||||
local world, entity1, entity2, entity3
|
local world, entity1, entity2, entity3
|
||||||
|
|
||||||
local moveSystem = tiny.processingSystem(
|
local moveSystem = tiny.system()
|
||||||
tiny.requireAll("xform", "vel"),
|
moveSystem.filter = tiny.requireAll("xform", "vel")
|
||||||
function(e, dt)
|
function moveSystem:update(world, entities, dt)
|
||||||
|
for e in pairs(entities) do
|
||||||
local xform = e.xform
|
local xform = e.xform
|
||||||
local vel = e.vel
|
local vel = e.vel
|
||||||
local x, y = xform.x, xform.y
|
local x, y = xform.x, xform.y
|
||||||
local xvel, yvel = vel.x, vel.y
|
local xvel, yvel = vel.x, vel.y
|
||||||
xform.x, xform.y = x + xvel * dt, y + yvel * dt
|
xform.x, xform.y = x + xvel * dt, y + yvel * dt
|
||||||
end
|
end
|
||||||
)
|
end
|
||||||
|
|
||||||
local timePassed = 0
|
local timePassed = 0
|
||||||
local oneTimeSystem = tiny.system(
|
local oneTimeSystem = tiny.system()
|
||||||
function(dt)
|
function oneTimeSystem:update(world, entities, dt)
|
||||||
timePassed = timePassed + dt
|
timePassed = timePassed + dt
|
||||||
end
|
end
|
||||||
)
|
|
||||||
|
|
||||||
before_each(function()
|
before_each(function()
|
||||||
entity1 = deep_copy(entityTemplate1)
|
entity1 = deep_copy(entityTemplate1)
|
||||||
|
159
tiny.lua
159
tiny.lua
@ -3,7 +3,7 @@
|
|||||||
local tiny = {}
|
local tiny = {}
|
||||||
|
|
||||||
--- Tiny-ecs Version, a period-separated three number string like "1.2.3"
|
--- Tiny-ecs Version, a period-separated three number string like "1.2.3"
|
||||||
tiny._VERSION = "0.3.0"
|
tiny._VERSION = "1.0.0"
|
||||||
|
|
||||||
-- Local versions of standard lua functions
|
-- Local versions of standard lua functions
|
||||||
local tinsert = table.insert
|
local tinsert = table.insert
|
||||||
@ -37,12 +37,12 @@ local tiny_remove
|
|||||||
function tiny.requireAll(...)
|
function tiny.requireAll(...)
|
||||||
local components = {...}
|
local components = {...}
|
||||||
local len = #components
|
local len = #components
|
||||||
return function(e)
|
return function(_, e)
|
||||||
local c
|
local c
|
||||||
for i = 1, len do
|
for i = 1, len do
|
||||||
c = components[i]
|
c = components[i]
|
||||||
if type(c) == 'function' then
|
if type(c) == 'function' then
|
||||||
if not c(e) then
|
if not c(_, e) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
elseif e[c] == nil then
|
elseif e[c] == nil then
|
||||||
@ -59,12 +59,12 @@ end
|
|||||||
function tiny.requireOne(...)
|
function tiny.requireOne(...)
|
||||||
local components = {...}
|
local components = {...}
|
||||||
local len = #components
|
local len = #components
|
||||||
return function(e)
|
return function(_, e)
|
||||||
local c
|
local c
|
||||||
for i = 1, len do
|
for i = 1, len do
|
||||||
c = components[i]
|
c = components[i]
|
||||||
if type(c) == 'function' then
|
if type(c) == 'function' then
|
||||||
if c(e) then
|
if c(_, e) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
elseif e[c] ~= nil then
|
elseif e[c] ~= nil then
|
||||||
@ -76,50 +76,39 @@ function tiny.requireOne(...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- System functions.
|
--- System functions.
|
||||||
-- A System a wrapper around function callbacks for manipulating Entities.
|
-- A System is a wrapper around function callbacks for manipulating Entities.
|
||||||
-- @section System
|
-- @section System
|
||||||
|
|
||||||
local systemMetaTable = {}
|
-- Use an empty table as a key for identifying Systems. Any table that contains
|
||||||
|
-- this key is considered a System rather than an Entity.
|
||||||
|
local systemTableKey = {}
|
||||||
|
|
||||||
--- Creates a System.
|
-- Check if tables are systems.
|
||||||
-- @param callback Function of one argument, delta time, that is called once
|
local function isSystem(table)
|
||||||
-- per world update
|
return table[systemTableKey]
|
||||||
-- @param filter Function of one argument, an Entity, that returns a boolean
|
end
|
||||||
-- @param entityCallback Function of two arguments, an Entity and delta time
|
|
||||||
-- @param onAdd Optional callback for when Enities are added to the System that
|
--- Marks a table conforming to the System interface as a System recognized by
|
||||||
-- takes one argument, an Entity
|
-- tiny-ecs. Systems are tables that contain at least one field, an update
|
||||||
-- @param onRemove Similar to onAdd, but is instead called when an Entity is
|
-- function that takes parameters like so:
|
||||||
-- removed from the System
|
-- `function system:update(world, entities, dt)`. The `world` is the World the
|
||||||
-- @param postCallback similar to callback, but is called after Entites are
|
-- System belongs to, `entities` is an unordered table of Entities,
|
||||||
-- processed
|
-- with Entities as the KEYS, and `dt` is the delta time. There are also a few
|
||||||
function tiny.system(callback, filter, entityCallback, onAdd, onRemove, postCallback)
|
-- other optional callbacks:
|
||||||
local ret = {
|
-- `function system:filter(entity)` - returns a boolean
|
||||||
callback = callback,
|
-- `function system:onAdd(entity)` - returns nil
|
||||||
filter = filter,
|
-- `function system:onRemove(entity)` - returns nil
|
||||||
entityCallback = entityCallback,
|
-- For Filters, it is conveient to use `tiny.requireAll` or `tiny.requireOne`,
|
||||||
onAdd = onAdd,
|
-- but one can write their own filters as well.
|
||||||
onRemove = onRemove,
|
function tiny.system(table)
|
||||||
postCallback = postCallback
|
if table == nil then
|
||||||
}
|
table = {}
|
||||||
setmetatable(ret, systemMetaTable)
|
end
|
||||||
return ret
|
table[systemTableKey] = true
|
||||||
|
return table
|
||||||
end
|
end
|
||||||
tiny_system = tiny.system
|
tiny_system = tiny.system
|
||||||
|
|
||||||
--- Creates a System that processes Entities every update. Also provides
|
|
||||||
-- optional callbacks for when Entities are added or removed from the System.
|
|
||||||
-- @param filter Function of one argument, an Entity, that returns a boolean
|
|
||||||
-- @param entityCallback Function of two arguments, an Entity and delta time
|
|
||||||
-- @param onAdd Optional callback for when Entities are added to the System that
|
|
||||||
-- takes one argument, an Entity
|
|
||||||
-- @param onRemove Similar to onAdd, but is instead called when an Entity is
|
|
||||||
-- removed from the System
|
|
||||||
-- @param postCallback similar to callback, but is called after Entites are
|
|
||||||
-- processed
|
|
||||||
function tiny.processingSystem(filter, entityCallback, onAdd, onRemove, postCallback)
|
|
||||||
return tiny_system(nil, filter, entityCallback, onAdd, onRemove, postCallback)
|
|
||||||
end
|
|
||||||
|
|
||||||
local worldMetaTable = { __index = tiny }
|
local worldMetaTable = { __index = tiny }
|
||||||
|
|
||||||
--- World functions.
|
--- World functions.
|
||||||
@ -167,12 +156,11 @@ function tiny.world(...)
|
|||||||
systemEntities = {}
|
systemEntities = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
tiny.add(ret, ...)
|
tiny_add(ret, ...)
|
||||||
tiny.manageSystems(ret)
|
tiny_manageSystems(ret)
|
||||||
tiny.manageEntities(ret)
|
tiny_manageEntities(ret)
|
||||||
|
|
||||||
setmetatable(ret, worldMetaTable)
|
return setmetatable(ret, worldMetaTable)
|
||||||
return ret
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -205,7 +193,7 @@ tiny_addSystem = tiny.addSystem
|
|||||||
function tiny.add(world, ...)
|
function tiny.add(world, ...)
|
||||||
local args = {...}
|
local args = {...}
|
||||||
for _, obj in ipairs(args) do
|
for _, obj in ipairs(args) do
|
||||||
if getmetatable(obj) == systemMetaTable then
|
if isSystem(obj) then
|
||||||
tiny_addSystem(world, obj)
|
tiny_addSystem(world, obj)
|
||||||
else -- Assume obj is an Entity
|
else -- Assume obj is an Entity
|
||||||
tiny_addEntity(world, obj)
|
tiny_addEntity(world, obj)
|
||||||
@ -241,7 +229,7 @@ tiny_removeSystem = tiny.removeSystem
|
|||||||
function tiny.remove(world, ...)
|
function tiny.remove(world, ...)
|
||||||
local args = {...}
|
local args = {...}
|
||||||
for _, obj in ipairs(args) do
|
for _, obj in ipairs(args) do
|
||||||
if getmetatable(obj) == systemMetaTable then
|
if isSystem(obj) then
|
||||||
tiny_removeSystem(world, obj)
|
tiny_removeSystem(world, obj)
|
||||||
else -- Assume obj is an Entity
|
else -- Assume obj is an Entity
|
||||||
tiny_removeEntity(world, obj)
|
tiny_removeEntity(world, obj)
|
||||||
@ -255,28 +243,8 @@ tiny_remove = tiny.remove
|
|||||||
-- @param system A System in the World to update
|
-- @param system A System in the World to update
|
||||||
-- @param dt Delta time
|
-- @param dt Delta time
|
||||||
function tiny.updateSystem(world, system, dt)
|
function tiny.updateSystem(world, system, dt)
|
||||||
local callback = system.callback
|
|
||||||
local entityCallback = system.entityCallback
|
|
||||||
local postCallback = system.postCallback
|
|
||||||
|
|
||||||
if callback then
|
|
||||||
callback(dt)
|
|
||||||
end
|
|
||||||
|
|
||||||
if entityCallback then
|
|
||||||
local entities = world.entities
|
|
||||||
local es = world.systemEntities[system]
|
local es = world.systemEntities[system]
|
||||||
if es then
|
system:update(world, es, dt)
|
||||||
for e in pairs(es) do
|
|
||||||
entityCallback(e, dt)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if postCallback then
|
|
||||||
postCallback(dt)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
tiny_updateSystem = tiny.updateSystem
|
tiny_updateSystem = tiny.updateSystem
|
||||||
|
|
||||||
@ -303,9 +271,14 @@ function tiny.manageSystems(world)
|
|||||||
systems[systemIndices[system]] = system
|
systems[systemIndices[system]] = system
|
||||||
activeSystems[system] = true
|
activeSystems[system] = true
|
||||||
local filter = system.filter
|
local filter = system.filter
|
||||||
|
local onAdd = system.onAdd
|
||||||
if filter then
|
if filter then
|
||||||
for e in pairs(entities) do
|
for e in pairs(entities) do
|
||||||
es[e] = filter(e) and true or nil
|
local added = filter(system, e) and true or nil
|
||||||
|
es[e] = added
|
||||||
|
if added and onAdd then
|
||||||
|
onAdd(system, e)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif status == "remove" then
|
elseif status == "remove" then
|
||||||
@ -348,17 +321,17 @@ function tiny.manageEntities(world)
|
|||||||
entityCount = entityCount + 1
|
entityCount = entityCount + 1
|
||||||
end
|
end
|
||||||
entities[e] = true
|
entities[e] = true
|
||||||
for sys, es in pairs(systemEntities) do
|
for system, es in pairs(systemEntities) do
|
||||||
local filter = sys.filter
|
local filter = system.filter
|
||||||
if filter then
|
if filter then
|
||||||
local matches = filter(e) and true or nil
|
local matches = filter(system, e) and true or nil
|
||||||
local onRemove = sys.onRemove
|
local onRemove = system.onRemove
|
||||||
if not matches and es[e] and onRemove then
|
if not matches and es[e] and onRemove then
|
||||||
onRemove(e)
|
onRemove(system, e)
|
||||||
end
|
end
|
||||||
local onAdd = sys.onAdd
|
local onAdd = system.onAdd
|
||||||
if onAdd and matches and not es[e] then
|
if onAdd and matches and not es[e] then
|
||||||
onAdd(e)
|
onAdd(system, e)
|
||||||
end
|
end
|
||||||
es[e] = matches
|
es[e] = matches
|
||||||
end
|
end
|
||||||
@ -368,10 +341,10 @@ function tiny.manageEntities(world)
|
|||||||
entityCount = entityCount - 1
|
entityCount = entityCount - 1
|
||||||
end
|
end
|
||||||
entities[e] = nil
|
entities[e] = nil
|
||||||
for sys, es in pairs(systemEntities) do
|
for system, es in pairs(systemEntities) do
|
||||||
local onRemove = sys.onRemove
|
local onRemove = system.onRemove
|
||||||
if es[e] and onRemove then
|
if es[e] and onRemove then
|
||||||
onRemove(e)
|
onRemove(system, e)
|
||||||
end
|
end
|
||||||
es[e] = nil
|
es[e] = nil
|
||||||
end
|
end
|
||||||
@ -437,6 +410,28 @@ function tiny.getSystemCount(world)
|
|||||||
return world.systemCount
|
return world.systemCount
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Gets the index of a System in the world. Lower indexed Systems are processed
|
||||||
|
-- before higher indexed systems.
|
||||||
|
-- @param world
|
||||||
|
-- @param system
|
||||||
|
function tiny.getSystemIndex(world, system)
|
||||||
|
return world.systemIndices[system]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sets the index of a System in the world. Changes the order in
|
||||||
|
-- which they Systems processed, because lower indexed Systems are processed
|
||||||
|
-- first.
|
||||||
|
-- @param world
|
||||||
|
-- @param system
|
||||||
|
-- @param index
|
||||||
|
function tiny.setSystemIndex(world, system, index)
|
||||||
|
local oldIndex = world.systemIndices[system]
|
||||||
|
local systems = world.systems
|
||||||
|
tremove(systems, oldIndex)
|
||||||
|
tinsert(systems, index, system)
|
||||||
|
world.systemIndices[system] = index
|
||||||
|
end
|
||||||
|
|
||||||
--- Activates Systems in the World.
|
--- Activates Systems in the World.
|
||||||
-- Activated Systems will be update whenever tiny.update(world, dt) is called.
|
-- Activated Systems will be update whenever tiny.update(world, dt) is called.
|
||||||
-- @param world
|
-- @param world
|
||||||
|
Loading…
Reference in New Issue
Block a user