|
@@ -0,0 +1,162 @@
|
|
|
|
|
+--[[
|
|
|
|
|
+ ECS World class
|
|
|
|
|
+ @author : Eiyeron Fulmincendii
|
|
|
|
|
+
|
|
|
|
|
+ Contains entities, components and system and orchestrates them.
|
|
|
|
|
+]]--
|
|
|
|
|
+local class = require("class")
|
|
|
|
|
+local object = require("object")
|
|
|
|
|
+local SystemFilter = require("SystemFilter")
|
|
|
|
|
+local Entity = require("Entity")
|
|
|
|
|
+local utils = require("utils")
|
|
|
|
|
+
|
|
|
|
|
+ECSWorld = class(object)
|
|
|
|
|
+
|
|
|
|
|
+function ECSWorld:init( )
|
|
|
|
|
+ self.entities = {}
|
|
|
|
|
+ self.components = {}
|
|
|
|
|
+ self.system_filters = {}
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ Search a filter that matches the required components.
|
|
|
|
|
+]]--
|
|
|
|
|
+function ECSWorld:searchFilter(required_components)
|
|
|
|
|
+ -- TODO : inquire why it doesn't call a composant's metamethod.
|
|
|
|
|
+ local component_list = table.sort(required_components)
|
|
|
|
|
+ for i,l in ipairs(self.system_filters) do
|
|
|
|
|
+ if l:compareComponentList(required_components) then
|
|
|
|
|
+ return l
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+ return nil
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ - system : system to register
|
|
|
|
|
+ - ... : array of components to require
|
|
|
|
|
+]]--
|
|
|
|
|
+function ECSWorld:registerSystem(system_type, ...)
|
|
|
|
|
+ local required_components = {...}
|
|
|
|
|
+ local found_filter = self:searchFilter(required_components)
|
|
|
|
|
+ if found_filter then
|
|
|
|
|
+ found_filter:registerSystem(system_type)
|
|
|
|
|
+ else
|
|
|
|
|
+ local new_filter = SystemFilter:new(required_components)
|
|
|
|
|
+ self.system_filters[#self.system_filters + 1] = new_filter
|
|
|
|
|
+ self:searchAndRegisterEntitiesInNewSystemFilter(new_filter)
|
|
|
|
|
+ new_filter:registerSystem(system_type)
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ On component attachment, register the holder entity to
|
|
|
|
|
+ every filter that can handle it now.
|
|
|
|
|
+]]
|
|
|
|
|
+function ECSWorld:searchAndRegisterEntityInSystemFilter(entity)
|
|
|
|
|
+ for i,system_filter in ipairs(self.system_filters) do
|
|
|
|
|
+ if system_filter:checkEntityCompatibility(entity) and not system_filter:hasEntity(entity) then
|
|
|
|
|
+ system_filter:registerEntry(entity)
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ On SystemFilter creation, register every compatible entity.
|
|
|
|
|
+]]
|
|
|
|
|
+function ECSWorld:searchAndRegisterEntitiesInNewSystemFilter(new_filter)
|
|
|
|
|
+ for i,entity in ipairs(self.entities) do
|
|
|
|
|
+ if new_filter:checkEntityCompatibility(entity) and not new_filter:hasEntity(entity) then
|
|
|
|
|
+ new_filter:registerEntry(entity)
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ On Component detachment, prune the filters from the now-incompatible holder entity.
|
|
|
|
|
+]]--
|
|
|
|
|
+function ECSWorld:searchAndPruneEntityFromSystemFilter(entity)
|
|
|
|
|
+ for i,system_filter in ipairs(self.system_filters) do
|
|
|
|
|
+ if system_filter:hasEntity(entity) and not system_filter:checkEntityCompatibility(entity) then
|
|
|
|
|
+ system_filter:unregisterEntry(entity)
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ - Copies the entity into the ECSWorld's entities
|
|
|
|
|
+ - Registers it in every SystemFilter already registered
|
|
|
|
|
+]]
|
|
|
|
|
+function ECSWorld:createEntity()
|
|
|
|
|
+ local index = #self.entities + 1
|
|
|
|
|
+ for i,entity_entry in ipairs(self.entities) do
|
|
|
|
|
+ if entity_entry.__destroyed then
|
|
|
|
|
+ index = i
|
|
|
|
|
+ break
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+ local e = Entity:new(index, self)
|
|
|
|
|
+ self.entities[index] = e
|
|
|
|
|
+ return e
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ Called by World itself to mark an entity as destroyed (to override it later if needed)
|
|
|
|
|
+]]--
|
|
|
|
|
+function ECSWorld:deleteEntity(entity)
|
|
|
|
|
+ self:searchAndUnregisterEntitiesInSystemFilter(entity)
|
|
|
|
|
+ entity.__destroyed = true
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ Called by Systems via their Entity. Creates and attach a component to the holder
|
|
|
|
|
+ entity.
|
|
|
|
|
+]]--
|
|
|
|
|
+function ECSWorld:attachComponentToEntity(entity, component_type, ...)
|
|
|
|
|
+ -- Create on the fly the component list if the world doesn't have it for this component class.
|
|
|
|
|
+ if not self.components[component_type] then self.components[component_type] = {} end
|
|
|
|
|
+ self.components[component_type][entity.__eid] = component_type:new(...)
|
|
|
|
|
+ self:searchAndRegisterEntityInSystemFilter(entity)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ Called by Systems via their Entity. Deletes and detaches the component from this entity.
|
|
|
|
|
+]]--
|
|
|
|
|
+function ECSWorld:detachComponentFromEntity(entity, component_type)
|
|
|
|
|
+ -- TODO : better way to remove component
|
|
|
|
|
+ self.components[component_type][entity.__eid] = nil
|
|
|
|
|
+ self:searchAndPruneEntityFromSystemFilter(entity)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ Called by Systems via their Entity.
|
|
|
|
|
+]]
|
|
|
|
|
+function ECSWorld:entityGetComponent(entity, component_type)
|
|
|
|
|
+ if not self.components[component_type] then return nil end
|
|
|
|
|
+ return self.components[component_type][entity.__eid]
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ Called by Systems via their Entity.
|
|
|
|
|
+]]
|
|
|
|
|
+function ECSWorld:entityHasComponent(entity, component_type)
|
|
|
|
|
+ return self:entityGetComponent(entity, component_type) ~= nil
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+--[[
|
|
|
|
|
+ Supposed to be called by the world owner. Calls every system and prunes marked for deletion entities
|
|
|
|
|
+]]
|
|
|
|
|
+function ECSWorld:update(dt)
|
|
|
|
|
+ for i,system_filter in ipairs(self.system_filters) do
|
|
|
|
|
+ system_filter:update(dt, self.entities)
|
|
|
|
|
+ end
|
|
|
|
|
+ -- Entity release. Done post-update.
|
|
|
|
|
+ for i,entity in ipairs(self.entities) do
|
|
|
|
|
+ if entity.__destroy_required == true then
|
|
|
|
|
+ self:deleteEntity(entity)
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+return ECSWorld
|