Start using LDoc for documentation.

Remove class system
Fix various small bug
This commit is contained in:
bakpakin
2015-03-29 20:19:31 +08:00
parent 8d4b994329
commit c38e5633d5
5 changed files with 244 additions and 225 deletions
Vendored
+2
View File
@@ -0,0 +1,2 @@
#Ignore generated documentation
doc/*
+6 -2
View File
@@ -53,7 +53,7 @@ local joe = {
hairColor = "brown" hairColor = "brown"
} }
local world = tiny.newWorld(talkingSystem, joe) local world = tiny.world(talkingSystem, joe)
for i = 1, 20 do for i = 1, 20 do
world:update(1) world:update(1)
@@ -64,10 +64,14 @@ end
Tiny-ecs uses [busted](http://olivinelabs.com/busted/) for testing. Install and run Tiny-ecs uses [busted](http://olivinelabs.com/busted/) for testing. Install and run
`busted` from the command line to test. `busted` from the command line to test.
## Documentation ##
See API [here](http://bakpakin.github.io/tiny-ecs/).
Documentation can be generated locally with [LDoc](http://stevedonovan.github.io/ldoc/).
## TODO ## ## TODO ##
* Dynamic reordering of Systems * Dynamic reordering of Systems
* More testing * More testing
* Performance testing / optimization * Performance testing / optimization
* API outside of source code * Improve Documentation
* Add more complete examples * Add more complete examples
+5
View File
@@ -0,0 +1,5 @@
file = "tiny.lua"
project = "tiny-ecs"
description = "Entity Component System for lua."
backtick_references = true
one = false
+2 -2
View File
@@ -97,7 +97,7 @@ describe('tiny-ecs:', function()
) )
local timePassed = 0 local timePassed = 0
local oneTimeSystem = tiny.emptySystem( local oneTimeSystem = tiny.system(
function(dt) function(dt)
timePassed = timePassed + dt timePassed = timePassed + dt
end end
@@ -107,7 +107,7 @@ describe('tiny-ecs:', function()
entity1 = deep_copy(entityTemplate1) entity1 = deep_copy(entityTemplate1)
entity2 = deep_copy(entityTemplate2) entity2 = deep_copy(entityTemplate2)
entity3 = deep_copy(entityTemplate3) entity3 = deep_copy(entityTemplate3)
world = tiny.newWorld(moveSystem, oneTimeSystem, entity1, entity2, entity3) world = tiny.world(moveSystem, oneTimeSystem, entity1, entity2, entity3)
timePassed = 0 timePassed = 0
end) end)
+229 -221
View File
@@ -1,8 +1,10 @@
local tiny = { --- @module tiny-ecs
_VERSION = "0.2.0", -- @author Calvin Rose
_URL = "https://github.com/bakpakin/tiny-ecs", local tiny = {}
_DESCRIPTION = "tiny-ecs - Entity Component System for lua."
} --- Tiny-ecs Version, a period-separated three number string like "1.2.3" with
-- no leading zeros.
tiny._VERSION = "0.3.0"
local tinsert = table.insert local tinsert = table.insert
local tremove = table.remove local tremove = table.remove
@@ -12,107 +14,165 @@ local ipairs = ipairs
local setmetatable = setmetatable local setmetatable = setmetatable
local getmetatable = getmetatable local getmetatable = getmetatable
-- Simple class implementation with no inheritance or polymorphism. -- Local versions of the library functions
local function class() local tiny_system
local c = {} local tiny_manageEntities
local mt = {} local tiny_manageSystems
setmetatable(c, mt) local tiny_updateSystem
c.__index = c local tiny_add
function mt.__call(_, ...) local tiny_remove
local newobj = {}
setmetatable(newobj, c) --- Filter functions.
if c.init then -- A Filter is a function that selects which Entities apply to a System.
c.init(newobj, ...) -- @section Filter
--- Makes a Filter that filters Entities with specified Components.
-- An Entity must have all Components to match the filter.
-- @param ... List of Components
function tiny.requireAll(...)
local components = {...}
local len = #components
return function(e)
local c
for i = 1, len do
c = components[i]
if e[c] == nil then
return false
end
end end
return newobj return true
end end
return c
end end
-- --- System --- -- --- Makes a Filter that filters Entities with specified Components.
local System = class() -- An Entity must have at least one specified Component to match the filter.
-- @param ... List of Components
-- Initializes a System. function tiny.requireOne(...)
function System:init(preupdate, filter, update, add, remove) local components = {...}
self.preupdate = preupdate local len = #components
self.update = update return function(e)
self.filter = filter local c
self.add = add for i = 1, len do
self.remove = remove c = components[i]
if e[c] ~= nil then
return true
end
end
return false
end
end end
function System:__tostring() --- System functions.
return "TinySystem<preupdate: " .. -- A System a wrapper around function callbacks for manipulating Entities.
self.preupdate .. -- @section System
", update: " ..
self.update .. local systemMetaTable = {}
", filter: " ..
self.filter .. --- Creates a System.
">" -- @param callback Function of one argument, delta time, that is called once
-- per world update
-- @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 Enities 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
function tiny.system(callback, filter, entityCallback, onAdd, onRemove)
local ret = {
callback = callback,
filter = filter,
entityCallback = entityCallback,
onAdd = onAdd,
onRemove = onRemove
}
setmetatable(ret, systemMetaTable)
return ret
end
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
function tiny.processingSystem(filter, entityCallback, onAdd, onRemove)
return tiny_system(nil, filter, entityCallback, onAdd, onRemove)
end end
-- --- World --- -- local worldMetaTable = { __index = tiny }
local World = class()
-- Initializes a World. --- World functions.
function World:init(...) -- A World is a container that manages Entities and Systems. The tiny-ecs module
-- is set to be the `__index` of all World tables, so the often clearer syntax of
-- World:method can be used for any function in the library. For example,
-- `tiny.add(world, e1, e2, e3)` is the same as `world:add(e1, e2, e3).`
-- @section World
-- Table of Entities to status --- Creates a new World.
self.status = {} -- Can optionally add default Systems and Entities.
-- @param ... Systems and Entities to add to the World
-- @return A new World
function tiny.world(...)
-- Set of Entities local ret = {
self.entities = {}
-- Number of Entities in World. -- Table of Entities to status
self.entityCount = 0 status = {},
-- Number of Systems in World. -- Set of Entities
self.systemCount = 0 entities = {},
-- List of Systems -- Number of Entities in World.
self.systems = {} entityCount = 0,
-- Table of Systems to whether or not they are active. -- Number of Systems in World.
self.activeSystems = {} systemCount = 0,
-- Table of Systems to System Indices -- List of Systems
self.systemIndices = {} systems = {},
-- Table of Systems to Sets of matching Entities -- Table of Systems to whether or not they are active.
self.systemEntities = {} activeSystems = {},
-- List of Systems to add next update -- Table of Systems to System Indices
self.systemsToAdd = {} systemIndices = {},
-- List of Systems to remove next update -- Table of Systems to Sets of matching Entities
self.systemsToRemove = {} systemEntities = {},
-- List of Systems to add next update
systemsToAdd = {},
-- List of Systems to remove next update
systemsToRemove = {}
}
tiny.add(ret, ...)
tiny.manageSystems(ret)
tiny.manageEntities(ret)
setmetatable(ret, worldMetaTable)
return ret
-- Add Systems and Entities
self:add(...)
self:manageSystems()
self:manageEntities()
end end
function World:__tostring() --- Adds Entities and Systems to the World.
return "TinyWorld<systemCount: " .. -- New objects will enter the World the next time World:update(dt) is called.
self.systemCount .. -- Also call this method when an Entity has had its Components changed, such
", entityCount: " .. -- that it matches different Filters.
self.entityCount .. -- @param world
">" -- @param ... Systems and Entities
end function tiny.add(world, ...)
-- World:add(...)
-- Adds Entities and Systems to the World. New objects will enter the World the
-- next time World:update(dt) is called. Also call this method when an Entity
-- has had its Components changed, such that it matches different Filters.
function World:add(...)
local args = {...} local args = {...}
local status = self.status local status = world.status
local entities = self.entities local entities = world.entities
local systemsToAdd = self.systemsToAdd local systemsToAdd = world.systemsToAdd
for _, obj in ipairs(args) do for _, obj in ipairs(args) do
if getmetatable(obj) == System then if getmetatable(obj) == systemMetaTable then
tinsert(systemsToAdd, obj) tinsert(systemsToAdd, obj)
else -- Assume obj is an Entity else -- Assume obj is an Entity
entities[obj] = true entities[obj] = true
@@ -120,60 +180,63 @@ function World:add(...)
end end
end end
end end
tiny_add = tiny.add
-- World:free(...) --- Removes Entities and Systems from the World. Objects will exit the World the
-- Removes Entities and Systems from the World. Objects will exit the World the
-- next time World:update(dt) is called. -- next time World:update(dt) is called.
function World:remove(...) -- @param world
-- @param ... Systems and Entities
function tiny.remove(world, ...)
local args = {...} local args = {...}
local status = self.status local status = world.status
local entities = self.entities local entities = world.entities
local systemsToRemove = self.systemsToRemove local systemsToRemove = world.systemsToRemove
for _, obj in ipairs(args) do for _, obj in ipairs(args) do
if getmetatable(obj) == System then if getmetatable(obj) == systemMetaTable then
tinsert(systemsToRemove, obj) tinsert(systemsToRemove, obj)
elseif entities[obj] then -- Assume obj is an Entity elseif entities[obj] then -- Assume obj is an Entity
status[obj] = "remove" status[obj] = "remove"
end end
end end
end end
tiny_remove = tiny.remove
-- World:updateSystem(system, dt) --- Updates a System.
-- @param world
-- @param system A System in the World to update
-- @param dt Delta time
function tiny.updateSystem(world, system, dt)
local callback = system.callback
local entityCallback = system.entityCallback
-- Updates a System if callback then
function World:updateSystem(system, dt) callback(dt)
local preupdate = system.preupdate
local update = system.update
if preupdate then
preupdate(dt)
end end
if update then if entityCallback then
local entities = self.entities local entities = world.entities
local es = self.systemEntities[system] local es = world.systemEntities[system]
if es then if es then
for e in pairs(es) do for e in pairs(es) do
update(e, dt) entityCallback(e, dt)
end end
end end
end end
end end
tiny_updateSystem = tiny.updateSystem
-- World:manageSystems() --- Adds and removes Systems that have been marked from the World.
-- The user of this library should seldom if ever call this.
-- @param world
function tiny.manageSystems(world)
-- Adds and removes Systems that have been marked from the world. The user of local systemEntities = world.systemEntities
-- this library should seldom if ever call this. local systemIndices = world.systemIndices
function World:manageSystems() local entities = world.entities
local systems = world.systems
local systemEntities = self.systemEntities local systemsToAdd = world.systemsToAdd
local systemIndices = self.systemIndices local systemsToRemove = world.systemsToRemove
local entities = self.entities local activeSystems = world.activeSystems
local systems = self.systems
local systemsToAdd = self.systemsToAdd
local systemsToRemove = self.systemsToRemove
local activeSystems = self.activeSystems
-- Keep track of the number of Systems in the world -- Keep track of the number of Systems in the world
local deltaSystemCount = 0 local deltaSystemCount = 0
@@ -188,10 +251,10 @@ function World:manageSystems()
if sysID then if sysID then
tremove(systems, sysID) tremove(systems, sysID)
local removeCallback = sys.remove local onRemove = sys.onRemove
if removeCallback then -- call 'remove' on all entities in the System if onRemove then
for e in pairs(systemEntities[sys]) do for e in pairs(systemEntities[sys]) do
removeCallback(e) onRemove(e)
end end
end end
@@ -214,10 +277,10 @@ function World:manageSystems()
systemIndices[sys] = #systems systemIndices[sys] = #systems
activeSystems[sys] = true activeSystems[sys] = true
local a = sys.filter local filter = sys.filter
if a then if filter then
for e in pairs(entities) do for e in pairs(entities) do
es[e] = a(e) and true or nil es[e] = filter(e) and true or nil
end end
end end
@@ -225,19 +288,19 @@ function World:manageSystems()
end end
-- Update the number of Systems in the World -- Update the number of Systems in the World
self.systemCount = self.systemCount + deltaSystemCount world.systemCount = world.systemCount + deltaSystemCount
end end
tiny_manageSystems = tiny.manageSystems
-- World:manageEntities() --- Adds and removes Entities that have been marked.
-- The user of this library should seldom if ever call this.
-- @param world
function tiny.manageEntities(world)
-- Adds and removes Entities that have been marked. The user of this library local statuses = world.status
-- should seldom if ever call this. local systemEntities = world.systemEntities
function World:manageEntities() local entities = world.entities
local systems = world.systems
local statuses = self.status
local systemEntities = self.systemEntities
local entities = self.entities
local systems = self.systems
-- Keep track of the number of Entities in the World -- Keep track of the number of Entities in the World
local deltaEntityCount = 0 local deltaEntityCount = 0
@@ -250,9 +313,9 @@ function World:manageEntities()
local filter = sys.filter local filter = sys.filter
if filter then if filter then
local matches = filter(e) and true or nil local matches = filter(e) and true or nil
local addCallback = sys.add local onAdd = sys.onAdd
if addCallback and matches and not es[e] then if onAdd and matches and not es[e] then
addCallback(e) onAdd(e)
end end
es[e] = matches es[e] = matches
end end
@@ -261,9 +324,9 @@ function World:manageEntities()
deltaEntityCount = deltaEntityCount - 1 deltaEntityCount = deltaEntityCount - 1
entities[e] = nil entities[e] = nil
for sys, es in pairs(systemEntities) do for sys, es in pairs(systemEntities) do
local removec = sys.remove local onRemove = sys.onRemove
if es[e] and removec then if es[e] and onRemove then
removec(e) onRemove(e)
end end
es[e] = nil es[e] = nil
end end
@@ -272,116 +335,61 @@ function World:manageEntities()
end end
-- Update Entity count -- Update Entity count
self.entityCount = self.entityCount + deltaEntityCount world.entityCount = world.entityCount + deltaEntityCount
end end
tiny_manageEntities = tiny.manageEntities
-- World:update() --- Updates the World.
-- Frees Entities that have been marked for freeing, adds
-- Updates the World, frees Entities that have been marked for freeing, adds
-- entities that have been marked for adding, etc. -- entities that have been marked for adding, etc.
function World:update(dt) -- @param world
-- @param dt Delta time
function tiny.update(world, dt)
self:manageSystems() tiny_manageSystems(world)
self:manageEntities() tiny_manageEntities(world)
-- Iterate through Systems IN ORDER -- Iterate through Systems IN ORDER
for _, s in ipairs(self.systems) do for _, s in ipairs(world.systems) do
if self.activeSystems[s] then if world.activeSystems[s] then
self:updateSystem(s, dt) tiny_updateSystem(world, s, dt)
end end
end end
end end
-- World:clearEntities() --- Removes all Entities from the World.
-- When World:update(dt) is next called,
-- Removes all Entities from the World. When World:update(dt) is next called,
-- all Entities will be removed. -- all Entities will be removed.
function World:clearEntities() -- @param world
local status = self.status function tiny.clearEntities(world)
for e in pairs(self.entities) do local status = world.status
for e in pairs(world.entities) do
status[e] = "remove" status[e] = "remove"
end end
end end
-- World:clearSystems() --- Removes all Systems from the World.
-- When World:update(dt) is next called,
-- Removes all Systems from the World. When World:update(dt) is next called,
-- all Systems will be removed. -- all Systems will be removed.
function World:clearSystems() -- @param world
function tiny.clearSystems(world)
local newSystemsToRemove = {} local newSystemsToRemove = {}
local systems = self.systems local systems = world.systems
for i = 1, #systems do for i = 1, #systems do
newSystemsToRemove[i] = systems[i] newSystemsToRemove[i] = systems[i]
end end
self.systemsToRemove = newSystemsToRemove world.systemsToRemove = newSystemsToRemove
end end
-- World:setSystemActive(system, active) --- Sets if a System is active in a world. If the system is active, it will
-- Sets if a System is active in a world. If the system is active, it will
-- update automatically when World:update(dt) is called. Otherwise, the user -- update automatically when World:update(dt) is called. Otherwise, the user
-- must call World:updateSystem(system, dt) to update the unactivated system. -- must call World:updateSystem(system, dt) to update the unactivated system.
function World:setSystemActive(system, active) -- @param world
self.activeSystem[system] = active and true or nil -- @param system A System in the World activate/deactivate
end -- @param active Boolean new state of the System
function tiny.setSystemActive(world, system, active)
-- --- Top Level module functions --- -- world.activeSystem[system] = active and true or nil
--- Creates a new tiny-ecs World.
function tiny.newWorld(...)
return World(...)
end
--- Makes a Filter that filters Entities with specified Components.
-- An Entity must have all Components to match the filter.
function tiny.requireAll(...)
local components = {...}
local len = #components
return function(e)
local c
for i = 1, len do
c = components[i]
if e[c] == nil then
return false
end
end
return true
end
end
--- Makes a Filter that filters Entities with specified Components.
-- An Entity must have at least one specified Component to match the filter.
function tiny.requireOne(...)
local components = {...}
local len = #components
return function(e)
local c
for i = 1, len do
c = components[i]
if e[c] ~= nil then
return true
end
end
return false
end
end
--- Creates a System that doesn't update any Entities, but executes a callback
-- once per update.
function tiny.emptySystem(callback)
return System(callback)
end
--- Creates a System that processes Entities every update. Also provides
-- optional callbacks for when Entities are added or removed from the System.
function tiny.processingSystem(filter, entityCallback, onAdd, onRemove)
return System(nil, filter, entityCallback, onAdd, onRemove)
end
--- Creates a System.
function tiny.system(callback, filter, entityCallback, onAdd, onRemove)
return System(callback, filter, entityCallback, onAdd, onRemove)
end end
return tiny return tiny