38 Commits

Author SHA1 Message Date
bakpakin
2403a9019c Release version 1.2-1. 2015-09-08 16:25:10 -04:00
bakpakin
6234e4b741 Document onAddToWorld and onRemoveFromWorld. 2015-09-08 16:19:07 -04:00
bakpakin
b870b5a34e Add onAddToWorld and onRemoveFromWorld callbacks to systems. Also, adding or removing systems to a world twice raises an error. 2015-09-08 13:37:53 -04:00
bakpakin
f3b16a40f6 Add some simple assertions to adding and removing systems. 2015-09-08 13:22:07 -04:00
bakpakin
5035d2c8a7 Make the scope of many local variables smaller. 2015-08-27 14:55:24 -04:00
bakpakin
18270cd60a Adding already added entities no longer removes them from the World first, just updates them to relavent Systems. 2015-08-27 14:31:45 -04:00
bakpakin
dba8e7c89f Remove indication of a versioned release in tiny.lua. 2015-08-27 13:51:13 -04:00
bakpakin
fccff0329e Add travis to tiny-ecs. 2015-08-27 13:47:28 -04:00
bakpakin
8a759edca2 Fix docs again. *sigh* 2015-08-27 13:32:51 -04:00
bakpakin
d9d311d858 Fix system docs. 2015-08-27 13:26:09 -04:00
bakpakin
9834dfa9d6 Fix Adding systems and items to the world inside a call to system:onAdd() 2015-08-24 12:52:11 -04:00
bakpakin
a5af4b6ecd Index systems from the end like string.sub. 2015-08-22 22:45:07 -04:00
bakpakin
f0e6ce7880 Update to version 1.1-7. 2015-08-05 21:01:53 -04:00
bakpakin
26f8259f2b Add tiny.refresh for updating a World without updating Systems. 2015-07-29 12:40:13 -04:00
bakpakin
b1c7e2ba83 Update to version 1.1-6. 2015-07-07 19:47:06 -04:00
bakpakin
6b549fcbc1 Readd tiny.getSystemIndex(world, system) 2015-07-07 19:26:12 -04:00
bakpakin
b9d994ad50 Update documentation for what functions return.
Remove useless code in world metatable
Make tiny.world(...) return newworld, ...
2015-07-07 19:05:56 -04:00
bakpakin
ce8ca35f7e Remove world.systemIndices to fix possible obscure bugs.
Remove tiny.getSystemIndex(world, system) in favor of a System 'index'
field.
Add some comments and documentation.
2015-07-06 23:28:50 -04:00
bakpakin
19ae03707f Make tiny.add, tiny.remove, and similar methods return entities and systems. 2015-07-06 22:01:06 -04:00
bakpakin
c1f1225248 Update to version 1.1-5. 2015-06-21 14:48:48 -04:00
bakpakin
45e8552054 Fix entity count in worlds. 2015-06-21 14:42:03 -04:00
bakpakin
f187dce4fd Update documentation. 2015-06-19 19:45:26 -04:00
bakpakin
927652597c Update to version 1.1-4. 2015-06-19 19:08:06 -04:00
bakpakin
6b3af0f2da Remove 2.0-1 from existence. 2015-06-19 18:55:06 -04:00
bakpakin
31f3396db1 Change System API again.
Remove tiny.getSystemEntityCount because it's useless and deprecation is for chumps.
2015-06-19 18:48:18 -04:00
bakpakin
cb9ab332d2 Fix sorting + add sorting test. 2015-06-18 16:02:16 -04:00
bakpakin
1fd32dab9a Add "world" flag in Systems. Clean up Systems when removing them from Worlds. 2015-06-18 15:19:21 -04:00
bakpakin
0a12897695 Correct use of 'self' in intervalSystemUpdate 2015-06-17 10:35:47 -04:00
bakpakin
e44ac6cdb3 Update to version 2.0-1 2015-06-16 23:48:57 -04:00
bakpakin
6f299470d0 Add CHANGELOG.md 2015-06-16 23:44:19 -04:00
bakpakin
61ffd2551b Readd tiny.processingSystem(table). 2015-06-16 23:26:48 -04:00
bakpakin
c66ed98a5a Add License sticker to README.md. 2015-06-16 23:13:53 -04:00
bakpakin
2621bd16a3 Add tiny.getSystemEntityCount(system). 2015-06-16 22:51:23 -04:00
bakpakin
c408267932 Update documentation. 2015-06-16 22:39:10 -04:00
bakpakin
557927577d Fix sorting. Change API for creating Systems to be more flexible. 2015-06-16 21:15:30 -04:00
bakpakin
cbfc3d360f Add attributes to tiny.system. Implement Interval Systems. 2015-06-16 20:28:21 -04:00
bakpakin
d35d62ed3c Add optional filter to tiny.update(world, dt, [filter]) 2015-06-16 19:45:09 -04:00
bakpakin
74666ca0c3 Fix adding inactive Systems. 2015-06-16 19:14:56 -04:00
10 changed files with 496 additions and 245 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
+7 -1
View File
@@ -1,4 +1,9 @@
# 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)
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.
Because of lua's tabular nature, Entity Component Systems are a natural choice Because of lua's tabular nature, Entity Component Systems are a natural choice
for simulating large and complex systems. For more explanation on Entity for simulating large and complex systems. For more explanation on Entity
@@ -75,7 +80,7 @@ Tiny-ecs is also on [Luarocks](https://luarocks.org/) and can be installed with
## Demo ## ## Demo ##
Check out the [demo](https://github.com/bakpakin/tiny-ecs/tree/demo-commandokibbles), a game Check out the [demo](https://github.com/bakpakin/tiny-ecs/tree/demo-commandokibbles), a game
originally written for Ludum Dare 32 with the theme 'An Unconventional Weapon'. The demo uses originally written for Ludum Dare 32 with the theme 'An Unconventional Weapon'. The demo uses
[LOVE](https://love2d.org/), an amazing game framework for lua. [LÖVE](https://love2d.org/), an amazing game framework for lua.
## Testing ## ## Testing ##
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
@@ -85,3 +90,4 @@ Tiny-ecs uses [busted](http://olivinelabs.com/busted/) for testing. Install and
See API [here](http://bakpakin.github.io/tiny-ecs/doc/). See API [here](http://bakpakin.github.io/tiny-ecs/doc/).
For the most up-to-date documentation, read the source code, or generate the HTML For the most up-to-date documentation, read the source code, or generate the HTML
locally with [LDoc](http://stevedonovan.github.io/ldoc/). locally with [LDoc](http://stevedonovan.github.io/ldoc/).
See the original forum thread [here](https://love2d.org/forums/viewtopic.php?f=5&t=79937&p=182589).
+1
View File
@@ -10,3 +10,4 @@ style = '!fixed'
package = 'tiny-ecs' package = 'tiny-ecs'
not_luadoc = true not_luadoc = true
boilerplate = true boilerplate = true
no_return_or_parms = true
+24
View File
@@ -0,0 +1,24 @@
package = "tiny-ecs"
version = "1.1-4"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.1-4"
}
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"
}
}
+24
View File
@@ -0,0 +1,24 @@
package = "tiny-ecs"
version = "1.1-5"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.1-5"
}
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"
}
}
+24
View File
@@ -0,0 +1,24 @@
package = "tiny-ecs"
version = "1.1-6"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.1-6"
}
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"
}
}
+24
View File
@@ -0,0 +1,24 @@
package = "tiny-ecs"
version = "1.1-7"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.1-7"
}
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"
}
}
+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"
}
}
+31 -4
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())
@@ -211,10 +221,27 @@ describe('tiny-ecs:', function()
world:update(1) world:update(1)
world:setSystemIndex(moveSystem, 2) world:setSystemIndex(moveSystem, 2)
world:update(1) world:update(1)
assert.equals(2, world:getSystemIndex(moveSystem)) assert.equals(2, moveSystem.index)
assert.equals(1, world:getSystemIndex(oneTimeSystem)) assert.equals(1, oneTimeSystem.index)
end)
it("Sorts Entities in Sorting Systems", function()
local sortsys = tiny.sortedProcessingSystem()
sortsys.filter = tiny.requireAll("vel")
function sortsys:compare(e1, e2)
return e1.vel.x < e2.vel.x
end
world:add(sortsys)
world:update(0)
assert.equals(sortsys.entities[1], entity2)
assert.equals(sortsys.entities[2], entity3)
assert.equals(sortsys.entities[3], entity1)
end) end)
end) end)
it("Doesn't pollute the global namespace", function()
assert.are.same(_G, GLOBALS)
end)
end) end)
+279 -221
View File
@@ -23,14 +23,13 @@ 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-3" } 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
local tremove = table.remove local tremove = table.remove
local tsort = table.sort local tsort = table.sort
local pairs = pairs local pairs = pairs
local ipairs = ipairs
local setmetatable = setmetatable local setmetatable = setmetatable
local type = type local type = type
local select = select local select = select
@@ -51,8 +50,8 @@ local tiny_remove
-- value indicating if the Entity should be processed by the System. -- value indicating if the Entity should be processed by the System.
-- --
-- Filters must be added to Systems by setting the `filter` field of the System. -- Filters must be added to Systems by setting the `filter` field of the System.
-- Filter's returned by `tiny.requireAll` and `tiny.requireAny` are immutable -- Filter's returned by tiny-ecs's Filter functions are immutable and can be
-- and can be used by multiple Systems. -- used by multiple Systems.
-- --
-- local f1 = tiny.requireAll("position", "velocity", "size") -- local f1 = tiny.requireAll("position", "velocity", "size")
-- local f2 = tiny.requireAny("position", "velocity", "size") -- local f2 = tiny.requireAny("position", "velocity", "size")
@@ -88,7 +87,6 @@ local tiny_remove
--- Makes a Filter that selects Entities with all specified Components and --- Makes a Filter that selects Entities with all specified Components and
-- Filters. -- Filters.
-- @param ... List of required Components and other Filters.
function tiny.requireAll(...) function tiny.requireAll(...)
local components = {...} local components = {...}
local len = #components local len = #components
@@ -110,7 +108,6 @@ end
--- Makes a Filter that selects Entities with at least one of the specified --- Makes a Filter that selects Entities with at least one of the specified
-- Components and Filters. -- Components and Filters.
-- @param ... List of required Components and other Filters.
function tiny.requireAny(...) function tiny.requireAny(...)
local components = {...} local components = {...}
local len = #components local len = #components
@@ -132,7 +129,6 @@ end
--- Makes a Filter that rejects Entities with all specified Components and --- Makes a Filter that rejects Entities with all specified Components and
-- Filters, and selects all other Entities. -- Filters, and selects all other Entities.
-- @param ... List of required Components and other Filters.
function tiny.rejectAll(...) function tiny.rejectAll(...)
local components = {...} local components = {...}
local len = #components local len = #components
@@ -154,7 +150,6 @@ end
--- Makes a Filter that rejects Entities with at least one of the specified --- Makes a Filter that rejects Entities with at least one of the specified
-- Components and Filters, and selects all other Entities. -- Components and Filters, and selects all other Entities.
-- @param ... List of required Components and other Filters.
function tiny.rejectAny(...) function tiny.rejectAny(...)
local components = {...} local components = {...}
local len = #components local len = #components
@@ -183,10 +178,19 @@ end
-- --
-- There are also a few other optional callbacks: -- There are also a few other optional callbacks:
-- --
-- * `function system:filter(entity)` -- * `function system:filter(entity)` - Returns true if this System should
-- * `function system:onAdd(entity)` -- include this Entity, otherwise should return false. If this isn't specified,
-- * `function system:onRemove(entity)` -- no Entities are included in the System.
-- * `function system:onModify(dt)` -- * `function system:onAdd(entity)` - Called when an Entity is added to the
-- System.
-- * `function system:onRemove(entity)` - Called when an Entity is removed
-- from the System.
-- * `function system:onModify(dt)` - Called when the System is modified by
-- 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
@@ -201,11 +205,20 @@ end
-- system is added to the World. A few are important, and few should be less -- system is added to the World. A few are important, and few should be less
-- commonly used. -- commonly used.
-- --
-- * The `world` field points to the World that the System belongs to. Useful
-- for adding and removing Entities from the world dynamically via the System.
-- * The `active` flag is whether or not the System is updated automatically. -- * The `active` flag is whether or not the System is updated automatically.
-- Inactive Systems should be updated manually or not at all via -- Inactive Systems should be updated manually or not at all via
-- `system:update(dt)`. Defaults to true. -- `system:update(dt)`. Defaults to true.
-- * The 'entities' field is an ordered list of Entities in the System. This -- * The `entities` field is an ordered list of Entities in the System. This
-- list can be used to quickly iterate through all Entities in a System. -- list can be used to quickly iterate through all Entities in a System.
-- * The `interval` field is an optional field that makes Systems update at
-- certain intervals using buffered time, regardless of World update frequency.
-- For example, to make a System update once a second, set the System's interval
-- to 1.
-- * The `index` field is the System's index in the World. Lower indexed
-- Systems are processed before higher indices. The `index` is a read only
-- field; to set the `index`, use `tiny.setSystemIndex(world, system)`.
-- * The `indices` field is a table of Entity keys to their indices in the -- * The `indices` field is a table of Entity keys to their indices in the
-- `entities` list. Most Systems can ignore this. -- `entities` list. Most Systems can ignore this.
-- * The `modified` flag is an indicator if the System has been modified in -- * The `modified` flag is an indicator if the System has been modified in
@@ -219,36 +232,26 @@ end
-- this key is considered a System rather than an Entity. -- this key is considered a System rather than an Entity.
local systemTableKey = { "SYSTEM_TABLE_KEY" } local systemTableKey = { "SYSTEM_TABLE_KEY" }
-- Check if tables are systems. -- Checks if a table is a System.
local function isSystem(table) local function isSystem(table)
return table[systemTableKey] return table[systemTableKey]
end end
--- Creates a default System.
-- @param table A table to be used as a System, or `nil` to create a new System.
-- @return A new System or System class
function tiny.system(table)
table = table or {}
table[systemTableKey] = true
return table
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
@@ -258,24 +261,45 @@ local function processingSystemUpdate(system, dt)
end end
end end
--- Creates a Processing System. -- Sorts Systems by a function system.sort(entity1, entity2) on modify.
local function sortedSystemOnModify(system, dt)
local entities = system.entities
local indices = system.indices
local sortDelegate = system.sortDelegate
if not sortDelegate then
local compare = system.compare
sortDelegate = function(e1, e2)
return compare(system, e1, e2)
end
system.sortDelegate = sortDelegate
end
tsort(entities, sortDelegate)
for i = 1, #entities do
local entity = entities[i]
indices[entity] = i
end
end
--- Creates a new System or System class from the supplied table. If `table` is
-- nil, creates a new table.
function tiny.system(table)
table = table or {}
table[systemTableKey] = true
return table
end
--- Creates a new Processing System or Processing System class. Processing
-- Systems process each entity individual, and are usually what is needed.
-- Processing Systems have three extra callbacks besides those inheritted from
-- vanilla Systems.
-- --
-- A Processing System iterates through its Entities in no particluar order, and -- function system:preProcess(dt) -- Called before iteration.
-- updates them individually. It has two important fields: -- function system:process(entity, dt) -- Process each entity.
-- function system:postProcess(dt) -- Called after iteration.
-- --
-- * `function system:process(entity, dt)` -- Processing Systems have their own `update` method, so don't implement a
-- * `function system:filter(entity)` -- a custom `update` callback for Processing Systems.
--
-- There are also a few other optional callbacks, including the optional
-- callbacks in `tiny.system`:
--
-- * `function system:preProcess(entities, dt)`
-- * `function system:postProcess(entities, dt)`
--
-- @param table A table to be used as a System, or `nil` to create a new
-- Processing System.
-- @see system -- @see system
-- @return A new Processing System or Processing System class
function tiny.processingSystem(table) function tiny.processingSystem(table)
table = table or {} table = table or {}
table[systemTableKey] = true table[systemTableKey] = true
@@ -283,45 +307,31 @@ function tiny.processingSystem(table)
return table return table
end end
-- Sorts Systems by a function system.sort(entity1, entity2) on modify. --- Creates a new Sorted System or Sorted System class. Sorted Systems sort
local function sortedSystemOnModify(system, dt) -- their Entities according to a user-defined method, `system:compare(e1, e2)`,
local entities = system.entities -- which should return true if `e1` should come before `e2` and false otherwise.
local entityIndices = system.entityIndices -- Sorted Systems also override the default System's `onModify` callback, so be
local sortDelegate = system.sortDelegate -- careful if defining a custom callback. However, for processing the sorted
if not sortDelegate then -- entities, consider `tiny.sortedProcessingSystem(table)`.
local compare = system.compare -- @see system
sortDelegate = function(e1, e2) function tiny.sortedSystem(table)
compare(system, e1, e2) table = table or {}
end table[systemTableKey] = true
system.sortDelegate = sortDelegate table.onModify = sortedSystemOnModify
end return table
tsort(entities, sortDelegate)
for i = 1, #entities do
local entity = entities[i]
entityIndices[entity] = i
end
end end
--- Creates a Sorted Processing System. A Sorted System iterates through its --- Creates a new Sorted Processing System or Sorted Processing System class.
-- Entities in a specific order, and updates them individually. It has three -- Sorted Processing Systems have both the aspects of Processing Systems and
-- important methods: -- Sorted Systems.
--
-- * `function system:process(entity, dt)`
-- * `function system:compare(entity1, entity2)`
-- * `function system:filter(entity)`
--
-- Sorted Systems have the same optitonal callbacks as ProcessingSystems.
-- @param table A table to be used as a System, or `nil` to create a new
-- Sorted System.
-- @see system -- @see system
-- @see processingSystem -- @see processingSystem
-- @return A new Sorted System or Sorted System class -- @see sortedSystem
function tiny.sortedSystem(table) function tiny.sortedProcessingSystem(table)
table = table or {} table = table or {}
table[systemTableKey] = true table[systemTableKey] = true
table.update = processingSystemUpdate table.update = processingSystemUpdate
table.onModify = sortedSystemOnModify table.onModify = sortedSystemOnModify
table.sort = sortedSystemOnModify
return table return table
end end
@@ -338,12 +348,10 @@ end
local worldMetaTable local worldMetaTable
--- Creates a new World. --- Creates a new World.
-- Can optionally add default Systems and Entities. -- Can optionally add default Systems and Entities. Returns the new World along
-- @param ... Systems and Entities to add to the World -- with default Entities and Systems.
-- @return A new World
function tiny.world(...) function tiny.world(...)
local ret = setmetatable({
local ret = {
-- List of Entities to add -- List of Entities to add
entitiesToAdd = {}, entitiesToAdd = {},
@@ -351,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 = {},
@@ -360,57 +371,50 @@ function tiny.world(...)
-- Set of Entities -- Set of Entities
entities = {}, entities = {},
-- Number of Entities in World. -- Number of Entities in World
entityCount = 0, entityCount = 0,
-- List of System Data. A data element is a table with 4 -- List of Systems
-- keys: system, indices, entities, and active. systems = {}
systems = {}, }, worldMetaTable)
-- Table of Systems to System Indices
systemIndices = {}
}
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. -- match different Filters. Returns the Entity.
-- @param world
-- @param entity
function tiny.addEntity(world, entity) function tiny.addEntity(world, entity)
if world.entities[entity] then
local e2c = world.entitiesToChange
e2c[#e2c + 1] = entity
else
local e2a = world.entitiesToAdd local e2a = world.entitiesToAdd
e2a[#e2a + 1] = entity e2a[#e2a + 1] = entity
if world.entities[entity] then
tiny_removeEntity(world, entity)
end end
return entity
end end
tiny_addEntity = tiny.addEntity tiny_addEntity = tiny.addEntity
--- Adds a System to the world. --- Adds a System to the world. Returns the System.
-- @param world
-- @param 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
end end
tiny_addSystem = tiny.addSystem tiny_addSystem = tiny.addSystem
--- Shortcut for adding multiple Entities and Systems to the World. --- Shortcut for adding multiple Entities and Systems to the World. Returns all
-- @param world -- added Entities and Systems.
-- @param ... Systems and Entities
-- @see addEntity
-- @see addSystem
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)
@@ -419,36 +423,32 @@ function tiny.add(world, ...)
end end
end end
end end
return ...
end end
tiny_add = tiny.add tiny_add = tiny.add
--- Removes an Entity to the World. --- Removes an Entity to the World. Returns the Entity.
-- @param world
-- @param entity
function tiny.removeEntity(world, entity) function tiny.removeEntity(world, entity)
local e2r = world.entitiesToRemove local e2r = world.entitiesToRemove
e2r[#e2r + 1] = entity e2r[#e2r + 1] = entity
return entity
end end
tiny_removeEntity = tiny.removeEntity tiny_removeEntity = tiny.removeEntity
--- Removes a System from the world. --- Removes a System from the world. Returns the System.
-- @param world
-- @param 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
end end
tiny_removeSystem = tiny.removeSystem tiny_removeSystem = tiny.removeSystem
--- Shortcut for removing multiple Entities and Systems from the World. --- Shortcut for removing multiple Entities and Systems from the World. Returns
-- @param world -- all removed Systems and Entities
-- @param ... Systems and Entities
-- @see removeEntity
-- @see removeSystem
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)
@@ -457,12 +457,12 @@ function tiny.remove(world, ...)
end end
end end
end end
return ...
end end
tiny_remove = tiny.remove 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
@@ -470,54 +470,68 @@ function tiny_manageSystems(world)
return return
end end
local systemIndices = world.systemIndices world.systemsToAdd = {}
world.systemsToRemove = {}
local entities = world.entities local entities = world.entities
local systems = world.systems local systems = world.systems
local system, index, filter
local entityList, entityIndices, entityIndex, onRemove, onAdd
-- Remove Systems -- Remove Systems
for i = 1, #s2r do for i = 1, #s2r do
system = s2r[i] local system = s2r[i]
index = systemIndices[system] local index = system.index
if index then local onRemove = system.onRemove
onRemove = system.onRemove
if onRemove then if onRemove then
entityList = system.entities local entityList = system.entities
for j = 1, #entityList do for j = 1, #entityList do
onRemove(system, entityList[j]) onRemove(system, entityList[j])
end end
end end
systemIndices[system] = nil
tremove(systems, index) tremove(systems, index)
for j = index, #systems do for j = index, #systems do
systemIndices[systems[j]] = j systems[j].index = j
end end
local onRemoveFromWorld = system.onRemoveFromWorld
if onRemoveFromWorld then
onRemoveFromWorld(system, world)
end end
s2r[i] = nil s2r[i] = nil
-- Clean up System
system.world = nil
system.entities = nil
system.indices = nil
system.index = nil
end end
-- Add Systems -- Add Systems
for i = 1, #s2a do for i = 1, #s2a do
system = s2a[i] local system = s2a[i]
if not systemIndices[system] then if systems[system.index] ~= system then
entityList = {} local entityList = {}
entityIndices = {} local entityIndices = {}
system.entities = entityList system.entities = entityList
system.indices = entityIndices system.indices = entityIndices
if system.active == nil then
system.active = true system.active = true
end
system.modified = true system.modified = true
index = #systems + 1 system.world = world
systemIndices[system] = index local index = #systems + 1
system.index = index
systems[index] = system systems[index] = system
local onAddToWorld = system.onAddToWorld
if onAddToWorld then
onAddToWorld(system, world)
end
-- Try to add Entities -- Try to add Entities
onAdd = system.onAdd local onAdd = system.onAdd
filter = system.filter local filter = system.filter
if filter then if filter then
for entity in pairs(entities) do for entity in pairs(entities) do
if filter(system, entity) then if filter(system, entity) then
entityIndex = #entityList + 1 local entityIndex = #entityList + 1
entityList[entityIndex] = entity entityList[entityIndex] = entity
entityIndices[entity] = entityIndex entityIndices[entity] = entityIndex
if onAdd then if onAdd then
@@ -529,120 +543,171 @@ function tiny_manageSystems(world)
end end
s2a[i] = nil s2a[i] = nil
end 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
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
system.modified = true
tmpEntity = ses[#ses]
ses[index] = tmpEntity
seis[tmpEntity] = index
seis[entity] = nil
ses[#ses] = nil
entityCount = entityCount - 1
onRemove = system.onRemove
if onRemove then
onRemove(system, entity)
end
end
end
end
e2r[i] = nil
end
-- Add Entities
for i = 1, #e2a do
entity = e2a[i]
if not entities[entity] then
entities[entity] = true
for j = 1, #systems do
system = systems[j]
ses = system.entities
seis = system.indices
filter = system.filter
if filter and filter(system, entity) then if filter and filter(system, entity) then
if not index then
system.modified = true system.modified = true
index = #ses + 1 index = #ses + 1
ses[index] = entity ses[index] = entity
seis[entity] = index seis[entity] = index
entityCount = entityCount + 1 local onAdd = system.onAdd
onAdd = system.onAdd
if onAdd then if onAdd then
onAdd(system, entity) onAdd(system, entity)
end end
end end
elseif 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
e2c[i] = nil
end 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
e2r[i] = nil
end
-- Add Entities
for i = 1, #e2a do
local entity = e2a[i]
if not entities[entity] then
entities[entity] = true
entityCount = entityCount + 1
for j = 1, #systems do
local system = systems[j]
local ses = system.entities
local seis = system.indices
local filter = system.filter
if filter and filter(system, entity) then
system.modified = true
local index = #ses + 1
ses[index] = entity
seis[entity] = index
local onAdd = system.onAdd
if onAdd then
onAdd(system, entity)
end
end
end
end end
e2a[i] = nil e2a[i] = nil
end end
-- Update Entity count -- Update Entity count
world.entityCount = entityCount world.entityCount = entityCount
end end
--- Updates the World. --- Manages Entities and Systems marked for deletion or addition. Call this
-- Put this in your main loop. -- before modifying Systems and Entities outside of a call to `tiny.update`.
-- @param world -- Do not call this within a call to `tiny.update`.
-- @param dt Delta time function tiny.refresh(world)
function tiny.update(world, dt) tiny_manageSystems(world)
tiny_manageEntities(world)
end
--- Updates the World by dt (delta time). Takes an optional parameter, `filter`,
-- which is a Filter that selects Systems from the World, and updates only those
-- Systems. If `filter` is not supplied, all Systems are updated. Put this
-- function in your main loop.
function tiny.update(world, dt, filter)
tiny_manageSystems(world) tiny_manageSystems(world)
tiny_manageEntities(world) tiny_manageEntities(world)
local systems = world.systems local systems = world.systems
local system, update, onModify, entities
-- 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 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
local interval = system.interval
if interval then
local bufferedTime = (system.bufferedTime or 0) + dt
while bufferedTime >= interval do
bufferedTime = bufferedTime - interval
if update then
update(system, interval)
end
end
system.bufferedTime = bufferedTime
else
update(system, dt) update(system, dt)
end end
end
system.modified = false system.modified = false
end end
@@ -650,7 +715,6 @@ function tiny.update(world, dt)
end end
--- Removes all Entities from the World. --- Removes all Entities from the World.
-- @param world
function tiny.clearEntities(world) function tiny.clearEntities(world)
for e in pairs(world.entities) do for e in pairs(world.entities) do
tiny_removeEntity(world, e) tiny_removeEntity(world, e)
@@ -658,7 +722,6 @@ function tiny.clearEntities(world)
end end
--- Removes all Systems from the World. --- Removes all Systems from the World.
-- @param world
function tiny.clearSystems(world) function tiny.clearSystems(world)
local systems = world.systems local systems = world.systems
for i = #systems, 1, -1 do for i = #systems, 1, -1 do
@@ -667,46 +730,37 @@ function tiny.clearSystems(world)
end end
--- Gets number of Entities in the World. --- Gets number of Entities in the World.
-- @param world
-- @return An integer
function tiny.getEntityCount(world) function tiny.getEntityCount(world)
return world.entityCount return world.entityCount
end end
--- Gets number of Systems in World. --- Gets number of Systems in World.
-- @param world
-- @return An integer
function tiny.getSystemCount(world) function tiny.getSystemCount(world)
return #(world.systems) return #(world.systems)
end end
--- Gets the index of a System in the World. Lower indexed Systems are processed --- Gets the index of the System in the World.
-- before higher indexed systems. -- A simpler alternative is `system.index`.
-- @param world
-- @param system
-- @return An integer between 1 and world:getSystemCount() inclusive
function tiny.getSystemIndex(world, system) function tiny.getSystemIndex(world, system)
return world.systemIndices[system] return system.index
end end
--- Sets the index of a System in the World. Changes the order in --- Sets the index of a System in the World, and returns the old index. Changes
-- which they Systems processed, because lower indexed Systems are processed -- the order in which they Systems processed, because lower indexed Systems are
-- first. -- processed first. Returns the old system.index.
-- @param world
-- @param system
-- @param index
-- @return Old index
function tiny.setSystemIndex(world, system, index) function tiny.setSystemIndex(world, system, index)
local systemIndices = world.systemIndices local oldIndex = system.index
local oldIndex = systemIndices[system]
local systems = world.systems local systems = world.systems
local system = systems[oldIndex]
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)
for i = oldIndex, index, index >= oldIndex and 1 or -1 do for i = oldIndex, index, index >= oldIndex and 1 or -1 do
systemIndices[systems[i]] = i systems[i].index = i
end end
return oldIndex return oldIndex
@@ -721,6 +775,7 @@ worldMetaTable = {
remove = tiny.remove, remove = tiny.remove,
removeEntity = tiny.removeEntity, removeEntity = tiny.removeEntity,
removeSystem = tiny.removeSystem, removeSystem = tiny.removeSystem,
refresh = tiny.refresh,
update = tiny.update, update = tiny.update,
clearEntities = tiny.clearEntities, clearEntities = tiny.clearEntities,
clearSystems = tiny.clearSystems, clearSystems = tiny.clearSystems,
@@ -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