Update to 1.0.0.

Change System creation API.
Add functionality for reordering Systems.
This commit is contained in:
bakpakin 2015-04-21 21:47:36 +08:00
parent 283d034b21
commit 29ed0977a0
3 changed files with 101 additions and 106 deletions

View File

@ -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

View File

@ -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
View File

@ -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