mirror of
https://github.com/Reuh/ubiquitousse.git
synced 2025-10-27 17:19:31 +00:00
Add doc for ecs, asset
This commit is contained in:
parent
31472b2a85
commit
d4e2a1d94a
14 changed files with 2938 additions and 592 deletions
258
ecs/ecs.can
258
ecs/ecs.can
|
|
@ -1,19 +1,43 @@
|
|||
--- ubiquitousse.ecs
|
||||
-- Optional dependency: ubiquitousse.scene, to allow quick creation of ECS-based scenes.
|
||||
local loaded, scene = pcall(require, (...):match("^(.-)ecs").."scene")
|
||||
if not loaded then scene = nil end
|
||||
|
||||
--- Entity Component System library, inspired by the excellent tiny-ecs. Main differences include:
|
||||
--- ECS (entity compenent system) library
|
||||
--
|
||||
-- Entity Component System library, inspired by the excellent tiny-ecs. Main differences include:
|
||||
--
|
||||
-- * ability to nest systems;
|
||||
-- * instanciation of systems for each world (no shared state);
|
||||
-- * adding and removing entities is done instantaneously
|
||||
-- * ability to add and remove components from entities after they were added to the world.
|
||||
--
|
||||
-- No mandatory dependency.
|
||||
-- Optional dependency: `ubiquitousse.scene`, to allow quick creation of ECS-based scenes.
|
||||
--
|
||||
-- The module returns a table that contains several functions, `world` or `scene` are starting points
|
||||
-- to create your world.
|
||||
--
|
||||
-- @module ecs
|
||||
-- @usage TODO
|
||||
local loaded, scene = pcall(require, (...):match("^(.-)ecs").."scene")
|
||||
if not loaded then scene = nil end
|
||||
|
||||
let ecs
|
||||
|
||||
-- TODO: Implement a skip list for faster search.
|
||||
-- better control over system order: process, draw, methods? (for lag reasons and dependencies)
|
||||
-- more generic events?
|
||||
|
||||
-- TODO: :reorder (like refresh but update order)
|
||||
|
||||
-- TODO: clarify documentation on entity tables and instanciated system table
|
||||
|
||||
--- Entity table.
|
||||
-- TODO
|
||||
-- @section Entity
|
||||
|
||||
--- Entity system table.
|
||||
-- @doc entity
|
||||
|
||||
--- @table entity
|
||||
-- @field .. d
|
||||
|
||||
--- Recursively remove subsystems from a system.
|
||||
let recDestroySystems = (system)
|
||||
for i=#system.systems, 1, -1 do
|
||||
|
|
@ -63,106 +87,193 @@ let copy = (a, b)
|
|||
end
|
||||
|
||||
--- System fields and methods.
|
||||
--
|
||||
-- When they are added to a world, a new, per-world self table is created and used for every method call (which we call "instancied system").
|
||||
-- Instancied systems can be retrieved in system.s or system.systems.
|
||||
-- Oh, the "world" is just the top-level system, behaving in exactly the same way as other systems.
|
||||
-- Every field defined below is optional unless written otherwise.
|
||||
-- Instancied systems can be retrieved in `system.s` or `system.systems`.
|
||||
--
|
||||
-- The "world" is just the top-level system, behaving in exactly the same way as other systems.
|
||||
--
|
||||
-- Every field defined below is optional and can be accessed or redefined at any time, unless written otherwise. Though you would typically set them
|
||||
-- when creating your system.
|
||||
-- @type System
|
||||
let system_mt = {
|
||||
--- Read-only after creation system options ---
|
||||
-- I mean, you can try to change them afterwards. But, heh.
|
||||
--- Modifiable fields
|
||||
-- @doc modifiable
|
||||
|
||||
--- Name of the system.
|
||||
-- Used to create a field with the system's name in world.s and into each entity (the "entity's system table").
|
||||
-- Used to create a field with the system's name in `world.s` and into each entity (the "entity's system table") that's in this system.
|
||||
-- If not set, the entity will not have a system table.
|
||||
--
|
||||
-- Do not change after system instanciation.
|
||||
-- @ftype string
|
||||
-- @ftype nil if no name
|
||||
name = nil,
|
||||
|
||||
--- List of subsystems.
|
||||
-- On a instancied system, this is a list of the same subsystems, but instancied for this world.
|
||||
--
|
||||
-- Do not change after system instanciation.
|
||||
-- @ftype table
|
||||
-- @ftype nil if no subsystem
|
||||
systems = nil,
|
||||
|
||||
--- Modifiable system options ---
|
||||
|
||||
--- Returns true if the entity should be added to this system (and therefore its subsystems).
|
||||
-- If this is a string or a table, it will be converted to a filter function on instanciation using ecs.any.
|
||||
-- If this true, will accept every entity; if false, reject every entity.
|
||||
-- Will only test entities when they are added; changing this after system creation will not affect entities already in the system.
|
||||
-- By default, rejects everything.
|
||||
filter = :(e) return false end,
|
||||
--- Returns true if e1 <= e2. e1 and e2 are two entities.
|
||||
-- Used to place the entity in the sorted entity list when it is added; changing this after system creation
|
||||
-- will not change the order of entities already in the system.
|
||||
compare = :(e1, e2) return true end,
|
||||
|
||||
--- Called when adding an entity to the system. s is the entity's system table.
|
||||
onAdd = :(s) end,
|
||||
--- Called when removing an entity from the system. s is the entity's system table.
|
||||
onRemove = :(s) end,
|
||||
--- Called when the system is instancied, before any call to :onAddToWorld (including other systems in the world).
|
||||
onInstance = :() end,
|
||||
--- Called when the system is added to a world.
|
||||
onAddToWorld = :(world) end,
|
||||
--- Called when the system is removed from a world (i.e., the world is destroyed).
|
||||
onRemoveFromWorld = :(world) end,
|
||||
--- Called when the world is destroyed, after every call to :onRemoveFromWorld (including other systems in the world).
|
||||
onDestroy = :() end,
|
||||
--- Called when updating the system.
|
||||
onUpdate = :(dt) end,
|
||||
--- Called when drawing the system.
|
||||
onDraw = :() end,
|
||||
--- Called when updating the system, for every entity the system contains. Called after :onUpdate was called on the system. s is the entity's system table.
|
||||
process = :(s, dt) end,
|
||||
--- Called when drawing the system, for every entity the system contains. Called after :onDraw was called on the system. s is the entity's system table.
|
||||
render = :(s) end,
|
||||
|
||||
--- If not false, the system will only update every interval seconds.
|
||||
-- `false` by default.
|
||||
-- @ftype number interval of time between each update
|
||||
-- @ftype false to disable
|
||||
interval = false,
|
||||
--- The system and its susbsystems will only update if this is true.
|
||||
-- `true` by default.
|
||||
-- @ftype boolean
|
||||
active = true,
|
||||
--- The system and its subsystems will only draw if this is true.
|
||||
-- `true` by default.
|
||||
-- @ftype boolean
|
||||
visible = true,
|
||||
|
||||
--- Defaults value to put into the entities's system table when they are added. Will recursively fill missing values.
|
||||
-- When an entity is added to a system, a .entity field is always set in the system table, referring to the full entity table.
|
||||
--
|
||||
-- When an entity is added to a system, a `.entity` field is always set in the system table, referring to the full entity table.
|
||||
--
|
||||
-- Changing this will not affect entities already in the system.
|
||||
-- @ftype table
|
||||
-- @ftype nil if no default
|
||||
default = nil,
|
||||
--- Defaults methods to assign to the entities's system table when they are added.
|
||||
-- When calling the methods with entity.systemName:method(...), the method will actually receive the
|
||||
-- arguments method(system, system table, ...). Methamethods are accepted. New methods can be
|
||||
--
|
||||
-- When calling the methods with `entity.systemName:method(...)`, the method will actually receive the
|
||||
-- arguments method(system, `system table, ...)`. Methamethods are accepted. New methods can be
|
||||
-- created anytime.
|
||||
-- @ftype table
|
||||
-- @ftype nil if no methods
|
||||
methods = nil,
|
||||
|
||||
--- Read-only system options ---
|
||||
--- Callbacks.
|
||||
--
|
||||
-- Functions that are called when something happens in the system.
|
||||
-- Redefine them to change system behaviour.
|
||||
-- @doc callbacks
|
||||
|
||||
--- Called when checking if an entity should be added to this system.
|
||||
-- Returns true if the entity should be added to this system (and therefore its subsystems).
|
||||
--
|
||||
-- If this is a string or a table, it will be converted to a filter function on instanciation using ecs.any.
|
||||
--
|
||||
-- If this true, will accept every entity; if false, reject every entity.
|
||||
--
|
||||
-- Will only test entities when they are added; changing this after system creation will not affect entities already in the system.
|
||||
--
|
||||
-- By default, rejects everything.
|
||||
-- @callback
|
||||
-- @tparam table e entity table to check
|
||||
-- @treturn boolean true if entity should be added
|
||||
filter = :(e) return false end,
|
||||
--- Called when adding an entity to this system determining its order.
|
||||
-- Returns true if e1 <= e2. e1 and e2 are two entities.
|
||||
--
|
||||
-- Used to place the entity in the sorted entity list when it is added; changing this after system creation
|
||||
-- will not change the order of entities already in the system.
|
||||
--
|
||||
-- By default, entities are in the same order they were inserted.
|
||||
-- @callback
|
||||
-- @tparam table e1 entity table to check for inferiority
|
||||
-- @tparam table e2 entity table to check for superiority
|
||||
-- @treturn boolean true if e1 <= e2
|
||||
compare = :(e1, e2) return true end,
|
||||
|
||||
--- Called when adding an entity to the system.
|
||||
-- @callback
|
||||
-- @tparam table s the entity's system table
|
||||
onAdd = :(s) end,
|
||||
--- Called when removing an entity from the system.
|
||||
-- @callback
|
||||
-- @tparam table s the entity's system table
|
||||
onRemove = :(s) end,
|
||||
--- Called when the system is instancied, before any call to `System:onAddToWorld` (including other systems in the world).
|
||||
-- @callback
|
||||
onInstance = :() end,
|
||||
--- Called when the system is added to a world.
|
||||
-- @callback
|
||||
-- @tparam System world world system
|
||||
onAddToWorld = :(world) end,
|
||||
--- Called when the system is removed from a world (i.e., the world is destroyed).
|
||||
-- @callback
|
||||
-- @tparam System world world system
|
||||
onRemoveFromWorld = :(world) end,
|
||||
--- Called when the world is destroyed, after every call to `System:onRemoveFromWorld` (including other systems in the world).
|
||||
-- @callback
|
||||
onDestroy = :() end,
|
||||
--- Called when updating the system.
|
||||
-- @callback
|
||||
-- @number dt delta-time since last update
|
||||
onUpdate = :(dt) end,
|
||||
--- Called when drawing the system.
|
||||
-- @callback
|
||||
onDraw = :() end,
|
||||
--- Called when updating the system, for every entity the system contains. Called after `System:onUpdate` was called on the system.
|
||||
-- @callback
|
||||
-- @tparam table s the entity's system table
|
||||
-- @number dt delta-time since last update
|
||||
process = :(s, dt) end,
|
||||
--- Called when drawing the system, for every entity the system contains. Called after `System:onDraw` was called on the system.
|
||||
-- @callback
|
||||
-- @tparam table s the entity's system table
|
||||
render = :(s) end,
|
||||
|
||||
--- Read-only fields
|
||||
-- @doc ro
|
||||
|
||||
--- The world the system belongs to.
|
||||
-- @ftype System world
|
||||
-- @ro
|
||||
world = nil,
|
||||
--- Number of entities in the system.
|
||||
-- @ftype integer
|
||||
-- @ro
|
||||
entityCount = 0,
|
||||
--- Map of named systems in the world (not only subsystems). Same for every system from the same world.
|
||||
--- Map of all named systems in the world (not only subsystems). Same for every system from the same world.
|
||||
-- @ftype table {[system.name]=instanciedSystem, ...}
|
||||
-- @ro
|
||||
s = nil,
|
||||
|
||||
--- Private fields ---
|
||||
|
||||
--- First element of the linked list of entities: { entity, next_element }.
|
||||
-- @local
|
||||
_first = nil,
|
||||
--- Associative map of entities in the system and their previous linked list element (or true if first element).
|
||||
-- This make the list effectively a doubly linked list, but with easy access to the previous element using this map (and therefore O(1) deletion).
|
||||
-- @local
|
||||
_previous = nil,
|
||||
--- Amount of time waited since last update (if interval is set).
|
||||
-- @local
|
||||
_waited = 0,
|
||||
--- Metatable of entities' system table.
|
||||
-- Contains the methods defined in the methods field, wrapped to be called with the correct arguments, as well as a
|
||||
-- __index field (if not redefined in methods).
|
||||
-- @local
|
||||
_methods_mt = nil,
|
||||
|
||||
--- Methods ---
|
||||
--- Methods.
|
||||
--
|
||||
-- Methods available on instancied system table.
|
||||
-- @doc smethods
|
||||
|
||||
--- Add entities to the system and its subsystems.
|
||||
--
|
||||
-- Will skip entities that are already in the system.
|
||||
--
|
||||
-- Entities are added to subsystems after they were succesfully added to their parent system.
|
||||
--
|
||||
-- If this is called on a subsystem instead of the world, be warned that this will bypass all the parent's systems filters.
|
||||
-- Since :remove will not search for entities in systems where they should have been filtered out, the added entities will not be removed
|
||||
-- when calling :remove on a parent system or the world. The entity can be removed by calling :remove on the system :add was called on.
|
||||
--
|
||||
-- Since `System:remove` will not search for entities in systems where they should have been filtered out, the added entities will not be removed
|
||||
-- when calling :remove on a parent system or the world. The entity can be removed by calling `System:remove` on the system `System:add` was called on.
|
||||
--
|
||||
-- Complexity: O(1) per unordered system, O(entityCount) per ordered system.
|
||||
-- @tparam table e entity to add
|
||||
-- @tparam table... ... other entities to add
|
||||
-- @treturn e,... the function arguments
|
||||
add = :(e, ...)
|
||||
if e ~= nil and not @_previous[e] and @filter(e) then
|
||||
-- setup entity
|
||||
|
|
@ -213,9 +324,14 @@ let system_mt = {
|
|||
end
|
||||
end,
|
||||
--- Refresh an entity's systems.
|
||||
-- Behave similarly to :add, but if the entity is already in the system, instead of skipping it, it
|
||||
--
|
||||
-- Behave similarly to `System:add`, but if the entity is already in the system, instead of skipping it, it
|
||||
-- will check for new and removed components and add and remove from (sub)systems accordingly.
|
||||
--
|
||||
-- Complexity: O(1) per system + add/remove complexity.
|
||||
-- @tparam table e entity to refresh
|
||||
-- @tparam table... ... other entities to refresh
|
||||
-- @treturn e,... the function arguments
|
||||
refresh = :(e, ...)
|
||||
if e ~= nil then
|
||||
if not @_previous[e] then
|
||||
|
|
@ -237,11 +353,19 @@ let system_mt = {
|
|||
end
|
||||
end,
|
||||
--- Remove entities to the system and its subsystems.
|
||||
--
|
||||
-- Will skip entities that are not in the system.
|
||||
--
|
||||
-- Entities are removed from subsystems before they are removed from their parent system.
|
||||
-- If you intend to call this on a subsystem instead of the world, please read the warning in :add.
|
||||
--
|
||||
-- If you intend to call this on a subsystem instead of the world, please read the warning in `System:add`.
|
||||
--
|
||||
-- Returns all removed entities.
|
||||
--
|
||||
-- Complexity: O(1) per system.
|
||||
-- @tparam table e entity to remove
|
||||
-- @tparam table... ... other entities to remove
|
||||
-- @treturn e,... the function arguments
|
||||
remove = :(e, ...)
|
||||
if e ~= nil then
|
||||
if @_previous[e] then
|
||||
|
|
@ -277,7 +401,11 @@ let system_mt = {
|
|||
end
|
||||
end,
|
||||
--- Returns true if every entity is in the system.
|
||||
--
|
||||
-- Complexity: O(1).
|
||||
-- @tparam table e entity that may be in the system
|
||||
-- @tparam table... ... other entities that may be in the system
|
||||
-- @treturn boolean true if every entity is in the system
|
||||
has = :(e, ...)
|
||||
let has = e == nil or not not @_previous[e]
|
||||
if ... then
|
||||
|
|
@ -287,6 +415,7 @@ let system_mt = {
|
|||
end
|
||||
end,
|
||||
--- Returns an iterator that iterate through the entties in this system.
|
||||
-- @treturn iterator iterator over the entities in this system, in order
|
||||
iter = :()
|
||||
return nextEntity, { @_first }
|
||||
end,
|
||||
|
|
@ -300,7 +429,9 @@ let system_mt = {
|
|||
end
|
||||
end,
|
||||
--- Try to update the system and its subsystems. Should be called on every game update.
|
||||
--
|
||||
-- Subsystems are updated after their parent system.
|
||||
-- @number dt delta-time since last update
|
||||
update = :(dt)
|
||||
if @active then
|
||||
if @interval then
|
||||
|
|
@ -324,6 +455,7 @@ let system_mt = {
|
|||
end
|
||||
end,
|
||||
--- Try to draw the system and its subsystems. Should be called on every game draw.
|
||||
--
|
||||
-- Subsystems are drawn after their parent system.
|
||||
draw = :()
|
||||
if @visible then
|
||||
|
|
@ -419,9 +551,12 @@ let recCallOnAddToWorld = (world, systems)
|
|||
end
|
||||
|
||||
--- ECS module.
|
||||
-- @section end
|
||||
ecs = {
|
||||
--- Create and returns a world system based on a list of systems.
|
||||
-- The systems will be instancied for this world.
|
||||
-- @tparam table,... ... list of (uninstancied) system tables
|
||||
-- @treturn System the world system
|
||||
world = (...)
|
||||
let world = setmetatable({
|
||||
filter = ecs.all(),
|
||||
|
|
@ -435,6 +570,8 @@ ecs = {
|
|||
end,
|
||||
|
||||
--- Returns a filter that returns true if, for every argument, a field with the same name exists in the entity.
|
||||
-- @tparam string,... ... list of field names that must be in entity
|
||||
-- @treturn function(e) that returns true if e has all the fields
|
||||
all = (...)
|
||||
if ... then
|
||||
let l = {...}
|
||||
|
|
@ -452,6 +589,8 @@ ecs = {
|
|||
end,
|
||||
|
||||
--- Returns a filter that returns true if one of the arguments if the name of a field in the entity.
|
||||
-- @tparam string,... ... list of field names that may be in entity
|
||||
-- @treturn function(e) that returns true if e has at leats one of the fields
|
||||
any = (...)
|
||||
if ... then
|
||||
let l = {...}
|
||||
|
|
@ -468,7 +607,12 @@ ecs = {
|
|||
end
|
||||
end,
|
||||
|
||||
--- If uqt.scene is available, returns a new scene that will consist of a ECS world with the specified systems and entities.
|
||||
--- If `uqt.scene` is available, returns a new scene that will consist of a ECS world with the specified systems and entities.
|
||||
-- @require ubiquitousse.scene
|
||||
-- @string name the name of the new scene
|
||||
-- @tparam[opt={}] table systems list of systems to add to the world
|
||||
-- @tparam[opt={}] table entities list of entities to add to the world
|
||||
-- @treturn scene the new scene
|
||||
scene = (name, systems={}, entities={})
|
||||
assert(scene, "ubiquitousse.scene unavailable")
|
||||
let s = scene.new(name)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue