Concord
Concord copied to clipboard
Check that all docs and the README are up to date
There have been several changes since the new version was started and there are some issues and typos to fix in the docs.
Some docs haven't been generated properly too so that needs to be checked.
We can then focus on improving the docs.
Started writing a new README:
Concord
Concord is a Lua library that allows you to structure your code around the Entity-Component-System model
Concord focuses on ease-of-use and developer experience, while trying to be competitive in performance against other alternatives.
Installation
Download the repository and copy the 'concord' folder into your project. Then require it in your project like so:
local Concord = require("path.to.concord")
Entity-Component-System
ECS or Entity-Component-System is a different way to structure your code, but how?
Well in the ECS model we have three core concepts as you may have already guessed, Entity, Components and Systems, but Concord adds a few more to this list, so lets start with our World:
World
The World is an enclosed environment where Entities live.
-- Initialize the World
local World = Concord.world()
For now this is enough lets now define what en Entity is.
Entity
An Entity is just that, an object or individual that exists in the World we are trying to model. This Entity holds no data, and has no behaviours of its own.
-- Create a new Entity in our World
local entity = World:newEntity()
Like this they are not very useful, we want to give them data that makes them unique (a cat and a plane should have different behaviours, and different properties), so in order to do this, Entities behave like a box, they themselves don't do much, but inside of them we can store Components.
Components
Components are labels, or tokens, that tells us about a specific aspect of our Entity, and hold relevant data of said aspect. They themselves don't have any behaviour, they are just properties, patterns, qualities, of our Entity.
-- Define a "position" component
Concord.component("position", function (self, x, y)
-- Initialize the data when the component gets added to an Entity
self.x = x
self.y = y
end)
So we have our Entity, we can now give it some labels to it in the form of Components.
-- Create an give an instance of our "position" component with x=10, y=40
entity:give("position", 10, 40)
But we haven't defined how this Entity behaves, that's where Systems come in
Events
Oh did you expect me to tell you about Systems? Well first let's define yet another new term.
We defined our World as an enclosed environment but that's not entirely true, we want to interact with it. Our World receives a bunch of external (and internal) stimulus which we call Events.
-- Emit an "spawn" event with 10 as argument
World:emit("spawn", 10)
If we want to make our World dynamic we want to make our Entities act based on those events, now THIS is where Systems come in.
Systems
A System is the way we can add different behaviours to our World, so that it can react to all the different Events we care about.
local SpawnSystem = System()
function SpawnSystem:spawn (quantity)
-- Do something when "spawn" is emitted
end
Systems are able to modify the current state of the World, that is, add/remove/edit Entities and their Components.
function SpawnSystem:spawn (quantity)
for i=1, quantity, 1 do
self:getWorld()
:newEntity()
:give("position", math.random(0, 1000), math.random(0, 1000))
end
end
Pools and Filters
The behaviour of a System may apply to none, one or many Entities, but they do not belong to a specific Entity, instead they filter through all the Entities in the World, based on the Components they have, and define a apply their effect to a subset of all the Entities.
This subset is what we call a Pool, each Pool has a specific Filter associated with it that tells the World what Components should the Entities in the Pool have and which ones they shouldn't have.
-- Define a filter called "renderable" so that we only grab entities with a "position" component
local RenderSystem = System({ renderable = { "position" } })
function RenderSystem:draw ()
-- Then we can access the "renderable" pool and loop through it
for _, entity in ipairs(self.renderable) do
-- All entities inside the pool should match the filter we defined
circle(entity.position.x, entity.position.y, 10)
end
end
Assemblages
Okey this is an extra, but sometimes we end up doing the same setup to create a bunch of Entities with the same set of Components when we could instead have a factory that creates these Entities.
To make this factory we could define a function that applies a bunch of Components to a given Entity. In Concord we call these methods Assemblages, and you will find them quite useful.
-- We define our assemblage, first parameter should always be the Entity
local randomPosition = function (entity, min, max)
entity:give("position", math.random(min, max), math.random(min, max)
-- We could keep on giving, removing or updating components
end
for i=1, 1000, 1 do
local e = World:newEntity()
-- We assemble our entities with our previously defined assemblage
e:assemble(randomPosition, i, 1000)
end
Conclusion
Okey so Pools, Filters, Events, Assemblages and World weren't part of the name, but they are still very relevant. These concepts are not always present in ECS but they are an integral part of Concord and will help you work with and understand Entities, Components and Systems.