mirror of
https://github.com/Keyslam-Group/Concord.git
synced 2025-09-02 12:24:11 -04:00
First version of documentation
This commit is contained in:
parent
0e1023d2ce
commit
424f27ed8a
1 changed files with 265 additions and 23 deletions
288
README.md
288
README.md
|
@ -1,39 +1,281 @@
|
||||||
# Concord
|
# Concord
|
||||||
|
|
||||||
Concord is a feature complete ECS.
|
Concord is a feature complete ECS for LÖVE.
|
||||||
It's main focus is on speed and usage. You should be able to quickly write code that performs well.
|
It's main focus is performance and ease of use.
|
||||||
|
With Concord it is possibile to easily write fast and clean code.
|
||||||
|
|
||||||
Documentation for Concord can be found in the [Wiki tab](https://github.com/Tjakka5/Concord/wiki).
|
This readme will explain how to use Concord.
|
||||||
|
|
||||||
Auto generated docs for Concord can be found in the [Github page](https://tjakka5.github.io/Concord/). These are still work in progress and might be incomplete though.
|
Additionally all of Concord is documented using the LDoc format.
|
||||||
|
Auto generated docs for Concord can be found in `docs` folder, or on the [Github page](https://tjakka5.github.io/Concord/).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Download the repository and drop it in your project, then simply require it as:
|
Download the repository and copy the 'src' folder in your project. Rename it to something that makes sense (Probably 'concord'), then require it in your project like so:
|
||||||
```lua
|
```lua
|
||||||
local Concord = require(PathToConcord).init()
|
local Concord = require("path.to.concord")
|
||||||
|
|
||||||
You will only need to call .init once when you first require it.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Modules
|
Concord has a bunch of modules. These can be accessed through Concord:
|
||||||
Below is a list of modules.
|
|
||||||
More information about what each done can be found in the Wiki
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local Concord = require("concord")
|
-- Modules
|
||||||
local Entity = require("concord.entity")
|
local Entity = Concord.entity
|
||||||
local Component = require("concord.component")
|
local Component = Concord.component
|
||||||
local System = require("concord.system")
|
local System = Concord.system
|
||||||
local Instance = require("concord.instance")
|
local World = Concord.world
|
||||||
|
local Assemblage = Concord.assemblage
|
||||||
|
|
||||||
|
-- Containers
|
||||||
|
local Components = Concord.components
|
||||||
|
local Systems = Concord.systems
|
||||||
|
local Worlds = Concord.worlds
|
||||||
|
local Assemblages = Concord.assemblages
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## ECS
|
||||||
|
Concord is an Entity Component System (ECS for short) library.
|
||||||
|
This is a coding paradigm where _composition_ is used over _inheritance_.
|
||||||
|
Because of this it is easier to write more modular code. It often allowes you to combine any form of behaviour for the objects in your game (Entities).
|
||||||
|
|
||||||
|
As the name might suggest, ECS consists of 3 core things: Entities, Components, and Systems. A proper understanding of these is required to use Concord effectively.
|
||||||
|
We'll start with the simplest one.
|
||||||
|
|
||||||
|
### Components
|
||||||
|
Components are pure raw data. In Concord this is just a table with some fields.
|
||||||
|
A position component might look like
|
||||||
|
`{ x = 100, y = 50}`, whereas a health Component might look like `{ currentHealth = 10, maxHealth = 100 }`.
|
||||||
|
What is most important is that Components are data and nothing more. They have 0 functionality.
|
||||||
|
|
||||||
|
### Entities
|
||||||
|
Entities are the actual objects in your game. Like a player, an enemy, a crate, or a bullet.
|
||||||
|
Every Entity has it's own set of Components, with their own values.
|
||||||
|
|
||||||
|
A crate might have the following components:
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
position = { x = 100, y = 200 },
|
||||||
|
texture = { path = "crate.png", image = Image },
|
||||||
|
pushable = { },
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Whereas a player might have the following components:
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
position = { x = 200, y = 300 },
|
||||||
|
texture = { path = "player.png", image = Image },
|
||||||
|
controllable = { keys = "wasd" },
|
||||||
|
health = { currentHealth = 10, maxHealth = 100},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Any Component can be given to any Entity (once). Which Components an Entity has will determine how it behaves. This is done through the last thing...
|
||||||
|
|
||||||
|
### Systems
|
||||||
|
Systems are the things that actually _do_ stuff. They contain all your fancy algorithms and cool game logic.
|
||||||
|
Each System will do one specific task like say, drawing Entities.
|
||||||
|
For this they will only act on Entities that have the Components needed for this: `position` and `texture`. All other Components are irrelevant.
|
||||||
|
|
||||||
|
In Concord this is done something alike this:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
drawSystem = System({position, texture}) -- Define a System that takes all Entities with a position and texture Component
|
||||||
|
|
||||||
|
function drawSystem:draw() -- Give it a draw function
|
||||||
|
for _, entity in ipairs(self.pool) do -- Iterate over all Entities that this System acts on
|
||||||
|
local position = entity[position] -- Get the position Component of this Entity
|
||||||
|
local texture = entity[texture] -- Get the texture Component of this Entity
|
||||||
|
|
||||||
|
-- Draw the Entity
|
||||||
|
love.graphics.draw(texture.image, position.x, position.y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### To summarize...
|
||||||
|
- Components contain only data.
|
||||||
|
- Entities contain any set of Components.
|
||||||
|
- Systems act on Entities that have a required set of Components.
|
||||||
|
|
||||||
|
By creating Components and Systems you create modular behaviour that can apply to any Entity.
|
||||||
|
What if we took our crate from before and gave it the `controllable` Component? It would respond to our user input of course.
|
||||||
|
|
||||||
|
Or what if the enemy shot bullets with a `health` Component? It would create bullets that we'd be able to destroy by shooting them.
|
||||||
|
|
||||||
|
And all that without writing a single extra line of code. Just reusing code that already existed and is guaranteed to be reuseable.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
### General design
|
||||||
|
|
||||||
|
#### Classes
|
||||||
|
Concord does a few things that might not be immediately clear. This segment should help understanding.
|
||||||
|
|
||||||
|
When you define a Component or System you are actually defining a `ComponentClass` and `SystemClass` respectively. From these instances of them can be created. They also act as identifiers for Concord.
|
||||||
|
|
||||||
|
For example. If you want to get a specific Component from an Entity, you'd do `Component = Entity:get(ComponentClass)`.
|
||||||
|
When ComponentClasses or SystemClasses are required it will be written clearly in the Documentation.
|
||||||
|
|
||||||
|
#### Containers
|
||||||
|
Since you'll be defining or creating lots of Components, Systems, Worlds and Assemblages Concord adds container tables for each of them so that they are easily accessible.
|
||||||
|
|
||||||
|
These containers can be accessed through
|
||||||
|
```lua
|
||||||
|
local Components = require("path.to.concord").components
|
||||||
|
local Systems = require("path.to.concord").systems
|
||||||
|
local Worlds = require("path.to.concord").worlds
|
||||||
|
local Assemblages = require("path.to.concord").aorlds
|
||||||
|
```
|
||||||
|
|
||||||
|
Concord has helper functions to fill these containers. There are the following options depending on your needs / preference:
|
||||||
|
```lua
|
||||||
|
-- Loads each file. They are put in the container according to it's filename ( 'src.components.component_1.lua' becomes 'component_1' )
|
||||||
|
Concord.loadComponents({"path.to.component_1", "path.to.component_2", "etc"})
|
||||||
|
|
||||||
|
-- Loads all files in the directory. They are put in the container according to it's filename ( 'src.components.component_1.lua' becomes 'component_1' )
|
||||||
|
Concord.loadComponents("path.to.directory.containing.components")
|
||||||
|
|
||||||
|
|
||||||
|
-- Put the ComponentClass into the container directly. Useful if you want more manual control. Note that you need to require the file in this case
|
||||||
|
Components.register("componentName", ComponentClass)
|
||||||
|
```
|
||||||
|
Things can then be accessed through their names:
|
||||||
|
```lua
|
||||||
|
local component_1_class = Components.component_1
|
||||||
|
local componentName_class = Components.componentName
|
||||||
|
```
|
||||||
|
|
||||||
|
All the above applies the same to all the other containers.
|
||||||
|
|
||||||
|
### Components
|
||||||
|
When defining a ComponentClass you usually pass in a `populate` function. This will fill the Component with values.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Define the ComponentClass.
|
||||||
|
-- The component variable is the actual Component given to an Entity
|
||||||
|
-- The x and y variables are values we pass in when we create the Component
|
||||||
|
local PositionComponentClass = Concord.component(function(component, x, y)
|
||||||
|
component.x = x or 0
|
||||||
|
component.y = y or 0
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- We can manually register the Component to the container if we want
|
||||||
|
Concord.components.register("positionComponent", PositionComponentClass)
|
||||||
|
|
||||||
|
-- Otherwise we just return the Component so it can be required
|
||||||
|
return PositionComponentClass
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also pass nothing. This will keep the Component empty.
|
||||||
|
This can be useful for Components that define a boolean state. For example `pushable` or `crouching`.
|
||||||
|
|
||||||
|
### Entities
|
||||||
|
Entities can be freely made and be given Components. You pass the ComponentClass and the values you want to pass. It will then create the Component for you.
|
||||||
|
|
||||||
|
Entities can only have a maximum of one of each Component.
|
||||||
|
Entities can not share Components.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Create a new Entity
|
||||||
|
local myEntity = Entity()
|
||||||
|
-- or
|
||||||
|
local myEntity = Entity(myWorld) -- To add it to a world immediately ( See World )
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Give the entity the position component defined above
|
||||||
|
-- x will become 100. y will become 50
|
||||||
|
myEntity:give(Concord.components.positionComponent, 100, 50)
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Retrieve a Component
|
||||||
|
local positionComponent = myEntity[Concord.components.positionComponent]
|
||||||
|
-- or
|
||||||
|
local positionComponent = myEntity:get(Concord.components.positionComponents)
|
||||||
|
|
||||||
|
print(positionComponent.x, positionComponent.y) -- 100, 50
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Remove a Component
|
||||||
|
myEntity:remove(Concord.compoennts.positionComponent)
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Check if the Entity has a Component
|
||||||
|
local hasPositionComponent = myEntity:has(Concord.components.positionComponent)
|
||||||
|
print(hasPositionComponent) -- false
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Entity:give will override a Component if the Entity already had it
|
||||||
|
-- Entity:ensure will only put the Component if the Entity does not already have it
|
||||||
|
|
||||||
|
Entity:ensure(Concord.components.positionComponents, 0, 0) -- Will give
|
||||||
|
-- Position is {x = 0, y = 0}
|
||||||
|
|
||||||
|
Entity:give(Concord.components.positionComponent, 50, 50) -- Will override
|
||||||
|
-- Position is {x = 50, y = 50}
|
||||||
|
|
||||||
|
Entity:give(Concord.components.positionComponent, 100, 100) -- Will override
|
||||||
|
-- Position is {x = 100, y = 100}
|
||||||
|
|
||||||
|
Entity:ensure(Concord.components.positionComponents, 0, 0) -- Wont do anything
|
||||||
|
-- Position is {x = 100, y = 100}
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Retrieve all Components
|
||||||
|
-- WARNING: Do not modify this table. It is read-only
|
||||||
|
local allComponents = myEntity:getComponents()
|
||||||
|
|
||||||
|
for ComponentClass, Component in ipairs(allComponents) do
|
||||||
|
-- Do stuff
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Assemble the Entity ( See Assemblages )
|
||||||
|
myEntity:assemble(myAssemblage, 100, true, "foo")
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Check if the Entity is in a world
|
||||||
|
local inWorld = myEntity:inWorld()
|
||||||
|
|
||||||
|
-- Get the World the Entity is in
|
||||||
|
local world = myEntity:getWorld()
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Destroy the Entity
|
||||||
|
myEntity:destroy()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Systems
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
### Worlds
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
### Assemblages
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
### Type
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
```
|
- __Positive07__: Constant support and a good rubberduck
|
||||||
Positive07: Constant support and a good rubberduck
|
- __Brbl__: Early testing and issue reporting
|
||||||
Brbl: Early testing and issue reporting
|
- __Josh__: Squashed a few bugs and generated docs
|
||||||
Josh: Squashed a few bugs and docs
|
- __Erasio__: I took inspiration from HooECS. He also introduced me to ECS
|
||||||
Erasio: Took inspiration from HooECS. Also introduced me to ECS.
|
- __Speak__: Lots of testing for new features of Concord
|
||||||
```
|
- __Tesselode__: Brainstorming and helpful support
|
||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
MIT Licensed - Copyright Justin van der Leij (Tjakka5)
|
MIT Licensed - Copyright Justin van der Leij (Tjakka5)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue