Skip to content

Instantly share code, notes, and snippets.

@wolf81
Last active April 18, 2024 07:23
Show Gist options
  • Save wolf81/d2fcb03c783c52ffa05a3c85be696c82 to your computer and use it in GitHub Desktop.
Save wolf81/d2fcb03c783c52ffa05a3c85be696c82 to your computer and use it in GitHub Desktop.
A simple ECS structure for Lua
--[[
-- A very basic ECS implementation, without Components clearly defined.
--
-- A component should be a class in order to properly use it with a System.
-- A component is a class if the table has a metatable describing it's type.
--
-- Suppose we have a component class Visual to show animations, we can then
-- add the Visual component to an Entity instance as follows:
--
-- local player = Entity()
-- player:addComponent(Visual())
--
-- If we now want to easily update all Visual components for all entities,
-- we can add the Visual components of all entities to a System as follows:
--
-- local visualSystem = System(Visual)
-- for _, entity in ipairs(entities) do
-- visualSystem:addComponent(entity)
-- do
--
-- ... then later we can use the visualSystem in the update(dt) method:
--
-- visualSystem:update(dt)
--
-- ... and all Visual components in the system will be updated.
--
-- Using it myself with stringly-typed classes and a factory that adds
-- Components to an Entity instance based on class type name. For example
-- an entity with type string 'player' will have an Input component added
-- to manage keyboard and mouse input. A monster will have a AI component
-- added to control movement, attacks, ...
--]]
--[[ ENTITY ]]--
Entity = {}
-- constructor
Entity.new = function(...)
local components = {}
-- add a component by type - if a component for given type is already added, will be replaced
local addComponent = function(self, component)
components[getmetatable(component)] = component
end
-- remove a component by type
local removeComponent = function(self, T)
components[T] = nil
end
-- get a component by type
local getComponent = function(self, T)
return components[T]
end
return setmetatable({
getComponent = getComponent,
removeComponent = removeComponent,
addComponent = addComponent,
}, Entity)
end
setmetatable(Entity, {
__call = function(_, ...) return Entity.new(...) end,
})
--[[ SYSTEM ]]--
System = {}
System.new = function(T)
local components = {}
local addComponent = function(self, entity)
local component = entity:getComponent(T)
if component ~= nil then
components[entity] = component
end
end
local removeComponent = function(self, entity)
components[entity] = nil
end
local update = function(self, dt, ...)
for entity, component in pairs(components) do
component:update(dt, ...)
end
end
return setmetatable({
addComponent = addComponent,
removeComponent = removeComponent,
update = update,
}, System)
end
setmetatable(System, {
__call = function(_, ...) return System.new(...) end,
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment