111 Commits

Author SHA1 Message Date
bakpakin
08e5f13d85 Begin experimental option to not use caching. 2016-03-06 14:42:48 -05:00
bakpakin
cbf71d2cb2 Add rockspec for version to 1.3-1 2016-03-05 16:42:14 -05:00
bakpakin
93a19d93c8 Wrap error throwing functions in pcalls and make doc look cleaner for tiny.filter. 2016-03-05 16:35:06 -05:00
bakpakin
4148855233 Bump version. 2016-03-05 16:20:09 -05:00
bakpakin
27f881444a Fix string filter generator. 2016-03-05 16:06:56 -05:00
bakpakin
3b4bd9920b Add generic filter. 2016-03-05 15:34:21 -05:00
bakpakin
206361ca38 Make filters compile. Add some filter tests. 2016-03-05 13:34:44 -05:00
bakpakin
29158282bd Update Copyright Date. 2016-02-29 19:19:34 -05:00
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
bakpakin
5978ace463 Update to version 1.1-3 2015-06-14 10:22:03 -04:00
bakpakin
a504bd6336 Add removeSystem to World metatable. 2015-06-14 10:17:18 -04:00
bakpakin
69ae37586e Fix scm rockspec. 2015-06-07 20:05:56 -04:00
bakpakin
44fb94c457 Add scm rockspec. Fix README. 2015-06-07 18:47:50 -04:00
bakpakin
9230dad1e1 Update README.md to direct users to demo. 2015-05-17 08:26:04 +08:00
bakpakin
e4bf3f3767 Update to version 1.1-2. 2015-05-07 19:53:51 +08:00
bakpakin
71e97c12f6 Improve System documentation. 2015-05-07 19:45:28 +08:00
bakpakin
7c4ff7747c Change names of tiny.requireOne and tiny.rejectOne to tiny.requireAny and tiny.rejectAny. 2015-05-07 17:32:32 +08:00
bakpakin
808407f7c4 Fix 'tiny.add' and 'tiny.remove' iterating nil values. 2015-05-05 22:21:39 +08:00
bakpakin
153f326612 Add two new filter functions - rejectAll and rejectOne. 2015-05-05 22:03:04 +08:00
bakpakin
a4e3c90724 Update version to 1.1-1. 2015-05-04 20:24:03 +08:00
bakpakin
e47920f332 Update documentation. 2015-05-04 20:21:12 +08:00
bakpakin
e96d494526 Change sorted System calbacks. 2015-05-04 12:05:53 +08:00
bakpakin
994f3b6533 Remove tiny.activate and tiny.deactivate. 2015-05-04 11:46:18 +08:00
bakpakin
264d835334 Fix Clearing Systems. 2015-05-04 09:32:04 +08:00
bakpakin
e54f29884c Remove systemData from luadoc. 2015-05-04 09:24:36 +08:00
bakpakin
9991ff7474 Remove use of systemData - instead, Systems now have mutable data.
This removes a level of indirection and prevents passing around of entities.
2015-05-04 09:09:34 +08:00
bakpakin
5e7d2ea7fc Add license in tiny.lua.
All non-release commits are now versioned 'dev'.
2015-05-04 08:21:30 +08:00
bakpakin
6d1b280a40 Make enabling/disabling Systems not use varargs. 2015-05-03 23:31:56 +08:00
bakpakin
fb345c7718 Add Sorted Systems.
Remove tiny.manageSystems and tiny.manageEntities from API.
Add onModify System callback.
2015-05-03 23:05:50 +08:00
bakpakin
0f86eae571 Remove pesky trailing whitespace >:( 2015-04-27 21:48:27 +08:00
bakpakin
fd49633d17 Don't update a System without an update field in the System. 2015-04-27 15:45:54 +08:00
bakpakin
94fc3ac453 Update README.md to include luarocks. 2015-04-27 14:24:59 +08:00
bakpakin
4109d37cdb Update README.md to include demo. 2015-04-27 14:20:34 +08:00
bakpakin
c9dccaf85f Ignore .rock. 2015-04-26 11:55:07 +08:00
bakpakin
d154c7f627 1.0-2 Release. 2015-04-26 11:40:53 +08:00
bakpakin
18bd48e163 Fix first rockspec. 2015-04-26 11:35:23 +08:00
bakpakin
724d5689fe Add 1.0.1 rockspec. 2015-04-26 11:29:20 +08:00
bakpakin
6531332ea8 Remove parallel tables that hold System data in favor of one table. 2015-04-26 09:55:55 +08:00
bakpakin
dc69636f72 Fix reordering of Systems. 2015-04-25 21:37:04 +08:00
bakpakin
dc3deab725 Remove inspect.lua from source. 2015-04-25 21:02:13 +08:00
bakpakin
39adae204b Fixed README.md example 2015-04-25 18:36:59 +08:00
bakpakin
f53db5ddb6 Update README.md 2015-04-25 18:30:05 +08:00
bakpakin
fa58c016e9 Update Version. 2015-04-25 18:16:14 +08:00
bakpakin
9a284ce9da Add some tests. 2015-04-25 18:14:41 +08:00
bakpakin
a612a7b13d Changed Implementation of Entity Storage.
Remove Use of Sets for Storing Entities in hopes to speed up Iteration.
Add some tests.
2015-04-25 17:59:41 +08:00
bakpakin
debce6666e Update Version. 2015-04-22 17:56:53 +08:00
bakpakin
b2f44feb5b Update README.md 2015-04-22 17:23:45 +08:00
bakpakin
049d0eba76 Add processing system back to new API. 2015-04-22 17:22:14 +08:00
bakpakin
b85fa59c74 Fix some doc typos. 2015-04-21 21:57:10 +08:00
bakpakin
29ed0977a0 Update to 1.0.0.
Change System creation API.
Add functionality for reordering Systems.
2015-04-21 21:47:36 +08:00
bakpakin
283d034b21 Clean up System and Entity adding code. Add options for adding and removeing single Entities and Systems. 2015-04-21 19:37:33 +08:00
bakpakin
beb043be49 Fix small bug when adding entities. 2015-04-19 14:22:51 +08:00
bakpakin
d1e5ad731a Fix bug with entity Count and adding already existing entities. 2015-04-19 13:40:42 +08:00
bakpakin
f23941a903 Fixed bug in ordering of systems. 2015-04-03 19:59:02 +08:00
bakpakin
77df6e20aa Change API for activating/deactivating systems. Fix bugs. Update version number. 2015-03-31 19:55:12 +08:00
bakpakin
53ef1f2d36 Add tests. Add getters and setters for entity and system count. 2015-03-31 17:59:17 +08:00
bakpakin
c5c27c679c Fix bug with deactivating Systems. Add test. 2015-03-30 15:22:23 +08:00
bakpakin
22daad2b26 Minor changes. 2015-03-29 20:39:59 +08:00
bakpakin
c38e5633d5 Start using LDoc for documentation.
Remove class system
Fix various small bug
2015-03-29 20:19:31 +08:00
bakpakin
8d4b994329 Change API.
Remove Aspects for Filters.
2015-03-29 17:07:39 +08:00
bakpakin
eb1ee4c210 Change repo name 2015-03-29 15:50:42 +08:00
bakpakin
26ecc4f0a5 Switch to 'busted' for testing
Update version, Unify add and remove methods for World
Fix small bugs
2015-03-29 11:19:22 +08:00
bakpakin
5b4acb55ee Remove OSX-specific .gitignore 2015-03-29 07:42:04 +08:00
bakpakin
231182e7c2 Add method for activating and deactivating Systems in Worlds. 2015-03-29 07:02:14 +08:00
bakpakin
05d7574794 Actually remove World:change(...)
Increase version number
2015-03-28 23:11:36 +08:00
bakpakin
c046bde701 Remove dependency on IDs in Systems and Entities.
This allows Systems to be reused in multiple worlds.
Fix a few bugs.
Remove World:change(...)
2015-03-28 22:54:34 +08:00
bakpakin
e8b4b7f106 Fix small but devestating bug for systems without Entities. 2015-03-28 21:03:42 +08:00
bakpakin
17592be9dc Add function to update systems indepenently of Worlds for, say, rendering. 2015-03-24 18:01:21 +08:00
bakpakin
db69cbca1d Add more callbacks to Systems for adding and removing entities. 2015-03-23 22:58:26 +08:00
bakpakin
408f436dd1 Change Example 2015-03-22 12:35:08 +08:00
bakpakin
216064d63c Update version to 0.0.2
Add System addition and removal
Changed implementation for Entity removal
Change README and example code
2015-03-22 12:07:42 +08:00
bakpakin
b98cd5a14d Rename World:free to World:remove.
Fix World:free bug.
Add __tostring method for Aspect and System.
Add a few more tests.
Add more comments.
Give systems ids.
Change injected "_id" field in Entities to
"id" field.
2015-03-22 10:41:34 +08:00
bakpakin
f4c6ce1004 Remove use of table.pack 2015-03-22 08:15:44 +08:00
bakpakin
f3b2e9a0e5 Initial Commit. 2015-03-21 16:14:12 +08:00
20 changed files with 1585 additions and 1041 deletions
Vendored
+8
View File
@@ -0,0 +1,8 @@
#Ignore generated documentation
doc/*
index.html
ldoc.css
topics/*
#Ignore generated rocks
*.rock
+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
View File
@@ -0,0 +1,7 @@
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.
+93
View File
@@ -0,0 +1,93 @@
# 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.
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(e.name .. ", who weighs " .. e.mass .. " pounds, says, \"" .. 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. Tagged releases will have a version
number in `tiny._VERSION`, while other commits will just have the string 'scm'.
## 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).
+13
View File
@@ -0,0 +1,13 @@
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
View File
@@ -1,729 +0,0 @@
<!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 |, &amp;, 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&amp;!b&amp;c' - Matches entities with an 'a' AND NOT 'b' AND 'c'.
'a|(b&amp;c&amp;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>
-312
View File
@@ -1,312 +0,0 @@
/* 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; }
+24
View File
@@ -0,0 +1,24 @@
package = "tiny-ecs"
version = "1.0-2"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.0-2"
}
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-1"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.1-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"
}
}
+24
View File
@@ -0,0 +1,24 @@
package = "tiny-ecs"
version = "1.1-2"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.1-2"
}
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-3"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.1-3"
}
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-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"
}
}
+24
View File
@@ -0,0 +1,24 @@
package = "tiny-ecs"
version = "1.3-1"
source = {
url = "git://github.com/bakpakin/tiny-ecs",
tag = "1.3-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"
}
}
+292
View File
@@ -0,0 +1,292 @@
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)
end)
it("Doesn't pollute the global namespace", function()
assert.are.same(_G, GLOBALS)
end)
end)
+23
View File
@@ -0,0 +1,23 @@
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"
}
}
+870
View File
@@ -0,0 +1,870 @@
--[[
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 2015
local tiny = { _VERSION = "scm" }
-- 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
local tiny_remove
--- 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.
--
-- 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 = ('do %s\n return function(system, e) return %s(%s) end 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 operated on with a boolean 'not'.
--
-- Examples are best:
-- 'a|b|c' - Matches entities with an 'a' component OR a 'b' component or a 'c' component.
-- 'a&!b&c' - Matches entities with an 'a' component AND NOT a 'b' component AND a 'c' component.
-- '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.
--
-- 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.
--
-- @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, 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
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 add
entitiesToAdd = {},
-- 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 Entities
entityList = {},
-- Number of Entities in World
entityCount = 0,
-- 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)
if world.entities[entity] then
local e2c = world.entitiesToChange
e2c[#e2c + 1] = entity
else
local e2a = world.entitiesToAdd
e2a[#e2a + 1] = entity
end
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 to 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
tiny_remove = tiny.remove
-- 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.entityList
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 e2a = world.entitiesToAdd
local e2r = world.entitiesToRemove
local e2c = world.entitiesToChange
-- Early exit
if #e2a == 0 and #e2r == 0 and #e2c == 0 then
return
end
world.entitiesToChange = {}
world.entitiesToAdd = {}
world.entitiesToRemove = {}
local entities = world.entities
local entityList = world.entityList
local systems = world.systems
local entityCount = world.entityCount
-- Change Entities
for i = 1, #e2c do
local entity = e2c[i]
if entities[entity] then
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
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 = entityList[#entityList]
entities[lastEntity] = listIndex
entities[entity] = nil
entityList[listIndex] = lastEntity
entityList[#entityList] = nil
entityCount = entityCount - 1
-- 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
-- Add Entities
for i = 1, #e2a do
local entity = e2a[i]
if not entities[entity] then
local listIndex = #entityList + 1
entities[entity] = listIndex
entityList[listIndex] = entity
entityCount = entityCount + 1
for j = 1, #systems do
local system = systems[j]
if not system.nocache then
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
end
-- Update Entity count
world.entityCount = entityCount
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 = 1, #systems 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 ORDER
for i = 1, #systems do
local system = systems[i]
if system.active and ((not filter) or filter(world, system)) 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
-- 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
if update then
update(system, interval)
end
end
system.bufferedTime = bufferedTime
else
update(system, dt)
end
end
system.modified = false
end
end
end
--- Removes all Entities from the World.
function tiny.clearEntities(world)
local el = world.entityList
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.entityCount
end
--- Gets number of Systems in World.
function tiny.getSystemCount(world)
return #(world.systems)
end
--- Gets the index of the System in the World.
-- A simpler alternative is `system.index`.
function tiny.getSystemIndex(world, system)
return system.index
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,
getSystemIndex = tiny.getSystemIndex,
setSystemIndex = tiny.setSystemIndex
},
__tostring = function(self)
return "<tiny-ecs_World>"
end
}
return tiny