mirror of
https://github.com/bakpakin/tiny-ecs.git
synced 2026-07-22 07:56:52 -06:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
283df5af93 | ||
|
|
35d3e7a9c1 | ||
|
|
58d5df8e6e | ||
|
|
ef83631abd | ||
|
|
5a2e9c2927 | ||
|
|
6020f91572 | ||
|
|
ad765772e8 | ||
|
|
c9c51e27b6 | ||
|
|
5b8fd2b1d0 | ||
|
|
6c2b0a5629 | ||
|
|
a38165da26 | ||
|
|
4ae55c2274 | ||
|
|
e4ca5f0158 | ||
|
|
5e5ae82a8c | ||
|
|
d995a345d9 | ||
|
|
7e8c8735b8 | ||
|
|
662ada2cb7 | ||
|
|
265fc0c6f8 |
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
#Ignore generated documentation
|
||||
doc/*
|
||||
index.html
|
||||
ldoc.css
|
||||
topics/*
|
||||
|
||||
#Ignore generated rocks
|
||||
*.rock
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
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
|
||||
@@ -1,7 +0,0 @@
|
||||
Copyright (c) 2016 Calvin Rose
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,90 +0,0 @@
|
||||
# tiny-ecs #
|
||||
|
||||
[](https://travis-ci.org/bakpakin/tiny-ecs)[](LICENSE)
|
||||
|
||||
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
|
||||
for simulating large and complex systems. For more explanation on Entity
|
||||
Component Systems, here is some
|
||||
[basic info](http://en.wikipedia.org/wiki/Entity_component_system "Wikipedia").
|
||||
|
||||
Tiny-ecs also works well with objected oriented programming in Lua because
|
||||
Systems and Entities do not use metatables. This means you can subclass your
|
||||
Systems and Entities, and use existing Lua class frameworks with tiny-ecs, no problem.
|
||||
For an example on how to use tiny-ecs with object-oriented Lua, take a look at the
|
||||
demo branch, specifically the systems and entities sub-directories.
|
||||
|
||||
## Overview ##
|
||||
Tiny-ecs has four important types: Worlds, Filters, Systems, and Entities.
|
||||
Entities, however, can be any Lua table, and Filters are just functions that
|
||||
take an Entity as a parameter.
|
||||
|
||||
### Entities ###
|
||||
Entities are simply Lua tables of data that gets processed by Systems. Entities
|
||||
should contain primarily data rather than code, as it is the Systems's job to
|
||||
do logic on data. Henceforth, a key-value pair in an Entity will
|
||||
be referred to as a Component.
|
||||
|
||||
### Worlds ###
|
||||
Worlds are the outermost containers in tiny-ecs that contain both Systems
|
||||
and Entities. In typical use, only one World is used at a time.
|
||||
|
||||
### Systems ###
|
||||
Systems in tiny-ecs describe how to update Entities. Systems select certain Entities
|
||||
using a Filter, and then only update those select Entities. Some Systems don't
|
||||
update Entities, and instead just act as function callbacks every update. Tiny-ecs
|
||||
provides functions for creating Systems easily, as well as creating Systems that
|
||||
can be used in an object oriented fashion.
|
||||
|
||||
### Filters ###
|
||||
Filters are used to select Entities. Filters can be any Lua function, but
|
||||
tiny-ecs provides some functions for generating common ones, like selecting
|
||||
only Entities that have all required components.
|
||||
|
||||
## Example ##
|
||||
```lua
|
||||
local tiny = require("tiny")
|
||||
|
||||
local talkingSystem = tiny.processingSystem()
|
||||
talkingSystem.filter = tiny.requireAll("name", "mass", "phrase")
|
||||
function talkingSystem:process(e, dt)
|
||||
e.mass = e.mass + dt * 3
|
||||
print(("%s who weighs %d pounds, says %q."):format(e.name, e.mass, e.phrase)
|
||||
end
|
||||
|
||||
local joe = {
|
||||
name = "Joe",
|
||||
phrase = "I'm a plumber.",
|
||||
mass = 150,
|
||||
hairColor = "brown"
|
||||
}
|
||||
|
||||
local world = tiny.world(talkingSystem, joe)
|
||||
|
||||
for i = 1, 20 do
|
||||
world:update(1)
|
||||
end
|
||||
```
|
||||
|
||||
## Use It ##
|
||||
Copy paste tiny.lua into your source folder. For stability and consistent API,
|
||||
please use a tagged release or use luarocks.
|
||||
|
||||
## Luarocks ##
|
||||
Tiny-ecs is also on [Luarocks](https://luarocks.org/) and can be installed with
|
||||
`luarocks install tiny-ecs`.
|
||||
|
||||
## Demo ##
|
||||
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
|
||||
[LÖVE](https://love2d.org/), an amazing game framework for Lua.
|
||||
|
||||
## Testing ##
|
||||
Tiny-ecs uses [busted](http://olivinelabs.com/busted/) for testing. Install and run
|
||||
`busted` from the command line to test.
|
||||
|
||||
## Documentation ##
|
||||
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
|
||||
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,13 +0,0 @@
|
||||
file = "tiny.lua"
|
||||
project = "tiny-ecs"
|
||||
description = "Entity Component System for lua."
|
||||
backtick_references = true
|
||||
format = 'discount'
|
||||
title = "tiny-ecs API"
|
||||
one = true
|
||||
dir = "doc"
|
||||
style = '!fixed'
|
||||
package = 'tiny-ecs'
|
||||
not_luadoc = true
|
||||
boilerplate = true
|
||||
no_return_or_parms = true
|
||||
+729
@@ -0,0 +1,729 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>tiny-ecs API</title>
|
||||
<link rel="stylesheet" href="ldoc_fixed.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>tiny-ecs</h1>
|
||||
|
||||
|
||||
<h2>Contents</h2>
|
||||
<ul>
|
||||
<li><a href="#Filter_functions">Filter functions </a></li>
|
||||
<li><a href="#System_functions">System functions </a></li>
|
||||
<li><a href="#World_functions">World functions </a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>Modules</h2>
|
||||
<ul class="$(kind=='Topics' and '' or 'nowrap'">
|
||||
<li><strong>tiny-ecs</strong></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>Module <code>tiny-ecs</code></h1>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<h3>Info:</h3>
|
||||
<ul>
|
||||
<li><strong>Copyright</strong>: 2016</li>
|
||||
<li><strong>License</strong>: MIT</li>
|
||||
<li><strong>Author</strong>: Calvin Rose</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2><a href="#Filter_functions">Filter functions </a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.requireAll">tiny.requireAll (...)</a></td>
|
||||
<td class="summary">Makes a Filter that selects Entities with all specified Components and
|
||||
Filters.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.requireAny">tiny.requireAny (...)</a></td>
|
||||
<td class="summary">Makes a Filter that selects Entities with at least one of the specified
|
||||
Components and Filters.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.rejectAll">tiny.rejectAll (...)</a></td>
|
||||
<td class="summary">Makes a Filter that rejects Entities with all specified Components and
|
||||
Filters, and selects all other Entities.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.rejectAny">tiny.rejectAny (...)</a></td>
|
||||
<td class="summary">Makes a Filter that rejects Entities with at least one of the specified
|
||||
Components and Filters, and selects all other Entities.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.filter">tiny.filter (pattern)</a></td>
|
||||
<td class="summary">Makes a Filter from a string.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#System_functions">System functions </a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.system">tiny.system (table)</a></td>
|
||||
<td class="summary">Creates a new System or System class from the supplied table.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.processingSystem">tiny.processingSystem (table)</a></td>
|
||||
<td class="summary">Creates a new Processing System or Processing System class.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.sortedSystem">tiny.sortedSystem (table)</a></td>
|
||||
<td class="summary">Creates a new Sorted System or Sorted System class.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.sortedProcessingSystem">tiny.sortedProcessingSystem (table)</a></td>
|
||||
<td class="summary">Creates a new Sorted Processing System or Sorted Processing System class.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#World_functions">World functions </a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.world">tiny.world (...)</a></td>
|
||||
<td class="summary">Creates a new World.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.addEntity">tiny.addEntity (world, entity)</a></td>
|
||||
<td class="summary">Adds an Entity to the world.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.addSystem">tiny.addSystem (world, system)</a></td>
|
||||
<td class="summary">Adds a System to the world.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.add">tiny.add (world, ...)</a></td>
|
||||
<td class="summary">Shortcut for adding multiple Entities and Systems to the World.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.removeEntity">tiny.removeEntity (world, entity)</a></td>
|
||||
<td class="summary">Removes an Entity from the World.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.removeSystem">tiny.removeSystem (world, system)</a></td>
|
||||
<td class="summary">Removes a System from the world.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.remove">tiny.remove (world, ...)</a></td>
|
||||
<td class="summary">Shortcut for removing multiple Entities and Systems from the World.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.refresh">tiny.refresh (world)</a></td>
|
||||
<td class="summary">Manages Entities and Systems marked for deletion or addition.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.update">tiny.update (world, dt, filter)</a></td>
|
||||
<td class="summary">Updates the World by dt (delta time).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.clearEntities">tiny.clearEntities (world)</a></td>
|
||||
<td class="summary">Removes all Entities from the World.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.clearSystems">tiny.clearSystems (world)</a></td>
|
||||
<td class="summary">Removes all Systems from the World.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.getEntityCount">tiny.getEntityCount (world)</a></td>
|
||||
<td class="summary">Gets number of Entities in the World.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.getSystemCount">tiny.getSystemCount (world)</a></td>
|
||||
<td class="summary">Gets number of Systems in World.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" nowrap><a href="#tiny.setSystemIndex">tiny.setSystemIndex (world, system, index)</a></td>
|
||||
<td class="summary">Sets the index of a System in the World, and returns the old index.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<h2 class="section-header has-description"><a name="Filter_functions"></a>Filter functions </h2>
|
||||
|
||||
<div class="section-description">
|
||||
|
||||
|
||||
<p> A Filter is a function that selects which Entities apply to a System.
|
||||
Filters take two parameters, the System and the Entity, and return a boolean
|
||||
value indicating if the Entity should be processed by the System. A truthy
|
||||
value includes the entity, while a falsey (nil or false) value excludes the
|
||||
entity.</p>
|
||||
|
||||
<p> Filters must be added to Systems by setting the <a href="index.html#tiny.filter">filter</a> field of the System.
|
||||
Filter's returned by tiny-ecs's Filter functions are immutable and can be
|
||||
used by multiple Systems.</p>
|
||||
|
||||
<pre><code>local f1 = tiny.requireAll("position", "velocity", "size")
|
||||
local f2 = tiny.requireAny("position", "velocity", "size")
|
||||
|
||||
local e1 = {
|
||||
position = {2, 3},
|
||||
velocity = {3, 3},
|
||||
size = {4, 4}
|
||||
}
|
||||
|
||||
local entity2 = {
|
||||
position = {4, 5},
|
||||
size = {4, 4}
|
||||
}
|
||||
|
||||
local e3 = {
|
||||
position = {2, 3},
|
||||
velocity = {3, 3}
|
||||
}
|
||||
|
||||
print(f1(nil, e1), f1(nil, e2), f1(nil, e3)) -- prints true, false, false
|
||||
print(f2(nil, e1), f2(nil, e2), f2(nil, e3)) -- prints true, true, true
|
||||
</code></pre>
|
||||
|
||||
<p> Filters can also be passed as arguments to other Filter constructors. This is
|
||||
a powerful way to create complex, custom Filters that select a very specific
|
||||
set of Entities.</p>
|
||||
|
||||
<pre><code>-- Selects Entities with an "image" Component, but not Entities with a
|
||||
-- "Player" or "Enemy" Component.
|
||||
filter = tiny.requireAll("image", tiny.rejectAny("Player", "Enemy"))
|
||||
</code></pre>
|
||||
|
||||
|
||||
</div>
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "tiny.requireAll"></a>
|
||||
<strong>tiny.requireAll (...)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Makes a Filter that selects Entities with all specified Components and
|
||||
Filters.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.requireAny"></a>
|
||||
<strong>tiny.requireAny (...)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Makes a Filter that selects Entities with at least one of the specified
|
||||
Components and Filters.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.rejectAll"></a>
|
||||
<strong>tiny.rejectAll (...)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Makes a Filter that rejects Entities with all specified Components and
|
||||
Filters, and selects all other Entities.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.rejectAny"></a>
|
||||
<strong>tiny.rejectAny (...)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Makes a Filter that rejects Entities with at least one of the specified
|
||||
Components and Filters, and selects all other Entities.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.filter"></a>
|
||||
<strong>tiny.filter (pattern)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<p>Makes a Filter from a string. Syntax of <code>pattern</code> is as follows.</p>
|
||||
|
||||
<ul>
|
||||
<li>Tokens are alphanumeric strings including underscores.</li>
|
||||
<li>Tokens can be separated by |, &, or surrounded by parentheses.</li>
|
||||
<li>Tokens can be prefixed with !, and are then inverted.</li>
|
||||
</ul>
|
||||
|
||||
<p> Examples are best:</p>
|
||||
<pre><code>'a|b|c' - Matches entities with an 'a' OR 'b' OR 'c'.
|
||||
'a&!b&c' - Matches entities with an 'a' AND NOT 'b' AND 'c'.
|
||||
'a|(b&c&d)|e - Matches 'a' OR ('b' AND 'c' AND 'd') OR 'e'
|
||||
</code></pre>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<h2 class="section-header has-description"><a name="System_functions"></a>System functions </h2>
|
||||
|
||||
<div class="section-description">
|
||||
|
||||
|
||||
<p> A System is a wrapper around function callbacks for manipulating Entities.
|
||||
Systems are implemented as tables that contain at least one method;
|
||||
an update function that takes parameters like so:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>function system:update(dt)</code>.</li>
|
||||
</ul>
|
||||
|
||||
<p> There are also a few other optional callbacks:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>function system:filter(entity)</code> - Returns true if this System should
|
||||
include this Entity, otherwise should return false. If this isn't specified,
|
||||
no Entities are included in the System.</li>
|
||||
<li><code>function system:onAdd(entity)</code> - Called when an Entity is added to the
|
||||
System.</li>
|
||||
<li><code>function system:onRemove(entity)</code> - Called when an Entity is removed
|
||||
from the System.</li>
|
||||
<li><code>function system:onModify(dt)</code> - Called when the System is modified by
|
||||
adding or removing Entities from the System.</li>
|
||||
<li><code>function system:onAddToWorld(world)</code> - Called when the System is added
|
||||
to the World, before any entities are added to the system.</li>
|
||||
<li><code>function system:onRemoveFromWorld(world)</code> - Called when the System is
|
||||
removed from the world, after all Entities are removed from the System.</li>
|
||||
<li><code>function system:preWrap(dt)</code> - Called on each system before update is
|
||||
called on any system.</li>
|
||||
<li><code>function system:postWrap(dt)</code> - Called on each system in reverse order
|
||||
after update is called on each system. The idea behind <code>preWrap</code> and
|
||||
<code>postWrap</code> is to allow for systems that modify the behavior of other systems.
|
||||
Say there is a DrawingSystem, which draws sprites to the screen, and a
|
||||
PostProcessingSystem, that adds some blur and bloom effects. In the preWrap
|
||||
method of the PostProcessingSystem, the System could set the drawing target
|
||||
for the DrawingSystem to a special buffer instead the screen. In the postWrap
|
||||
method, the PostProcessingSystem could then modify the buffer and render it
|
||||
to the screen. In this setup, the PostProcessingSystem would be added to the
|
||||
World after the drawingSystem (A similar but less flexible behavior could
|
||||
be accomplished with a single custom update function in the DrawingSystem).</li>
|
||||
</ul>
|
||||
|
||||
<p> For Filters, it is convenient to use <a href="index.html#tiny.requireAll">tiny.requireAll</a> or <a href="index.html#tiny.requireAny">tiny.requireAny</a>,
|
||||
but one can write their own filters as well. Set the Filter of a System like
|
||||
so:</p>
|
||||
<pre><code>system.filter = tiny.requireAll("a", "b", "c")
|
||||
</code></pre>
|
||||
<p> or</p>
|
||||
<pre><code>function system:filter(entity)
|
||||
return entity.myRequiredComponentName ~= nil
|
||||
end
|
||||
</code></pre>
|
||||
|
||||
<p> All Systems also have a few important fields that are initialized when the
|
||||
system is added to the World. A few are important, and few should be less
|
||||
commonly used.</p>
|
||||
|
||||
<ul>
|
||||
<li>The <a href="index.html#tiny.world">world</a> field points to the World that the System belongs to. Useful
|
||||
for adding and removing Entities from the world dynamically via the System.</li>
|
||||
<li>The <code>active</code> flag is whether or not the System is updated automatically.
|
||||
Inactive Systems should be updated manually or not at all via
|
||||
<code>system:update(dt)</code>. Defaults to true.</li>
|
||||
<li>The <code>entities</code> field is an ordered list of Entities in the System. This
|
||||
list can be used to quickly iterate through all Entities in a System.</li>
|
||||
<li>The <code>interval</code> 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.</li>
|
||||
<li>The <code>index</code> field is the System's index in the World. Lower indexed
|
||||
Systems are processed before higher indices. The <code>index</code> is a read only
|
||||
field; to set the <code>index</code>, use <code>tiny.setSystemIndex(world, system)</code>.</li>
|
||||
<li>The <code>indices</code> field is a table of Entity keys to their indices in the
|
||||
<code>entities</code> list. Most Systems can ignore this.</li>
|
||||
<li>The <code>modified</code> flag is an indicator if the System has been modified in
|
||||
the last update. If so, the <code>onModify</code> callback will be called on the System
|
||||
in the next update, if it has one. This is usually managed by tiny-ecs, so
|
||||
users should mostly ignore this, too.</li>
|
||||
</ul>
|
||||
|
||||
<p> There is another option to (hopefully) increase performance in systems that
|
||||
have items added to or removed from them often, and have lots of entities in
|
||||
them. Setting the <code>nocache</code> field of the system might improve performance.
|
||||
It is still experimental. There are some restriction to systems without
|
||||
caching, however.</p>
|
||||
|
||||
<ul>
|
||||
<li>There is no <code>entities</code> table.</li>
|
||||
<li>Callbacks such onAdd, onRemove, and onModify will never be called</li>
|
||||
<li>Noncached systems cannot be sorted (There is no entities list to sort).</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "tiny.system"></a>
|
||||
<strong>tiny.system (table)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Creates a new System or System class from the supplied table. If <a href="http://www.lua.org/manual/5.2/manual.html#6.5">table</a> is
|
||||
nil, creates a new table.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.processingSystem"></a>
|
||||
<strong>tiny.processingSystem (table)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
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.</p>
|
||||
|
||||
<pre><code> function system:preProcess(dt) -- Called before iteration.
|
||||
function system:process(entity, dt) -- Process each entity.
|
||||
function system:postProcess(dt) -- Called after iteration.
|
||||
</code></pre>
|
||||
|
||||
<p> Processing Systems have their own <a href="index.html#tiny.update">update</a> method, so don't implement a
|
||||
a custom <a href="index.html#tiny.update">update</a> callback for Processing Systems.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>See also:</h3>
|
||||
<ul>
|
||||
<a href="index.html#tiny.system">system</a>
|
||||
</ul>
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.sortedSystem"></a>
|
||||
<strong>tiny.sortedSystem (table)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Creates a new Sorted System or Sorted System class. Sorted Systems sort
|
||||
their Entities according to a user-defined method, <code>system:compare(e1, e2)</code>,
|
||||
which should return true if <code>e1</code> should come before <code>e2</code> and false otherwise.
|
||||
Sorted Systems also override the default System's <code>onModify</code> callback, so be
|
||||
careful if defining a custom callback. However, for processing the sorted
|
||||
entities, consider <code>tiny.sortedProcessingSystem(table)</code>.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>See also:</h3>
|
||||
<ul>
|
||||
<a href="index.html#tiny.system">system</a>
|
||||
</ul>
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.sortedProcessingSystem"></a>
|
||||
<strong>tiny.sortedProcessingSystem (table)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Creates a new Sorted Processing System or Sorted Processing System class.
|
||||
Sorted Processing Systems have both the aspects of Processing Systems and
|
||||
Sorted Systems.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>See also:</h3>
|
||||
<ul>
|
||||
<li><a href="index.html#tiny.system">system</a></li>
|
||||
<li><a href="index.html#tiny.processingSystem">processingSystem</a></li>
|
||||
<li><a href="index.html#tiny.sortedSystem">sortedSystem</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<h2 class="section-header has-description"><a name="World_functions"></a>World functions </h2>
|
||||
|
||||
<div class="section-description">
|
||||
A World is a container that manages Entities and Systems. Typically, a
|
||||
program uses one World at a time.</p>
|
||||
|
||||
<p> For all World functions except <code>tiny.world(...)</code>, object-oriented syntax can
|
||||
be used instead of the documented syntax. For example,
|
||||
<code>tiny.add(world, e1, e2, e3)</code> is the same as <code>world:add(e1, e2, e3)</code>.
|
||||
</div>
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "tiny.world"></a>
|
||||
<strong>tiny.world (...)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Creates a new World.
|
||||
Can optionally add default Systems and Entities. Returns the new World along
|
||||
with default Entities and Systems.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.addEntity"></a>
|
||||
<strong>tiny.addEntity (world, entity)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Adds an Entity to the world.
|
||||
Also call this on Entities that have changed Components such that they
|
||||
match different Filters. Returns the Entity.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.addSystem"></a>
|
||||
<strong>tiny.addSystem (world, system)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Adds a System to the world. Returns the System.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.add"></a>
|
||||
<strong>tiny.add (world, ...)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Shortcut for adding multiple Entities and Systems to the World. Returns all
|
||||
added Entities and Systems.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.removeEntity"></a>
|
||||
<strong>tiny.removeEntity (world, entity)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Removes an Entity from the World. Returns the Entity.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.removeSystem"></a>
|
||||
<strong>tiny.removeSystem (world, system)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Removes a System from the world. Returns the System.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.remove"></a>
|
||||
<strong>tiny.remove (world, ...)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Shortcut for removing multiple Entities and Systems from the World. Returns
|
||||
all removed Systems and Entities
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.refresh"></a>
|
||||
<strong>tiny.refresh (world)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Manages Entities and Systems marked for deletion or addition. Call this
|
||||
before modifying Systems and Entities outside of a call to <a href="index.html#tiny.update">tiny.update</a>.
|
||||
Do not call this within a call to <a href="index.html#tiny.update">tiny.update</a>.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.update"></a>
|
||||
<strong>tiny.update (world, dt, filter)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Updates the World by dt (delta time). Takes an optional parameter, <a href="index.html#tiny.filter">filter</a>,
|
||||
which is a Filter that selects Systems from the World, and updates only those
|
||||
Systems. If <a href="index.html#tiny.filter">filter</a> is not supplied, all Systems are updated. Put this
|
||||
function in your main loop.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.clearEntities"></a>
|
||||
<strong>tiny.clearEntities (world)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Removes all Entities from the World.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.clearSystems"></a>
|
||||
<strong>tiny.clearSystems (world)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Removes all Systems from the World.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.getEntityCount"></a>
|
||||
<strong>tiny.getEntityCount (world)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Gets number of Entities in the World.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.getSystemCount"></a>
|
||||
<strong>tiny.getSystemCount (world)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Gets number of Systems in World.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "tiny.setSystemIndex"></a>
|
||||
<strong>tiny.setSystemIndex (world, system, index)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Sets the index of a System in the World, and returns the old index. Changes
|
||||
the order in which they Systems processed, because lower indexed Systems are
|
||||
processed first. Returns the old system.index.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.3</a></i>
|
||||
<i style="float:right;">Last updated 2016-08-10 21:48:45 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,312 @@
|
||||
/* BEGIN RESET
|
||||
|
||||
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
|
||||
Code licensed under the BSD License:
|
||||
http://developer.yahoo.com/yui/license.html
|
||||
version: 2.8.2r1
|
||||
*/
|
||||
html {
|
||||
color: #000;
|
||||
background: #FFF;
|
||||
}
|
||||
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
fieldset,img {
|
||||
border: 0;
|
||||
}
|
||||
address,caption,cite,code,dfn,em,strong,th,var,optgroup {
|
||||
font-style: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
del,ins {
|
||||
text-decoration: none;
|
||||
}
|
||||
li {
|
||||
list-style: disc;
|
||||
margin-left: 20px;
|
||||
}
|
||||
caption,th {
|
||||
text-align: left;
|
||||
}
|
||||
h1,h2,h3,h4,h5,h6 {
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
}
|
||||
q:before,q:after {
|
||||
content: '';
|
||||
}
|
||||
abbr,acronym {
|
||||
border: 0;
|
||||
font-variant: normal;
|
||||
}
|
||||
sup {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
sub {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
legend {
|
||||
color: #000;
|
||||
}
|
||||
input,button,textarea,select,optgroup,option {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-style: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
input,button,textarea,select {*font-size:100%;
|
||||
}
|
||||
/* END RESET */
|
||||
|
||||
body {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
font-family: arial, helvetica, geneva, sans-serif;
|
||||
background-color: #ffffff; margin: 0px;
|
||||
}
|
||||
|
||||
code, tt { font-family: monospace; font-size: 1.1em; }
|
||||
span.parameter { font-family:monospace; }
|
||||
span.parameter:after { content:":"; }
|
||||
span.types:before { content:"("; }
|
||||
span.types:after { content:")"; }
|
||||
.type { font-weight: bold; font-style:italic }
|
||||
|
||||
body, p, td, th { font-size: .95em; line-height: 1.2em;}
|
||||
|
||||
p, ul { margin: 10px 0 0 0px;}
|
||||
|
||||
strong { font-weight: bold;}
|
||||
|
||||
em { font-style: italic;}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
h2, h3, h4 { margin: 15px 0 10px 0; }
|
||||
h2 { font-size: 1.25em; }
|
||||
h3 { font-size: 1.15em; }
|
||||
h4 { font-size: 1.06em; }
|
||||
|
||||
a:link { font-weight: bold; color: #004080; text-decoration: none; }
|
||||
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
|
||||
a:link:hover { text-decoration: underline; }
|
||||
|
||||
hr {
|
||||
color:#cccccc;
|
||||
background: #00007f;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
blockquote { margin-left: 3em; }
|
||||
|
||||
ul { list-style-type: disc; }
|
||||
|
||||
p.name {
|
||||
font-family: "Andale Mono", monospace;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: rgb(245, 245, 245);
|
||||
border: 1px solid #C0C0C0; /* silver */
|
||||
padding: 10px;
|
||||
margin: 10px 0 10px 0;
|
||||
overflow: auto;
|
||||
font-family: "Andale Mono", monospace;
|
||||
}
|
||||
|
||||
pre.example {
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
table.index { border: 1px #00007f; }
|
||||
table.index td { text-align: left; vertical-align: top; }
|
||||
|
||||
#container {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#product {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#product big {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#main {
|
||||
background-color:#FFFFFF; // #f0f0f0;
|
||||
border-left: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
float: left;
|
||||
width: 14em;
|
||||
vertical-align: top;
|
||||
background-color:#FFFFFF; // #f0f0f0;
|
||||
border-right: 2px solid #cccccc;
|
||||
overflow: visible;
|
||||
overflow-y: scroll;
|
||||
height: 100%;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
#navigation h2 {
|
||||
background-color:#FFFFFF;//:#e7e7e7;
|
||||
font-size:1.1em;
|
||||
color:#000000;
|
||||
text-align: left;
|
||||
padding:0.2em;
|
||||
border-bottom:1px solid #dddddd;
|
||||
}
|
||||
|
||||
#navigation ul
|
||||
{
|
||||
font-size:1em;
|
||||
list-style-type: none;
|
||||
margin: 1px 1px 10px 1px;
|
||||
}
|
||||
|
||||
#navigation li {
|
||||
text-indent: -1em;
|
||||
display: block;
|
||||
margin: 3px 0px 0px 22px;
|
||||
}
|
||||
|
||||
#navigation li li a {
|
||||
margin: 0px 3px 0px -1em;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 14em;
|
||||
padding: 1em;
|
||||
padding-left: 2em;
|
||||
width: 700px;
|
||||
border-left: 2px solid #cccccc;
|
||||
// border-right: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#about {
|
||||
clear: both;
|
||||
padding-left: 1em;
|
||||
margin-left: 14em; // avoid the damn sidebar!
|
||||
border-top: 2px solid #cccccc;
|
||||
border-left: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font: 12pt "Times New Roman", "TimeNR", Times, serif;
|
||||
}
|
||||
a { font-weight: bold; color: #004080; text-decoration: underline; }
|
||||
|
||||
#main {
|
||||
background-color: #ffffff;
|
||||
border-left: 0px;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-left: 2%;
|
||||
margin-right: 2%;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 1em;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
display: none;
|
||||
}
|
||||
pre.example {
|
||||
font-family: "Andale Mono", monospace;
|
||||
font-size: 10pt;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
table.module_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.module_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; }
|
||||
table.module_list td.summary { width: 100%; }
|
||||
|
||||
table.function_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.function_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.function_list td.name { background-color: #f6f6ff; ; min-width: 200px; }
|
||||
table.function_list td.summary { width: 100%; }
|
||||
|
||||
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
|
||||
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
|
||||
dl.table h3, dl.function h3 {font-size: .95em;}
|
||||
|
||||
ul.nowrap {
|
||||
overflow:auto;
|
||||
whitespace:nowrap;
|
||||
}
|
||||
|
||||
/* stop sublists from having initial vertical space */
|
||||
ul ul { margin-top: 0px; }
|
||||
ol ul { margin-top: 0px; }
|
||||
ol ol { margin-top: 0px; }
|
||||
ul ol { margin-top: 0px; }
|
||||
|
||||
/* make the target distinct; helps when we're navigating to a function */
|
||||
a:target + * {
|
||||
background-color: #FF9;
|
||||
}
|
||||
|
||||
|
||||
/* styles for prettification of source */
|
||||
pre .comment { color: #558817; }
|
||||
pre .constant { color: #a8660d; }
|
||||
pre .escape { color: #844631; }
|
||||
pre .keyword { color: #aa5050; font-weight: bold; }
|
||||
pre .library { color: #0e7c6b; }
|
||||
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
|
||||
pre .string { color: #8080ff; }
|
||||
pre .number { color: #f8660d; }
|
||||
pre .operator { color: #2239a8; font-weight: bold; }
|
||||
pre .preprocessor, pre .prepro { color: #a33243; }
|
||||
pre .global { color: #800080; }
|
||||
pre .user-keyword { color: #800080; }
|
||||
pre .prompt { color: #558817; }
|
||||
pre .url { color: #272fc2; text-decoration: underline; }
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
-- Helper Lua file for easy require if tiny-ecs is used as a git submodule or
|
||||
-- folder. Not needed in many cases, including luarocks distribution.
|
||||
|
||||
local directory = (...):match("(.-)[^%.]+$")
|
||||
return require(directory .. 'tiny')
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.0-2"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.1-1"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.1-2"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.1-3"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.1-4"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.1-5"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.1-6"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.1-7"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.2-1"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.3-1"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.3-2"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "1.3-3"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
tag = version
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
local GLOBALS = {}
|
||||
for k, v in pairs(_G) do
|
||||
GLOBALS[k] = v
|
||||
end
|
||||
|
||||
local tiny = require "tiny"
|
||||
|
||||
local function deep_copy(x)
|
||||
if type(x) == 'table' then
|
||||
local nx = {}
|
||||
for k, v in next, x, nil do
|
||||
nx[deep_copy(k)] = deep_copy(v)
|
||||
end
|
||||
return nx
|
||||
else
|
||||
return x
|
||||
end
|
||||
end
|
||||
|
||||
local entityTemplate1 = {
|
||||
xform = {x = 0, y = 0},
|
||||
vel = {x = 1, y = 2},
|
||||
name = "E1",
|
||||
size = 11,
|
||||
description = "It goes to 11.",
|
||||
spinalTap = true
|
||||
}
|
||||
|
||||
local entityTemplate2 = {
|
||||
xform = {x = 2, y = 2},
|
||||
vel = {x = -1, y = 0},
|
||||
name = "E2",
|
||||
size = 10,
|
||||
description = "It does not go to 11.",
|
||||
onlyTen = true
|
||||
}
|
||||
|
||||
local entityTemplate3 = {
|
||||
xform = {x = 4, y = 5},
|
||||
vel = {x = 0, y = 3},
|
||||
name = "E3",
|
||||
size = 8,
|
||||
description = "The smallest entity.",
|
||||
littleMan = true
|
||||
}
|
||||
|
||||
describe('tiny-ecs:', function()
|
||||
|
||||
describe('Filters:', function()
|
||||
|
||||
local entity1, entity2, entity3
|
||||
|
||||
before_each(function()
|
||||
entity1 = deep_copy(entityTemplate1)
|
||||
entity2 = deep_copy(entityTemplate2)
|
||||
entity3 = deep_copy(entityTemplate3)
|
||||
end)
|
||||
|
||||
it("Default Filters", function()
|
||||
|
||||
local ftap = tiny.requireAll("spinalTap")
|
||||
local fvel = tiny.requireAll("vel")
|
||||
local fxform = tiny.requireAll("xform")
|
||||
local fall = tiny.requireAny("spinalTap", "onlyTen", "littleMan")
|
||||
|
||||
-- Only select Entities without "spinalTap"
|
||||
local frtap = tiny.rejectAny("spinalTap")
|
||||
|
||||
-- Select Entities without all three: "spinalTap", "onlyTen", and
|
||||
-- "littleMan"
|
||||
local frall = tiny.rejectAll("spinalTap", "onlyTen", "littleMan")
|
||||
|
||||
assert.truthy(fall(nil, entity1))
|
||||
assert.truthy(ftap(nil, entity1))
|
||||
assert.falsy(ftap(nil, entity2))
|
||||
assert.truthy(fxform(nil, entity1))
|
||||
assert.truthy(fxform(nil, entity2))
|
||||
|
||||
assert.truthy(fall(nil, entity1))
|
||||
assert.truthy(fall(nil, entity2))
|
||||
assert.truthy(fall(nil, entity3))
|
||||
|
||||
assert.falsy(frtap(nil, entity1))
|
||||
assert.truthy(frtap(nil, entity2))
|
||||
assert.truthy(frtap(nil, entity3))
|
||||
|
||||
assert.truthy(frall(nil, entity1))
|
||||
assert.truthy(frall(nil, entity2))
|
||||
assert.truthy(frall(nil, entity3))
|
||||
|
||||
end)
|
||||
|
||||
it("Can use functions as subfilters", function()
|
||||
|
||||
local f1 = tiny.requireAny('a', 'b', 'c')
|
||||
local f2 = tiny.requireAll('x', 'y', 'z')
|
||||
local f = tiny.requireAll(f1, f2)
|
||||
|
||||
assert.truthy(f(nil, {
|
||||
x = true, y = true, z = true, a = true, b = true, c = true
|
||||
}))
|
||||
assert.truthy(f(nil, {
|
||||
x = true, y = true, z = true, a = true
|
||||
}))
|
||||
assert.falsy(f(nil, {
|
||||
x = true, y = true, a = true
|
||||
}))
|
||||
assert.falsy(f(nil, {
|
||||
x = true, y = true, z = true
|
||||
}))
|
||||
|
||||
end)
|
||||
|
||||
it("Can use string filters", function()
|
||||
|
||||
local f = tiny.filter('a|b|c')
|
||||
|
||||
assert.truthy(f(nil, {
|
||||
a = true, b = true, c = true
|
||||
}))
|
||||
assert.truthy(f(nil, {
|
||||
a = true
|
||||
}))
|
||||
assert.truthy(f(nil, {
|
||||
b = true
|
||||
}))
|
||||
assert.truthy(f(nil, {
|
||||
c = true
|
||||
}))
|
||||
assert.falsy(f(nil, {
|
||||
x = true, y = true, z = true
|
||||
}))
|
||||
|
||||
end)
|
||||
|
||||
end)
|
||||
|
||||
describe('World:', function()
|
||||
|
||||
local world, entity1, entity2, entity3
|
||||
|
||||
local moveSystem = tiny.processingSystem()
|
||||
moveSystem.filter = tiny.requireAll("xform", "vel")
|
||||
function moveSystem:process(e, dt)
|
||||
local xform = e.xform
|
||||
local vel = e.vel
|
||||
local x, y = xform.x, xform.y
|
||||
local xvel, yvel = vel.x, vel.y
|
||||
xform.x, xform.y = x + xvel * dt, y + yvel * dt
|
||||
end
|
||||
|
||||
local timePassed = 0
|
||||
local oneTimeSystem = tiny.system()
|
||||
function oneTimeSystem:update(dt)
|
||||
timePassed = timePassed + dt
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
entity1 = deep_copy(entityTemplate1)
|
||||
entity2 = deep_copy(entityTemplate2)
|
||||
entity3 = deep_copy(entityTemplate3)
|
||||
world = tiny.world(moveSystem, oneTimeSystem, entity1, entity2, entity3)
|
||||
timePassed = 0
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
world:clearSystems()
|
||||
world:refresh()
|
||||
end)
|
||||
|
||||
it("Create World", function()
|
||||
assert.equals(world:getEntityCount(), 3)
|
||||
assert.equals(world:getSystemCount(), 2)
|
||||
end)
|
||||
|
||||
it("Run simple simulation", function()
|
||||
world:update(1)
|
||||
assert.equals(timePassed, 1)
|
||||
assert.equals(entity1.xform.x, 1)
|
||||
assert.equals(entity1.xform.y, 2)
|
||||
end)
|
||||
|
||||
it("Remove Entities", function()
|
||||
world:remove(entity1, entity2)
|
||||
world:update(1)
|
||||
assert.equals(timePassed, 1)
|
||||
assert.equals(entity1.xform.x, entityTemplate1.xform.x)
|
||||
assert.equals(entity2.xform.x, entityTemplate2.xform.x)
|
||||
assert.equals(entity3.xform.y, 8)
|
||||
end)
|
||||
|
||||
it("Remove Systems", function()
|
||||
world:remove(moveSystem, oneTimeSystem)
|
||||
world:update(1)
|
||||
assert.equals(timePassed, 0)
|
||||
assert.equals(entity1.xform.x, entityTemplate1.xform.x)
|
||||
assert.equals(0, world:getSystemCount())
|
||||
end)
|
||||
|
||||
it("Deactivate and Activate Systems", function()
|
||||
moveSystem.active = false
|
||||
oneTimeSystem.active = false
|
||||
world:update(1)
|
||||
assert.equals(world:getSystemCount(), 2)
|
||||
assert.equals(timePassed, 0)
|
||||
assert.equals(entity1.xform.x, entityTemplate1.xform.x)
|
||||
moveSystem.active = true
|
||||
oneTimeSystem.active = true
|
||||
world:update(1)
|
||||
assert.equals(timePassed, 1)
|
||||
assert.are_not.equal(entity1.xform.x, entityTemplate1.xform.x)
|
||||
assert.equals(world:getSystemCount(), 2)
|
||||
end)
|
||||
|
||||
it("Clear Entities", function()
|
||||
world:clearEntities()
|
||||
world:update(1)
|
||||
assert.equals(0, world:getEntityCount())
|
||||
end)
|
||||
|
||||
it("Clear Systems", function()
|
||||
world:clearSystems()
|
||||
world:update(1)
|
||||
assert.equals(0, world:getSystemCount())
|
||||
end)
|
||||
|
||||
it("Add Entities Multiple Times", function()
|
||||
world:update(1)
|
||||
world:add(entity1, entity2, entity3)
|
||||
world:update(2)
|
||||
assert.equals(2, world:getSystemCount())
|
||||
assert.equals(3, world:getEntityCount())
|
||||
end)
|
||||
|
||||
it("Remove Entities Multiple Times", function()
|
||||
assert.equals(3, world:getEntityCount())
|
||||
world:update(1)
|
||||
world:remove(entity1, entity2, entity3)
|
||||
world:update(2)
|
||||
assert.equals(0, world:getEntityCount())
|
||||
world:remove(entity1, entity2, entity3)
|
||||
world:update(2)
|
||||
assert.equals(2, world:getSystemCount())
|
||||
assert.equals(0, world:getEntityCount())
|
||||
end)
|
||||
|
||||
it("Add Systems Multiple Times", function()
|
||||
world:update(1)
|
||||
assert.has_error(function() world:add(moveSystem, oneTimeSystem) end, "System already belongs to a World.")
|
||||
world:update(2)
|
||||
assert.equals(2, world:getSystemCount())
|
||||
assert.equals(3, world:getEntityCount())
|
||||
end)
|
||||
|
||||
it("Remove Systems Multiple Times", function()
|
||||
world:update(1)
|
||||
world:remove(moveSystem)
|
||||
world:update(2)
|
||||
assert.has_error(function() world:remove(moveSystem) end, "System does not belong to this World.")
|
||||
world:update(2)
|
||||
assert.equals(1, world:getSystemCount())
|
||||
assert.equals(3, world:getEntityCount())
|
||||
end)
|
||||
|
||||
it("Reorder Systems", function()
|
||||
world:update(1)
|
||||
world:setSystemIndex(moveSystem, 2)
|
||||
world:update(1)
|
||||
assert.equals(2, moveSystem.index)
|
||||
assert.equals(1, oneTimeSystem.index)
|
||||
end)
|
||||
|
||||
it("Sorts Entities in Sorting Systems", function()
|
||||
local sortsys = tiny.sortedProcessingSystem()
|
||||
sortsys.filter = tiny.filter("vel|xform")
|
||||
function sortsys:compare(e1, e2)
|
||||
return e1.vel.x < e2.vel.x
|
||||
end
|
||||
world:add(sortsys)
|
||||
world:refresh()
|
||||
assert.equals(sortsys.entities[1], entity2)
|
||||
assert.equals(sortsys.entities[2], entity3)
|
||||
assert.equals(sortsys.entities[3], entity1)
|
||||
end)
|
||||
|
||||
it("Runs preWrap and postWrap for systems.", function()
|
||||
local str = ""
|
||||
local sys1 = tiny.system()
|
||||
local sys2 = tiny.system()
|
||||
function sys1:preWrap(dt)
|
||||
str = str .. "<"
|
||||
end
|
||||
function sys2:preWrap(dt)
|
||||
str = str .. "{"
|
||||
end
|
||||
function sys1:postWrap(dt)
|
||||
str = str .. ">"
|
||||
end
|
||||
function sys2:postWrap(dt)
|
||||
str = str .. "}"
|
||||
end
|
||||
world:add(sys1, sys2)
|
||||
world:update(1)
|
||||
assert.equals(str, "{<>}")
|
||||
end)
|
||||
|
||||
end)
|
||||
|
||||
it("Doesn't pollute the global namespace", function()
|
||||
assert.are.same(_G, GLOBALS)
|
||||
end)
|
||||
|
||||
end)
|
||||
@@ -1,23 +0,0 @@
|
||||
package = "tiny-ecs"
|
||||
version = "scm-0"
|
||||
source = {
|
||||
url = "git://github.com/bakpakin/tiny-ecs",
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,861 +0,0 @@
|
||||
--[[
|
||||
Copyright (c) 2016 Calvin Rose
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
--- @module tiny-ecs
|
||||
-- @author Calvin Rose
|
||||
-- @license MIT
|
||||
-- @copyright 2016
|
||||
local tiny = {}
|
||||
|
||||
-- Local versions of standard lua functions
|
||||
local tinsert = table.insert
|
||||
local tremove = table.remove
|
||||
local tsort = table.sort
|
||||
local setmetatable = setmetatable
|
||||
local type = type
|
||||
local select = select
|
||||
|
||||
-- Local versions of the library functions
|
||||
local tiny_manageEntities
|
||||
local tiny_manageSystems
|
||||
local tiny_addEntity
|
||||
local tiny_addSystem
|
||||
local tiny_add
|
||||
local tiny_removeEntity
|
||||
local tiny_removeSystem
|
||||
|
||||
--- Filter functions.
|
||||
-- A Filter is a function that selects which Entities apply to a System.
|
||||
-- Filters take two parameters, the System and the Entity, and return a boolean
|
||||
-- value indicating if the Entity should be processed by the System. A truthy
|
||||
-- value includes the entity, while a falsey (nil or false) value excludes the
|
||||
-- entity.
|
||||
--
|
||||
-- Filters must be added to Systems by setting the `filter` field of the System.
|
||||
-- Filter's returned by tiny-ecs's Filter functions are immutable and can be
|
||||
-- used by multiple Systems.
|
||||
--
|
||||
-- local f1 = tiny.requireAll("position", "velocity", "size")
|
||||
-- local f2 = tiny.requireAny("position", "velocity", "size")
|
||||
--
|
||||
-- local e1 = {
|
||||
-- position = {2, 3},
|
||||
-- velocity = {3, 3},
|
||||
-- size = {4, 4}
|
||||
-- }
|
||||
--
|
||||
-- local entity2 = {
|
||||
-- position = {4, 5},
|
||||
-- size = {4, 4}
|
||||
-- }
|
||||
--
|
||||
-- local e3 = {
|
||||
-- position = {2, 3},
|
||||
-- velocity = {3, 3}
|
||||
-- }
|
||||
--
|
||||
-- print(f1(nil, e1), f1(nil, e2), f1(nil, e3)) -- prints true, false, false
|
||||
-- print(f2(nil, e1), f2(nil, e2), f2(nil, e3)) -- prints true, true, true
|
||||
--
|
||||
-- Filters can also be passed as arguments to other Filter constructors. This is
|
||||
-- a powerful way to create complex, custom Filters that select a very specific
|
||||
-- set of Entities.
|
||||
--
|
||||
-- -- Selects Entities with an "image" Component, but not Entities with a
|
||||
-- -- "Player" or "Enemy" Component.
|
||||
-- filter = tiny.requireAll("image", tiny.rejectAny("Player", "Enemy"))
|
||||
--
|
||||
-- @section Filter
|
||||
|
||||
-- A helper function to compile filters.
|
||||
local filterJoin
|
||||
|
||||
-- A helper function to filters from string
|
||||
local filterBuildString
|
||||
|
||||
do
|
||||
|
||||
local loadstring = loadstring or load
|
||||
local function getchr(c)
|
||||
return "\\" .. c:byte()
|
||||
end
|
||||
local function make_safe(text)
|
||||
return ("%q"):format(text):gsub('\n', 'n'):gsub("[\128-\255]", getchr)
|
||||
end
|
||||
|
||||
local function filterJoinRaw(prefix, seperator, ...)
|
||||
local accum = {}
|
||||
local build = {}
|
||||
for i = 1, select('#', ...) do
|
||||
local item = select(i, ...)
|
||||
if type(item) == 'string' then
|
||||
accum[#accum + 1] = ("(e[%s] ~= nil)"):format(make_safe(item))
|
||||
elseif type(item) == 'function' then
|
||||
build[#build + 1] = ('local subfilter_%d_ = select(%d, ...)')
|
||||
:format(i, i)
|
||||
accum[#accum + 1] = ('(subfilter_%d_(system, e))'):format(i)
|
||||
else
|
||||
error 'Filter token must be a string or a filter function.'
|
||||
end
|
||||
end
|
||||
local source = ('%s\nreturn function(system, e) return %s(%s) end')
|
||||
:format(
|
||||
table.concat(build, '\n'),
|
||||
prefix,
|
||||
table.concat(accum, seperator))
|
||||
local loader, err = loadstring(source)
|
||||
if err then error(err) end
|
||||
return loader(...)
|
||||
end
|
||||
|
||||
function filterJoin(...)
|
||||
local state, value = pcall(filterJoinRaw, ...)
|
||||
if state then return value else return nil, value end
|
||||
end
|
||||
|
||||
local function buildPart(str)
|
||||
local accum = {}
|
||||
local subParts = {}
|
||||
str = str:gsub('%b()', function(p)
|
||||
subParts[#subParts + 1] = buildPart(p:sub(2, -2))
|
||||
return ('\255%d'):format(#subParts)
|
||||
end)
|
||||
for invert, part, sep in str:gmatch('(%!?)([^%|%&%!]+)([%|%&]?)') do
|
||||
if part:match('^\255%d+$') then
|
||||
local partIndex = tonumber(part:match(part:sub(2)))
|
||||
accum[#accum + 1] = ('%s(%s)')
|
||||
:format(invert == '' and '' or 'not', subParts[partIndex])
|
||||
else
|
||||
accum[#accum + 1] = ("(e[%s] %s nil)")
|
||||
:format(make_safe(part), invert == '' and '~=' or '==')
|
||||
end
|
||||
if sep ~= '' then
|
||||
accum[#accum + 1] = (sep == '|' and ' or ' or ' and ')
|
||||
end
|
||||
end
|
||||
return table.concat(accum)
|
||||
end
|
||||
|
||||
function filterBuildString(str)
|
||||
local source = ("return function(_, e) return %s end")
|
||||
:format(buildPart(str))
|
||||
local loader, err = loadstring(source)
|
||||
if err then
|
||||
error(err)
|
||||
end
|
||||
return loader()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Makes a Filter that selects Entities with all specified Components and
|
||||
-- Filters.
|
||||
function tiny.requireAll(...)
|
||||
return filterJoin('', ' and ', ...)
|
||||
end
|
||||
|
||||
--- Makes a Filter that selects Entities with at least one of the specified
|
||||
-- Components and Filters.
|
||||
function tiny.requireAny(...)
|
||||
return filterJoin('', ' or ', ...)
|
||||
end
|
||||
|
||||
--- Makes a Filter that rejects Entities with all specified Components and
|
||||
-- Filters, and selects all other Entities.
|
||||
function tiny.rejectAll(...)
|
||||
return filterJoin('not', ' and ', ...)
|
||||
end
|
||||
|
||||
--- Makes a Filter that rejects Entities with at least one of the specified
|
||||
-- Components and Filters, and selects all other Entities.
|
||||
function tiny.rejectAny(...)
|
||||
return filterJoin('not', ' or ', ...)
|
||||
end
|
||||
|
||||
--- Makes a Filter from a string. Syntax of `pattern` is as follows.
|
||||
--
|
||||
-- * Tokens are alphanumeric strings including underscores.
|
||||
-- * Tokens can be separated by |, &, or surrounded by parentheses.
|
||||
-- * Tokens can be prefixed with !, and are then inverted.
|
||||
--
|
||||
-- Examples are best:
|
||||
-- 'a|b|c' - Matches entities with an 'a' OR 'b' OR 'c'.
|
||||
-- 'a&!b&c' - Matches entities with an 'a' AND NOT 'b' AND 'c'.
|
||||
-- 'a|(b&c&d)|e - Matches 'a' OR ('b' AND 'c' AND 'd') OR 'e'
|
||||
-- @param pattern
|
||||
function tiny.filter(pattern)
|
||||
local state, value = pcall(filterBuildString, pattern)
|
||||
if state then return value else return nil, value end
|
||||
end
|
||||
|
||||
--- System functions.
|
||||
-- A System is a wrapper around function callbacks for manipulating Entities.
|
||||
-- Systems are implemented as tables that contain at least one method;
|
||||
-- an update function that takes parameters like so:
|
||||
--
|
||||
-- * `function system:update(dt)`.
|
||||
--
|
||||
-- There are also a few other optional callbacks:
|
||||
--
|
||||
-- * `function system:filter(entity)` - Returns true if this System should
|
||||
-- include this Entity, otherwise should return false. If this isn't specified,
|
||||
-- no Entities are included in the System.
|
||||
-- * `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.
|
||||
-- * `function system:preWrap(dt)` - Called on each system before update is
|
||||
-- called on any system.
|
||||
-- * `function system:postWrap(dt) - Called on each system in reverse order
|
||||
-- after update is called on each system. The idea behind `preWrap` and
|
||||
-- `postWrap` is to allow for systems that modify the behavior of other systems.
|
||||
-- Say there is a DrawingSystem, which draws sprites to the screen, and a
|
||||
-- PostProcessingSystem, that adds some blur and bloom effects. In the preWrap
|
||||
-- method of the PostProcessingSystem, the System could set the drawing target
|
||||
-- for the DrawingSystem to a special buffer instead the screen. In the postWrap
|
||||
-- method, the PostProcessingSystem could then modify the buffer and render it
|
||||
-- to the screen. In this setup, the PostProcessingSystem would be added to the
|
||||
-- World after the drawingSystem (A similar but less flexible behavior could
|
||||
-- be accomplished with a single custom update function in the DrawingSystem).
|
||||
--
|
||||
-- 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
|
||||
-- so:
|
||||
-- system.filter = tiny.requireAll("a", "b", "c")
|
||||
-- or
|
||||
-- function system:filter(entity)
|
||||
-- return entity.myRequiredComponentName ~= nil
|
||||
-- end
|
||||
--
|
||||
-- All Systems also have a few important fields that are initialized when the
|
||||
-- system is added to the World. A few are important, and few should be less
|
||||
-- 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.
|
||||
-- Inactive Systems should be updated manually or not at all via
|
||||
-- `system:update(dt)`. Defaults to true.
|
||||
-- * 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.
|
||||
-- * 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
|
||||
-- `entities` list. Most Systems can ignore this.
|
||||
-- * The `modified` flag is an indicator if the System has been modified in
|
||||
-- the last update. If so, the `onModify` callback will be called on the System
|
||||
-- in the next update, if it has one. This is usually managed by tiny-ecs, so
|
||||
-- users should mostly ignore this, too.
|
||||
--
|
||||
-- There is another option to (hopefully) increase performance in systems that
|
||||
-- have items added to or removed from them often, and have lots of entities in
|
||||
-- them. Setting the `nocache' field of the system might improve performance.
|
||||
-- It is still experimental. There are some restriction to systems without
|
||||
-- caching, however. * There is no `entities` table. * Callbacks such onAdd,
|
||||
-- onRemove, and onModify will never be called * Noncached systems cannot be
|
||||
-- sorted (There is no entities list to sort).
|
||||
--
|
||||
-- @section System
|
||||
|
||||
-- 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 = { "SYSTEM_TABLE_KEY" }
|
||||
|
||||
-- Checks if a table is a System.
|
||||
local function isSystem(table)
|
||||
return table[systemTableKey]
|
||||
end
|
||||
|
||||
-- Update function for all Processing Systems.
|
||||
local function processingSystemUpdate(system, dt)
|
||||
local preProcess = system.preProcess
|
||||
local process = system.process
|
||||
local postProcess = system.postProcess
|
||||
|
||||
if preProcess then
|
||||
preProcess(system, dt)
|
||||
end
|
||||
|
||||
if process then
|
||||
if system.nocache then
|
||||
local entities = system.world.entityList
|
||||
local filter = system.filter
|
||||
if filter then
|
||||
for i = 1, #entities do
|
||||
local entity = entities[i]
|
||||
if filter(system, entity) then
|
||||
process(system, entity, dt)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local entities = system.entities
|
||||
for i = 1, #entities do
|
||||
process(system, entities[i], dt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if postProcess then
|
||||
postProcess(system, dt)
|
||||
end
|
||||
end
|
||||
|
||||
-- Sorts Systems by a function system.sortDelegate(entity1, entity2) on modify.
|
||||
local function sortedSystemOnModify(system)
|
||||
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
|
||||
indices[entities[i]] = 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.
|
||||
--
|
||||
-- function system:preProcess(dt) -- Called before iteration.
|
||||
-- function system:process(entity, dt) -- Process each entity.
|
||||
-- function system:postProcess(dt) -- Called after iteration.
|
||||
--
|
||||
-- Processing Systems have their own `update` method, so don't implement a
|
||||
-- a custom `update` callback for Processing Systems.
|
||||
-- @see system
|
||||
function tiny.processingSystem(table)
|
||||
table = table or {}
|
||||
table[systemTableKey] = true
|
||||
table.update = processingSystemUpdate
|
||||
return table
|
||||
end
|
||||
|
||||
--- Creates a new Sorted System or Sorted System class. Sorted Systems sort
|
||||
-- their Entities according to a user-defined method, `system:compare(e1, e2)`,
|
||||
-- which should return true if `e1` should come before `e2` and false otherwise.
|
||||
-- Sorted Systems also override the default System's `onModify` callback, so be
|
||||
-- careful if defining a custom callback. However, for processing the sorted
|
||||
-- entities, consider `tiny.sortedProcessingSystem(table)`.
|
||||
-- @see system
|
||||
function tiny.sortedSystem(table)
|
||||
table = table or {}
|
||||
table[systemTableKey] = true
|
||||
table.onModify = sortedSystemOnModify
|
||||
return table
|
||||
end
|
||||
|
||||
--- Creates a new Sorted Processing System or Sorted Processing System class.
|
||||
-- Sorted Processing Systems have both the aspects of Processing Systems and
|
||||
-- Sorted Systems.
|
||||
-- @see system
|
||||
-- @see processingSystem
|
||||
-- @see sortedSystem
|
||||
function tiny.sortedProcessingSystem(table)
|
||||
table = table or {}
|
||||
table[systemTableKey] = true
|
||||
table.update = processingSystemUpdate
|
||||
table.onModify = sortedSystemOnModify
|
||||
return table
|
||||
end
|
||||
|
||||
--- World functions.
|
||||
-- A World is a container that manages Entities and Systems. Typically, a
|
||||
-- program uses one World at a time.
|
||||
--
|
||||
-- For all World functions except `tiny.world(...)`, object-oriented syntax can
|
||||
-- be used instead of the documented syntax. For example,
|
||||
-- `tiny.add(world, e1, e2, e3)` is the same as `world:add(e1, e2, e3)`.
|
||||
-- @section World
|
||||
|
||||
-- Forward declaration
|
||||
local worldMetaTable
|
||||
|
||||
--- Creates a new World.
|
||||
-- Can optionally add default Systems and Entities. Returns the new World along
|
||||
-- with default Entities and Systems.
|
||||
function tiny.world(...)
|
||||
local ret = setmetatable({
|
||||
|
||||
-- List of Entities to remove
|
||||
entitiesToRemove = {},
|
||||
|
||||
-- List of Entities to change
|
||||
entitiesToChange = {},
|
||||
|
||||
-- List of Entities to add
|
||||
systemsToAdd = {},
|
||||
|
||||
-- List of Entities to remove
|
||||
systemsToRemove = {},
|
||||
|
||||
-- Set of Entities
|
||||
entities = {},
|
||||
|
||||
-- List of Systems
|
||||
systems = {}
|
||||
|
||||
}, worldMetaTable)
|
||||
|
||||
tiny_add(ret, ...)
|
||||
tiny_manageSystems(ret)
|
||||
tiny_manageEntities(ret)
|
||||
|
||||
return ret, ...
|
||||
end
|
||||
|
||||
--- Adds an Entity to the world.
|
||||
-- Also call this on Entities that have changed Components such that they
|
||||
-- match different Filters. Returns the Entity.
|
||||
function tiny.addEntity(world, entity)
|
||||
local e2c = world.entitiesToChange
|
||||
e2c[#e2c + 1] = entity
|
||||
return entity
|
||||
end
|
||||
tiny_addEntity = tiny.addEntity
|
||||
|
||||
--- Adds a System to the world. Returns the System.
|
||||
function tiny.addSystem(world, system)
|
||||
assert(system.world == nil, "System already belongs to a World.")
|
||||
local s2a = world.systemsToAdd
|
||||
s2a[#s2a + 1] = system
|
||||
system.world = world
|
||||
return system
|
||||
end
|
||||
tiny_addSystem = tiny.addSystem
|
||||
|
||||
--- Shortcut for adding multiple Entities and Systems to the World. Returns all
|
||||
-- added Entities and Systems.
|
||||
function tiny.add(world, ...)
|
||||
for i = 1, select("#", ...) do
|
||||
local obj = select(i, ...)
|
||||
if obj then
|
||||
if isSystem(obj) then
|
||||
tiny_addSystem(world, obj)
|
||||
else -- Assume obj is an Entity
|
||||
tiny_addEntity(world, obj)
|
||||
end
|
||||
end
|
||||
end
|
||||
return ...
|
||||
end
|
||||
tiny_add = tiny.add
|
||||
|
||||
--- Removes an Entity from the World. Returns the Entity.
|
||||
function tiny.removeEntity(world, entity)
|
||||
local e2r = world.entitiesToRemove
|
||||
e2r[#e2r + 1] = entity
|
||||
return entity
|
||||
end
|
||||
tiny_removeEntity = tiny.removeEntity
|
||||
|
||||
--- Removes a System from the world. Returns the System.
|
||||
function tiny.removeSystem(world, system)
|
||||
assert(system.world == world, "System does not belong to this World.")
|
||||
local s2r = world.systemsToRemove
|
||||
s2r[#s2r + 1] = system
|
||||
return system
|
||||
end
|
||||
tiny_removeSystem = tiny.removeSystem
|
||||
|
||||
--- Shortcut for removing multiple Entities and Systems from the World. Returns
|
||||
-- all removed Systems and Entities
|
||||
function tiny.remove(world, ...)
|
||||
for i = 1, select("#", ...) do
|
||||
local obj = select(i, ...)
|
||||
if obj then
|
||||
if isSystem(obj) then
|
||||
tiny_removeSystem(world, obj)
|
||||
else -- Assume obj is an Entity
|
||||
tiny_removeEntity(world, obj)
|
||||
end
|
||||
end
|
||||
end
|
||||
return ...
|
||||
end
|
||||
|
||||
-- Adds and removes Systems that have been marked from the World.
|
||||
function tiny_manageSystems(world)
|
||||
local s2a, s2r = world.systemsToAdd, world.systemsToRemove
|
||||
|
||||
-- Early exit
|
||||
if #s2a == 0 and #s2r == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
world.systemsToAdd = {}
|
||||
world.systemsToRemove = {}
|
||||
|
||||
local worldEntityList = world.entities
|
||||
local systems = world.systems
|
||||
|
||||
-- Remove Systems
|
||||
for i = 1, #s2r do
|
||||
local system = s2r[i]
|
||||
local index = system.index
|
||||
local onRemove = system.onRemove
|
||||
if onRemove and not system.nocache then
|
||||
local entityList = system.entities
|
||||
for j = 1, #entityList do
|
||||
onRemove(system, entityList[j])
|
||||
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
|
||||
|
||||
-- Clean up System
|
||||
system.world = nil
|
||||
system.entities = nil
|
||||
system.indices = nil
|
||||
system.index = nil
|
||||
end
|
||||
|
||||
-- Add Systems
|
||||
for i = 1, #s2a do
|
||||
local system = s2a[i]
|
||||
if systems[system.index or 0] ~= system then
|
||||
if not system.nocache then
|
||||
system.entities = {}
|
||||
system.indices = {}
|
||||
end
|
||||
if system.active == nil then
|
||||
system.active = true
|
||||
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
|
||||
if not system.nocache then
|
||||
local entityList = system.entities
|
||||
local entityIndices = system.indices
|
||||
local onAdd = system.onAdd
|
||||
local filter = system.filter
|
||||
if filter then
|
||||
for j = 1, #worldEntityList do
|
||||
local entity = worldEntityList[j]
|
||||
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
|
||||
s2a[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Adds, removes, and changes Entities that have been marked.
|
||||
function tiny_manageEntities(world)
|
||||
|
||||
local e2r = world.entitiesToRemove
|
||||
local e2c = world.entitiesToChange
|
||||
|
||||
-- Early exit
|
||||
if #e2r == 0 and #e2c == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
world.entitiesToChange = {}
|
||||
world.entitiesToRemove = {}
|
||||
|
||||
local entities = world.entities
|
||||
local systems = world.systems
|
||||
|
||||
-- Change Entities
|
||||
for i = 1, #e2c do
|
||||
local entity = e2c[i]
|
||||
-- Add if needed
|
||||
if not entities[entity] then
|
||||
local index = #entities + 1
|
||||
entities[entity] = index
|
||||
entities[index] = entity
|
||||
end
|
||||
for j = 1, #systems do
|
||||
local system = systems[j]
|
||||
if not system.nocache then
|
||||
local ses = system.entities
|
||||
local seis = system.indices
|
||||
local index = seis[entity]
|
||||
local filter = system.filter
|
||||
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
|
||||
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
|
||||
|
||||
-- Remove Entities
|
||||
for i = 1, #e2r do
|
||||
local entity = e2r[i]
|
||||
e2r[i] = nil
|
||||
local listIndex = entities[entity]
|
||||
if listIndex then
|
||||
-- Remove Entity from world state
|
||||
local lastEntity = entities[#entities]
|
||||
entities[lastEntity] = listIndex
|
||||
entities[entity] = nil
|
||||
entities[listIndex] = lastEntity
|
||||
entities[#entities] = nil
|
||||
-- Remove from cached systems
|
||||
for j = 1, #systems do
|
||||
local system = systems[j]
|
||||
if not system.nocache then
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
--- Manages Entities and Systems marked for deletion or addition. Call this
|
||||
-- before modifying Systems and Entities outside of a call to `tiny.update`.
|
||||
-- Do not call this within a call to `tiny.update`.
|
||||
function tiny.refresh(world)
|
||||
tiny_manageSystems(world)
|
||||
tiny_manageEntities(world)
|
||||
local systems = world.systems
|
||||
for i = #systems, 1, -1 do
|
||||
local system = systems[i]
|
||||
if system.active then
|
||||
local onModify = system.onModify
|
||||
if onModify and system.modified then
|
||||
onModify(system, 0)
|
||||
end
|
||||
system.modified = false
|
||||
end
|
||||
end
|
||||
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_manageEntities(world)
|
||||
|
||||
local systems = world.systems
|
||||
|
||||
-- Iterate through Systems IN REVERSE ORDER
|
||||
for i = #systems, 1, -1 do
|
||||
local system = systems[i]
|
||||
if system.active then
|
||||
-- Call the modify callback on Systems that have been modified.
|
||||
local onModify = system.onModify
|
||||
if onModify and system.modified then
|
||||
onModify(system, dt)
|
||||
end
|
||||
local preWrap = system.preWrap
|
||||
if preWrap and
|
||||
((not filter) or filter(world, system)) then
|
||||
preWrap(system, dt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Iterate through Systems IN ORDER
|
||||
for i = 1, #systems do
|
||||
local system = systems[i]
|
||||
if system.active and ((not filter) or filter(world, system)) then
|
||||
|
||||
-- Update Systems that have an update method (most Systems)
|
||||
local update = system.update
|
||||
if update then
|
||||
local interval = system.interval
|
||||
if interval then
|
||||
local bufferedTime = (system.bufferedTime or 0) + dt
|
||||
while bufferedTime >= interval do
|
||||
bufferedTime = bufferedTime - interval
|
||||
update(system, interval)
|
||||
end
|
||||
system.bufferedTime = bufferedTime
|
||||
else
|
||||
update(system, dt)
|
||||
end
|
||||
end
|
||||
|
||||
system.modified = false
|
||||
end
|
||||
end
|
||||
|
||||
-- Iterate through Systems IN ORDER AGAIN
|
||||
for i = 1, #systems do
|
||||
local system = systems[i]
|
||||
local postWrap = system.postWrap
|
||||
if postWrap and system.active and
|
||||
((not filter) or filter(world, system)) then
|
||||
postWrap(system, dt)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Removes all Entities from the World.
|
||||
function tiny.clearEntities(world)
|
||||
local el = world.entities
|
||||
for i = 1, #el do
|
||||
tiny_removeEntity(world, el[i])
|
||||
end
|
||||
end
|
||||
|
||||
--- Removes all Systems from the World.
|
||||
function tiny.clearSystems(world)
|
||||
local systems = world.systems
|
||||
for i = #systems, 1, -1 do
|
||||
tiny_removeSystem(world, systems[i])
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets number of Entities in the World.
|
||||
function tiny.getEntityCount(world)
|
||||
return #world.entities
|
||||
end
|
||||
|
||||
--- Gets number of Systems in World.
|
||||
function tiny.getSystemCount(world)
|
||||
return #world.systems
|
||||
end
|
||||
|
||||
--- Sets the index of a System in the World, and returns the old index. Changes
|
||||
-- the order in which they Systems processed, because lower indexed Systems are
|
||||
-- processed first. Returns the old system.index.
|
||||
function tiny.setSystemIndex(world, system, index)
|
||||
local oldIndex = system.index
|
||||
local systems = world.systems
|
||||
|
||||
if index < 0 then
|
||||
index = tiny.getSystemCount(world) + 1 + index
|
||||
end
|
||||
|
||||
tremove(systems, oldIndex)
|
||||
tinsert(systems, index, system)
|
||||
|
||||
for i = oldIndex, index, index >= oldIndex and 1 or -1 do
|
||||
systems[i].index = i
|
||||
end
|
||||
|
||||
return oldIndex
|
||||
end
|
||||
|
||||
-- Construct world metatable.
|
||||
worldMetaTable = {
|
||||
__index = {
|
||||
add = tiny.add,
|
||||
addEntity = tiny.addEntity,
|
||||
addSystem = tiny.addSystem,
|
||||
remove = tiny.remove,
|
||||
removeEntity = tiny.removeEntity,
|
||||
removeSystem = tiny.removeSystem,
|
||||
refresh = tiny.refresh,
|
||||
update = tiny.update,
|
||||
clearEntities = tiny.clearEntities,
|
||||
clearSystems = tiny.clearSystems,
|
||||
getEntityCount = tiny.getEntityCount,
|
||||
getSystemCount = tiny.getSystemCount,
|
||||
setSystemIndex = tiny.setSystemIndex
|
||||
},
|
||||
__tostring = function()
|
||||
return "<tiny-ecs_World>"
|
||||
end
|
||||
}
|
||||
|
||||
return tiny
|
||||
Reference in New Issue
Block a user