Difference between revisions of "Tiny-ecs"

From GiderosMobile
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
__NOTOC__
+
__TOC__
  
=== tiny-ecs ===
+
== tiny-ecs ==
 
'''[https://github.com/bakpakin/tiny-ecs tiny-ecs GitHub]'''
 
'''[https://github.com/bakpakin/tiny-ecs tiny-ecs GitHub]'''
 
*Copyright: '''2016'''
 
*Copyright: '''2016'''
 
*License: '''MIT'''
 
*License: '''MIT'''
 
*Author: '''Calvin Rose'''
 
*Author: '''Calvin Rose'''
 +
 +
== Gideros version ==
 +
You can download the Gideros version which just adds the ''--!NOEXEC'' tag at the beginning of the file:
 +
 +
'''[[Media:tiny-ecs.lua]]''' '''(tip: right click and save link as)'''
 +
 +
'''Note: the file is supposed to be ''tiny-ecs.lua'' but MediaWiki puts a Capital letter :-('''
 +
 +
In your project you then call (please change the path accordingly):
 +
<syntaxhighlight lang="lua">
 +
tiny = require "tiny-ecs"
 +
</syntaxhighlight>
 +
 +
== Tutorial ==
 +
You can find a tutorial which walks you through a demo using tiny-ecs here:
 +
 +
'''<big>[[Tutorial - tiny-ecs demo]]</big>''' '''<big>a nice intro to the ECS paradigm</big>'''
  
 
== API ==
 
== API ==
=== Filter functions ===
+
'''http://bakpakin.github.io/tiny-ecs/doc/'''
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.
+
 
 +
----
 +
 
 +
=== 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)''.
 +
 
 +
==== tiny.world (...) ====
 +
Creates a new World. Can optionally add default Systems and Entities. Returns the new World along with default Entities and Systems.
 +
 
 +
==== tiny.addEntity (world, entity) ====
 +
Adds an Entity to the world. Also call this on Entities that have changed Components such that they match different Filters.
 +
 
 +
Returns the Entity.
 +
 
 +
==== tiny.addSystem (world, system) ====
 +
Adds a System to the world.
 +
 
 +
Returns the System.
 +
 
 +
==== tiny.add (world, ...) ====
 +
Shortcut for adding multiple Entities and Systems to the World.
 +
 
 +
Returns all added Entities and Systems.
 +
 
 +
==== tiny.removeEntity (world, entity) ====
 +
Removes an Entity from the World.
 +
 
 +
Returns the Entity.
 +
 
 +
==== tiny.removeSystem (world, system) ====
 +
Removes a System from the world.
 +
 
 +
Returns 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.
+
==== tiny.remove (world, ...) ====
<syntaxhighlight lang="lua">
+
Shortcut for removing multiple Entities and Systems from the World.
local f1 = tiny.requireAll("position", "velocity", "size")
 
local f2 = tiny.requireAny("position", "velocity", "size")
 
  
local e1 = {
+
Returns all removed Systems and Entities.
position = {2, 3},
 
velocity = {3, 3},
 
size = {4, 4}
 
}
 
  
local entity2 = {
+
==== tiny.refresh (world) ====
position = {4, 5},
+
Manages Entities and Systems marked for deletion or addition.
size = {4, 4}
 
}
 
  
local e3 = {
+
Call this before modifying Systems and Entities outside of a call to tiny.update. Do not call this within a call to tiny.update.
position = {2, 3},
 
velocity = {3, 3}
 
}
 
  
print(f1(nil, e1), f1(nil, e2), f1(nil, e3)) -- prints true, false, false
+
==== tiny.update (world, dt, filter) ====
print(f2(nil, e1), f2(nil, e2), f2(nil, e3)) -- prints true, true, true
+
Updates the World by dt (delta time).
</syntaxhighlight>
 
  
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.
+
Takes an optional parameter, filter, which is a Filter that selects Systems from the World, and updates only those Systems.
<syntaxhighlight lang="lua">
 
-- Selects Entities with an "image" Component, but not Entities with a
 
-- "Player" or "Enemy" Component.
 
filter = tiny.requireAll("image", tiny.rejectAny("Player", "Enemy"))
 
</syntaxhighlight>
 
  
==== tiny.requireAll (...)  ====
+
If filter is not supplied, all Systems are updated. Put this function in your main loop.
Makes a Filter that selects Entities with all specified Components and Filters.
 
  
==== tiny.requireAny (...) ====
+
==== tiny.clearEntities (world) ====
Makes a Filter that selects Entities with at least one of the specified Components and Filters.
+
Removes all Entities from the World.
  
==== tiny.rejectAll (...) ====
+
==== tiny.clearSystems (world) ====
Makes a Filter that rejects Entities with all specified Components and Filters, and selects all other Entities.
+
Removes all Systems from the World.
  
==== tiny.rejectAny (...) ====
+
==== tiny.getEntityCount (world) ====
Makes a Filter that rejects Entities with at least one of the specified Components and Filters, and selects all other Entities.
+
Gets number of Entities in the World.
  
==== tiny.filter (pattern) ====
+
==== tiny.getSystemCount (world) ====
Makes a Filter from a string. Syntax of pattern is as follows:
+
Gets number of Systems in World.
*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'''
 
<syntaxhighlight lang="lua">
 
'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'
 
</syntaxhighlight>
 
  
 +
==== tiny.setSystemIndex (world, system, index) ====
 +
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.
  
'''Note''': 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:
+
Returns the old system.index.
<syntaxhighlight lang="lua">
 
system.filter = tiny.requireAll("a", "b", "c")
 
</syntaxhighlight>
 
or
 
<syntaxhighlight lang="lua">
 
function system:filter(entity)
 
    return entity.myRequiredComponentName ~= nil
 
end
 
</syntaxhighlight>
 
  
  
Line 139: Line 159:
 
----
 
----
  
=== World functions ===
+
=== Filter functions ===
A World is a container that manages Entities and Systems. Typically, a program uses one World at a time.
+
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.
  
For all World functions except ''tiny.world(...)'', object-oriented syntax can be used instead of the documented syntax.
+
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.
 +
<syntaxhighlight lang="lua">
 +
local f1 = tiny.requireAll("position", "velocity", "size")
 +
local f2 = tiny.requireAny("position", "velocity", "size")
  
For example, ''tiny.add(world, e1, e2, e3)'' is the same as ''world:add(e1, e2, e3)''.
+
local e1 = {
 +
position = {2, 3},
 +
velocity = {3, 3},
 +
size = {4, 4}
 +
}
  
==== tiny.world (...) ====
+
local entity2 = {
Creates a new World. Can optionally add default Systems and Entities. Returns the new World along with default Entities and Systems.
+
position = {4, 5},
 +
size = {4, 4}
 +
}
  
==== tiny.addEntity (world, entity) ====
+
local e3 = {
Adds an Entity to the world. Also call this on Entities that have changed Components such that they match different Filters.
+
position = {2, 3},
 +
velocity = {3, 3}
 +
}
  
Returns the Entity.
+
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
 +
</syntaxhighlight>
  
==== tiny.addSystem (world, system) ====
+
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.
Adds a System to the world.
+
<syntaxhighlight lang="lua">
 +
-- Selects Entities with an "image" Component, but not Entities with a
 +
-- "Player" or "Enemy" Component.
 +
filter = tiny.requireAll("image", tiny.rejectAny("Player", "Enemy"))
 +
</syntaxhighlight>
  
Returns the System.
+
==== tiny.requireAll (...)  ====
 +
Makes a Filter that selects Entities with all specified Components and Filters.
  
==== tiny.add (world, ...) ====
+
==== tiny.requireAny (...) ====
Shortcut for adding multiple Entities and Systems to the World.
+
Makes a Filter that selects Entities with at least one of the specified Components and Filters.
  
Returns all added Entities and Systems.
+
==== tiny.rejectAll (...) ====
 +
Makes a Filter that rejects Entities with all specified Components and Filters, and selects all other Entities.
  
==== tiny.removeEntity (world, entity) ====
+
==== tiny.rejectAny (...) ====
Removes an Entity from the World.
+
Makes a Filter that rejects Entities with at least one of the specified Components and Filters, and selects all other Entities.
  
Returns the Entity.
+
==== tiny.filter (pattern) ====
 
+
Makes a Filter from a string. Syntax of pattern is as follows:
==== tiny.removeSystem (world, system) ====
+
*Tokens are alphanumeric strings including underscores
Removes a System from the world.
+
*Tokens can be separated by |, &, or surrounded by parentheses
 +
*Tokens can be prefixed with !, and are then inverted
 +
'''Examples are best'''
 +
<syntaxhighlight lang="lua">
 +
'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'
 +
</syntaxhighlight>
  
Returns the System.
 
  
==== tiny.remove (world, ...) ====
 
Shortcut for removing multiple Entities and Systems from the World.
 
  
Returns all removed Systems and Entities.
+
'''Note''': 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:
 +
<syntaxhighlight lang="lua">
 +
system.filter = tiny.requireAll("a", "b", "c")
 +
</syntaxhighlight>
 +
or
 +
<syntaxhighlight lang="lua">
 +
function system:filter(entity)
 +
    return entity.myRequiredComponentName ~= nil
 +
end
 +
</syntaxhighlight>
  
==== tiny.refresh (world) ====
 
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.
 
 
==== tiny.update (world, dt, filter) ====
 
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.
 
 
==== tiny.clearEntities (world) ====
 
Removes all Entities from the World.
 
 
==== tiny.clearSystems (world) ====
 
Removes all Systems from the World.
 
 
==== tiny.getEntityCount (world) ====
 
Gets number of Entities in the World.
 
 
==== tiny.getSystemCount (world) ====
 
Gets number of Systems in World.
 
 
==== tiny.setSystemIndex (world, system, index) ====
 
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.
 
  
  

Latest revision as of 01:54, 22 December 2023

tiny-ecs

tiny-ecs GitHub

  • Copyright: 2016
  • License: MIT
  • Author: Calvin Rose

Gideros version

You can download the Gideros version which just adds the --!NOEXEC tag at the beginning of the file:

Media:tiny-ecs.lua (tip: right click and save link as)

Note: the file is supposed to be tiny-ecs.lua but MediaWiki puts a Capital letter :-(

In your project you then call (please change the path accordingly):

tiny = require "tiny-ecs"

Tutorial

You can find a tutorial which walks you through a demo using tiny-ecs here:

Tutorial - tiny-ecs demo a nice intro to the ECS paradigm

API

http://bakpakin.github.io/tiny-ecs/doc/


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).

tiny.world (...)

Creates a new World. Can optionally add default Systems and Entities. Returns the new World along with default Entities and Systems.

tiny.addEntity (world, entity)

Adds an Entity to the world. Also call this on Entities that have changed Components such that they match different Filters.

Returns the Entity.

tiny.addSystem (world, system)

Adds a System to the world.

Returns the System.

tiny.add (world, ...)

Shortcut for adding multiple Entities and Systems to the World.

Returns all added Entities and Systems.

tiny.removeEntity (world, entity)

Removes an Entity from the World.

Returns the Entity.

tiny.removeSystem (world, system)

Removes a System from the world.

Returns the System.

tiny.remove (world, ...)

Shortcut for removing multiple Entities and Systems from the World.

Returns all removed Systems and Entities.

tiny.refresh (world)

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.

tiny.update (world, dt, filter)

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.

tiny.clearEntities (world)

Removes all Entities from the World.

tiny.clearSystems (world)

Removes all Systems from the World.

tiny.getEntityCount (world)

Gets number of Entities in the World.

tiny.getSystemCount (world)

Gets number of Systems in World.

tiny.setSystemIndex (world, system, index)

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.



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 of 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).

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)

tiny.system (table)

Creates a new System or System class from the supplied table. If table is nil, creates a new table.

tiny.processingSystem (table)

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 custom update callback for Processing Systems.

tiny.sortedSystem (table)

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).

tiny.sortedProcessingSystem (table)

Creates a new Sorted Processing System or Sorted Processing System class.

Sorted Processing Systems have both the aspects of Processing Systems and Sorted Systems.



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"))

tiny.requireAll (...)

Makes a Filter that selects Entities with all specified Components and Filters.

tiny.requireAny (...)

Makes a Filter that selects Entities with at least one of the specified Components and Filters.

tiny.rejectAll (...)

Makes a Filter that rejects Entities with all specified Components and Filters, and selects all other Entities.

tiny.rejectAny (...)

Makes a Filter that rejects Entities with at least one of the specified Components and Filters, and selects all other Entities.

tiny.filter (pattern)

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'


Note: 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



ECS