12 Commits

5 changed files with 254 additions and 117 deletions
+39
View File
@@ -0,0 +1,39 @@
language: erlang
env:
global:
- LUAROCKS_BASE=luarocks-2.2.2
matrix:
- LUA=lua5.1 LUA_DEV=liblua5.1-dev LUA_VER=5.1 LUA_SFX=5.1 LUA_INCDIR=/usr/include/lua5.1
- LUA=lua5.2 LUA_DEV=liblua5.2-dev LUA_VER=5.2 LUA_SFX=5.2 LUA_INCDIR=/usr/include/lua5.2
- LUA=luajit LUA_DEV=libluajit-5.1-dev LUA_VER=5.1 LUA_SFX=jit LUA_INCDIR=/usr/include/luajit-2.0
before_install:
- if [ $LUA = "luajit" ]; then
sudo add-apt-repository ppa:mwild1/ppa -y && sudo apt-get update -y;
sudo apt-get install lua5.1;
sudo apt-get install liblua5.1-dev;
fi
- sudo apt-get install $LUA
- sudo apt-get install $LUA_DEV
- lua$LUA_SFX -v
# Install a recent luarocks release
- wget http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz
- tar zxvpf $LUAROCKS_BASE.tar.gz
- cd $LUAROCKS_BASE
- ./configure
--lua-version=$LUA_VER --lua-suffix=$LUA_SFX --with-lua-include="$LUA_INCDIR"
- sudo make
- sudo make install
- cd $TRAVIS_BUILD_DIR
install:
- sudo -E luarocks install busted
script:
- sudo -E busted -v
notifications:
email:
on_success: change
on_failure: always
+2
View File
@@ -1,5 +1,7 @@
# tiny-ecs # # tiny-ecs #
[![Build Status](https://travis-ci.org/bakpakin/tiny-ecs.png?branch=master)](https://travis-ci.org/bakpakin/tiny-ecs)
[![License](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENSE) [![License](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENSE)
Tiny-ecs is an Entity Component System for lua that's simple, flexible, and useful. Tiny-ecs is an Entity Component System for lua that's simple, flexible, and useful.
+24
View File
@@ -0,0 +1,24 @@
package = "tiny-ecs"
version = "1.2-1"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.2-1"
}
description = {
summary = "Entity Component System for Lua.",
detailed = [[
Pure Lua implementation of an easy to use, compact, fast, and flexible
Entity Component System. Works well with Object Orientation.
]],
homepage = "https://github.com/bakpakin/tiny-ecs",
license = "MIT"
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "builtin",
modules = {
tiny = "tiny.lua"
}
}
+16 -2
View File
@@ -1,3 +1,8 @@
local GLOBALS = {}
for k, v in pairs(_G) do
GLOBALS[k] = v
end
local tiny = require "tiny" local tiny = require "tiny"
local function deep_copy(x) local function deep_copy(x)
@@ -115,6 +120,11 @@ describe('tiny-ecs:', function()
timePassed = 0 timePassed = 0
end) end)
after_each(function()
world:clearSystems()
world:refresh()
end)
it("Create World", function() it("Create World", function()
assert.equals(world:getEntityCount(), 3) assert.equals(world:getEntityCount(), 3)
assert.equals(world:getSystemCount(), 2) assert.equals(world:getSystemCount(), 2)
@@ -191,7 +201,7 @@ describe('tiny-ecs:', function()
it("Add Systems Multiple Times", function() it("Add Systems Multiple Times", function()
world:update(1) world:update(1)
world:add(moveSystem, oneTimeSystem) assert.has_error(function() world:add(moveSystem, oneTimeSystem) end, "System already belongs to a World.")
world:update(2) world:update(2)
assert.equals(2, world:getSystemCount()) assert.equals(2, world:getSystemCount())
assert.equals(3, world:getEntityCount()) assert.equals(3, world:getEntityCount())
@@ -201,7 +211,7 @@ describe('tiny-ecs:', function()
world:update(1) world:update(1)
world:remove(moveSystem) world:remove(moveSystem)
world:update(2) world:update(2)
world:remove(moveSystem) assert.has_error(function() world:remove(moveSystem) end, "System does not belong to this World.")
world:update(2) world:update(2)
assert.equals(1, world:getSystemCount()) assert.equals(1, world:getSystemCount())
assert.equals(3, world:getEntityCount()) assert.equals(3, world:getEntityCount())
@@ -230,4 +240,8 @@ describe('tiny-ecs:', function()
end) end)
it("Doesn't pollute the global namespace", function()
assert.are.same(_G, GLOBALS)
end)
end) end)
+173 -115
View File
@@ -23,7 +23,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- @author Calvin Rose -- @author Calvin Rose
-- @license MIT -- @license MIT
-- @copyright 2015 -- @copyright 2015
local tiny = { _VERSION = "1.1-7" } local tiny = { _VERSION = "1.2-1" }
-- Local versions of standard lua functions -- Local versions of standard lua functions
local tinsert = table.insert local tinsert = table.insert
@@ -187,6 +187,10 @@ end
-- from the System. -- from the System.
-- * `function system:onModify(dt)` - Called when the System is modified by -- * `function system:onModify(dt)` - Called when the System is modified by
-- adding or removing Entities from the System. -- adding or removing Entities from the System.
-- * `function system:onAddToWorld(world)` - Called when the System is added
-- to the World, before any entities are added to the system.
-- * `function system:onRemoveFromWorld(world)` - Called when the System is
-- removed from the world, after all Entities are removed from the System.
-- --
-- For Filters, it is convenient to use `tiny.requireAll` or `tiny.requireAny`, -- For Filters, it is convenient to use `tiny.requireAll` or `tiny.requireAny`,
-- but one can write their own filters as well. Set the Filter of a System like -- but one can write their own filters as well. Set the Filter of a System like
@@ -235,20 +239,19 @@ end
-- Update function for all Processing Systems. -- Update function for all Processing Systems.
local function processingSystemUpdate(system, dt) local function processingSystemUpdate(system, dt)
local entities = system.entities
local preProcess = system.preProcess local preProcess = system.preProcess
local process = system.process local process = system.process
local postProcess = system.postProcess local postProcess = system.postProcess
local entity
if preProcess then if preProcess then
preProcess(system, dt) preProcess(system, dt)
end end
if process then if process then
local entities = system.entities
local len = #entities local len = #entities
for i = 1, len do for i = 1, len do
entity = entities[i] local entity = entities[i]
process(system, entity, dt) process(system, entity, dt)
end end
end end
@@ -290,9 +293,9 @@ end
-- Processing Systems have three extra callbacks besides those inheritted from -- Processing Systems have three extra callbacks besides those inheritted from
-- vanilla Systems. -- vanilla Systems.
-- --
-- function system:preProcess(entities, dt) -- Called before iteration. -- function system:preProcess(dt) -- Called before iteration.
-- function system:process(entities, dt) -- Process each entity. -- function system:process(entity, dt) -- Process each entity.
-- function system:postProcess(entity, dt) -- Called after iteration. -- function system:postProcess(dt) -- Called after iteration.
-- --
-- Processing Systems have their own `update` method, so don't implement a -- Processing Systems have their own `update` method, so don't implement a
-- a custom `update` callback for Processing Systems. -- a custom `update` callback for Processing Systems.
@@ -348,7 +351,7 @@ local worldMetaTable
-- Can optionally add default Systems and Entities. Returns the new World along -- Can optionally add default Systems and Entities. Returns the new World along
-- with default Entities and Systems. -- with default Entities and Systems.
function tiny.world(...) function tiny.world(...)
local ret = { local ret = setmetatable({
-- List of Entities to add -- List of Entities to add
entitiesToAdd = {}, entitiesToAdd = {},
@@ -356,6 +359,9 @@ function tiny.world(...)
-- List of Entities to remove -- List of Entities to remove
entitiesToRemove = {}, entitiesToRemove = {},
-- List of Entities to change
entitiesToChange = {},
-- List of Entities to add -- List of Entities to add
systemsToAdd = {}, systemsToAdd = {},
@@ -370,23 +376,25 @@ function tiny.world(...)
-- List of Systems -- List of Systems
systems = {} systems = {}
} }, worldMetaTable)
tiny_add(ret, ...) tiny_add(ret, ...)
tiny_manageSystems(ret) tiny_manageSystems(ret)
tiny_manageEntities(ret) tiny_manageEntities(ret)
return setmetatable(ret, worldMetaTable), ... return ret, ...
end end
--- Adds an Entity to the world. --- Adds an Entity to the world.
-- Also call this on Entities that have changed Components such that they -- Also call this on Entities that have changed Components such that they
-- match different Filters. Returns the Entity. -- match different Filters. Returns the Entity.
function tiny.addEntity(world, entity) function tiny.addEntity(world, entity)
local e2a = world.entitiesToAdd
e2a[#e2a + 1] = entity
if world.entities[entity] then if world.entities[entity] then
tiny_removeEntity(world, entity) local e2c = world.entitiesToChange
e2c[#e2c + 1] = entity
else
local e2a = world.entitiesToAdd
e2a[#e2a + 1] = entity
end end
return entity return entity
end end
@@ -394,8 +402,10 @@ tiny_addEntity = tiny.addEntity
--- Adds a System to the world. Returns the System. --- Adds a System to the world. Returns the System.
function tiny.addSystem(world, system) function tiny.addSystem(world, system)
assert(system.world == nil, "System already belongs to a World.")
local s2a = world.systemsToAdd local s2a = world.systemsToAdd
s2a[#s2a + 1] = system s2a[#s2a + 1] = system
system.world = world
return system return system
end end
tiny_addSystem = tiny.addSystem tiny_addSystem = tiny.addSystem
@@ -403,9 +413,8 @@ tiny_addSystem = tiny.addSystem
--- Shortcut for adding multiple Entities and Systems to the World. Returns all --- Shortcut for adding multiple Entities and Systems to the World. Returns all
-- added Entities and Systems. -- added Entities and Systems.
function tiny.add(world, ...) function tiny.add(world, ...)
local obj
for i = 1, select("#", ...) do for i = 1, select("#", ...) do
obj = select(i, ...) local obj = select(i, ...)
if obj then if obj then
if isSystem(obj) then if isSystem(obj) then
tiny_addSystem(world, obj) tiny_addSystem(world, obj)
@@ -428,6 +437,7 @@ tiny_removeEntity = tiny.removeEntity
--- Removes a System from the world. Returns the System. --- Removes a System from the world. Returns the System.
function tiny.removeSystem(world, system) function tiny.removeSystem(world, system)
assert(system.world == world, "System does not belong to this World.")
local s2r = world.systemsToRemove local s2r = world.systemsToRemove
s2r[#s2r + 1] = system s2r[#s2r + 1] = system
return system return system
@@ -437,14 +447,13 @@ tiny_removeSystem = tiny.removeSystem
--- Shortcut for removing multiple Entities and Systems from the World. Returns --- Shortcut for removing multiple Entities and Systems from the World. Returns
-- all removed Systems and Entities -- all removed Systems and Entities
function tiny.remove(world, ...) function tiny.remove(world, ...)
local obj
for i = 1, select("#", ...) do for i = 1, select("#", ...) do
obj = select(i, ...) local obj = select(i, ...)
if obj then if obj then
if isSystem(obj) 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)
end end
end end
end end
@@ -454,153 +463,196 @@ tiny_remove = tiny.remove
-- Adds and removes Systems that have been marked from the World. -- Adds and removes Systems that have been marked from the World.
function tiny_manageSystems(world) function tiny_manageSystems(world)
local s2a, s2r = world.systemsToAdd, world.systemsToRemove local s2a, s2r = world.systemsToAdd, world.systemsToRemove
-- Early exit -- Early exit
if #s2a == 0 and #s2r == 0 then if #s2a == 0 and #s2r == 0 then
return return
end end
local entities = world.entities world.systemsToAdd = {}
local systems = world.systems world.systemsToRemove = {}
local system, index, filter
local entityList, entityIndices, entityIndex, onRemove, onAdd
-- Remove Systems local entities = world.entities
for i = 1, #s2r do local systems = world.systems
system = s2r[i]
index = system.index -- Remove Systems
if system.world == world then for i = 1, #s2r do
onRemove = system.onRemove local system = s2r[i]
if onRemove then local index = system.index
entityList = system.entities local onRemove = system.onRemove
for j = 1, #entityList do if onRemove then
onRemove(system, entityList[j]) local entityList = system.entities
end for j = 1, #entityList do
end onRemove(system, entityList[j])
tremove(systems, index)
for j = index, #systems do
systems[j].index = j
end
end end
s2r[i] = nil
-- Clean up System
system.world = nil
system.entities = nil
system.indices = nil
system.index = nil
end end
tremove(systems, index)
for j = index, #systems do
systems[j].index = j
end
local onRemoveFromWorld = system.onRemoveFromWorld
if onRemoveFromWorld then
onRemoveFromWorld(system, world)
end
s2r[i] = nil
-- Add Systems -- Clean up System
for i = 1, #s2a do system.world = nil
system = s2a[i] system.entities = nil
if systems[system.index] ~= system then system.indices = nil
entityList = {} system.index = nil
entityIndices = {} end
system.entities = entityList
system.indices = entityIndices
if system.active == nil then
system.active = true
end
system.modified = true
system.world = world
index = #systems + 1
system.index = index
systems[index] = system
-- Try to add Entities -- Add Systems
onAdd = system.onAdd for i = 1, #s2a do
filter = system.filter local system = s2a[i]
if filter then if systems[system.index] ~= system then
for entity in pairs(entities) do local entityList = {}
if filter(system, entity) then local entityIndices = {}
entityIndex = #entityList + 1 system.entities = entityList
entityList[entityIndex] = entity system.indices = entityIndices
entityIndices[entity] = entityIndex if system.active == nil then
if onAdd then system.active = true
onAdd(system, entity) end
end system.modified = true
system.world = world
local index = #systems + 1
system.index = index
systems[index] = system
local onAddToWorld = system.onAddToWorld
if onAddToWorld then
onAddToWorld(system, world)
end
-- Try to add Entities
local onAdd = system.onAdd
local filter = system.filter
if filter then
for entity in pairs(entities) do
if filter(system, entity) then
local entityIndex = #entityList + 1
entityList[entityIndex] = entity
entityIndices[entity] = entityIndex
if onAdd then
onAdd(system, entity)
end end
end end
end end
end end
s2a[i] = nil
end end
s2a[i] = nil
end
end end
-- Adds and removes Entities that have been marked. -- Adds, removes, and changes Entities that have been marked.
function tiny_manageEntities(world) function tiny_manageEntities(world)
local e2a, e2r = world.entitiesToAdd, world.entitiesToRemove
local e2a = world.entitiesToAdd
local e2r = world.entitiesToRemove
local e2c = world.entitiesToChange
-- Early exit -- Early exit
if #e2a == 0 and #e2r == 0 then if #e2a == 0 and #e2r == 0 and #e2c == 0 then
return return
end end
world.entitiesToChange = {}
world.entitiesToAdd = {}
world.entitiesToRemove = {}
local entities = world.entities local entities = world.entities
local systems = world.systems local systems = world.systems
local entityCount = world.entityCount local entityCount = world.entityCount
local entity, system, index
local onRemove, onAdd, ses, seis, filter, tmpEntity
-- Remove Entities -- Change Entities
for i = 1, #e2r do for i = 1, #e2c do
entity = e2r[i] local entity = e2c[i]
if entities[entity] then if entities[entity] then
entities[entity] = nil
entityCount = entityCount - 1
for j = 1, #systems do for j = 1, #systems do
system = systems[j] local system = systems[j]
ses = system.entities local ses = system.entities
seis = system.indices local seis = system.indices
index = seis[entity] local index = seis[entity]
local filter = system.filter
if index then if filter and filter(system, entity) then
if not index then
system.modified = true
index = #ses + 1
ses[index] = entity
seis[entity] = index
local onAdd = system.onAdd
if onAdd then
onAdd(system, entity)
end
end
elseif index then
system.modified = true system.modified = true
tmpEntity = ses[#ses] local tmpEntity = ses[#ses]
ses[index] = tmpEntity ses[index] = tmpEntity
seis[tmpEntity] = index seis[tmpEntity] = index
seis[entity] = nil seis[entity] = nil
ses[#ses] = nil ses[#ses] = nil
onRemove = system.onRemove local onRemove = system.onRemove
if onRemove then if onRemove then
onRemove(system, entity) onRemove(system, entity)
end end
end end
end end
end
e2c[i] = nil
end
-- Remove Entities
for i = 1, #e2r do
local entity = e2r[i]
if entities[entity] then
entities[entity] = nil
entityCount = entityCount - 1
for j = 1, #systems do
local system = systems[j]
local ses = system.entities
local seis = system.indices
local index = seis[entity]
if index then
system.modified = true
local tmpEntity = ses[#ses]
ses[index] = tmpEntity
seis[tmpEntity] = index
seis[entity] = nil
ses[#ses] = nil
local onRemove = system.onRemove
if onRemove then
onRemove(system, entity)
end
end
end
end end
e2r[i] = nil e2r[i] = nil
end end
-- Add Entities -- Add Entities
for i = 1, #e2a do for i = 1, #e2a do
entity = e2a[i] local entity = e2a[i]
if not entities[entity] then if not entities[entity] then
entities[entity] = true entities[entity] = true
entityCount = entityCount + 1 entityCount = entityCount + 1
for j = 1, #systems do for j = 1, #systems do
system = systems[j] local system = systems[j]
ses = system.entities local ses = system.entities
seis = system.indices local seis = system.indices
filter = system.filter local filter = system.filter
if filter and filter(system, entity) then if filter and filter(system, entity) then
system.modified = true system.modified = true
index = #ses + 1 local index = #ses + 1
ses[index] = entity ses[index] = entity
seis[entity] = index seis[entity] = index
onAdd = system.onAdd local onAdd = system.onAdd
if onAdd then if onAdd then
onAdd(system, entity) onAdd(system, entity)
end end
end end
end end
end end
e2a[i] = nil e2a[i] = nil
end end
@@ -627,23 +679,22 @@ function tiny.update(world, dt, filter)
tiny_manageEntities(world) tiny_manageEntities(world)
local systems = world.systems local systems = world.systems
local system, update, interval, onModify
-- Iterate through Systems IN ORDER -- Iterate through Systems IN ORDER
for i = 1, #systems do for i = 1, #systems do
system = systems[i] local system = systems[i]
if system.active and ((not filter) or filter(world, system)) then if system.active and ((not filter) or filter(world, system)) then
-- Call the modify callback on Systems that have been modified. -- Call the modify callback on Systems that have been modified.
onModify = system.onModify local onModify = system.onModify
if onModify and system.modified then if onModify and system.modified then
onModify(system, dt) onModify(system, dt)
end end
-- Update Systems that have an update method (most Systems) -- Update Systems that have an update method (most Systems)
update = system.update local update = system.update
if update then if update then
interval = system.interval local interval = system.interval
if interval then if interval then
local bufferedTime = (system.bufferedTime or 0) + dt local bufferedTime = (system.bufferedTime or 0) + dt
while bufferedTime >= interval do while bufferedTime >= interval do
@@ -701,6 +752,10 @@ function tiny.setSystemIndex(world, system, index)
local oldIndex = system.index local oldIndex = system.index
local systems = world.systems local systems = world.systems
if index < 0 then
index = tiny.getSystemCount(world) + 1 + index
end
tremove(systems, oldIndex) tremove(systems, oldIndex)
tinsert(systems, index, system) tinsert(systems, index, system)
@@ -728,7 +783,10 @@ worldMetaTable = {
getSystemCount = tiny.getSystemCount, getSystemCount = tiny.getSystemCount,
getSystemIndex = tiny.getSystemIndex, getSystemIndex = tiny.getSystemIndex,
setSystemIndex = tiny.setSystemIndex setSystemIndex = tiny.setSystemIndex
} },
__tostring = function(self)
return "<tiny-ecs_World>"
end
} }
return tiny return tiny