# Minimal entity component system

I was trying to figure out what a minimal ECS might look like in Lua; here's what I came up with. This is based very loosely on darkf/microecs.

local function createSystem(components, process)
local function handle(entity)
for i, component in ipairs(components) do
if entity[component] == nil then return end
end
process(entity)
end

return function(entities)
for i, entity in ipairs(entities) do
handle(entity)
end
end
end

local meta = { __index = component }
entity[component] = setmetatable(instance or {}, meta)
return entity
end

local function createEntity(instance)
local meta = { __index = { addComponent = addComponent } }
return setmetatable(instance or {}, meta)
end

return {
createEntity = createEntity,
createSystem = createSystem,
}


Here's how you could use it:

local ecs = require 'ecs'

local thing = ecs.createEntity()

local positionComponent = { x = 0, y = 0 }
local velocityComponent = { vx = 0, vy = 0 }

thing:addComponent(positionComponent, { x = 50, y = 60 })
thing:addComponent(velocityComponent, { vx = 5, vy = 6 })

local velocitySystem = ecs.createSystem(
{ positionComponent, velocityComponent },
function (entity)
local pos = entity[positionComponent]
local vel = entity[velocityComponent]
pos.x = pos.x + vel.vx
pos.y = pos.y + vel.vy
end)


Interestingly, the entities and components aren't nearly as important as the systems. The createEntity function only exists so that you can use addComponent in an object-oriented way, but you can just as easily write it like this:

local thing = {}
-- ...
ecs.addComponent(thing, positionComponent, { x = 50, y = 60 })
ecs.addComponent(thing, velocityComponent, { vx = 5, vy = 6 })


You can also ignore addComponent if you don't care about the "safety" of having "typed" components.

local thing = {
position = { x = 50, y = 60 },
velocity = { vx = 5, vy = 6 },
}

local velocitySystem = ecs.createSystem(
{ 'position', 'velocity' },
function (entity)
local pos = entity.position
local vel = entity.velocity
pos.x = pos.x + vel.vx
pos.y = pos.y + vel.vy
end)


This means systems are useful on arbitrary data structures, with the table representing the entity and its properties representing components. Keying properties with strings like this seems more idiomatic from a Lua standpoint.

To have a system process entities, simply call the function returned from createSystem, passing it a table of entities to process. Only the entities having the components specified in the first argument to createSystem will be processed.

function tick()
velocitySystem({ thing, anotherThing })
anotherSystem({ thing, somethingElse })
end


All comments are welcome, but I'm more interested in hearing about the implementation itself than stylistic choices (like whether I should have used _ in place of i, or 4 space indents instead of 2).