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