# README
Kostayne ECS v2
This package provides a basic implementation of Entity Component System pattern. ECS separates logic from data, which makes the app more scalable and flexible. It's ideal for complex games or simulations.
Definitions
Entity - a unique object in the world, it has no any logic.
Component - a piece of data that can be attached to an entity.
System - a set of logic that can be applied to the entity.
TOC
Usage
To use ECS, you need to define components and systems, then create a main loop.
Define components
First, we'll define a simple component (component.go):
package example
type PositionComponent struct {
X float64
Y float64
}
func (c *PositionComponent) Type() string {
return "position"
}
func MakePositionComponent(x, y float64) *PositionComponent {
return &PositionComponent{
X: x,
Y: y,
}
}
Define systems
Next, we'll define a simple system (system.go):
package example
import (
"time"
"github.com/kostayne/ecs/v2/core"
)
// Use SystemBase to reduce boilerplate
type MovementSystem struct {
core.SystemBase
}
// Main logic
func (s *MovementSystem) Process(es *core.EntityStore, dt time.Duration) {
finder := core.MakeFinder(es)
entities := finder.Has("position").GetMany()
for _, e := range entities {
pos := (*e.Get("position")).(*PositionComponent)
pos.X += 1
pos.Y += 2
}
}
func MakeMovementSystem() *MovementSystem {
return &MovementSystem{
// Define system params here
SystemBase: *core.MakeSystemBase("sys_movement", 0, 0),
}
}
The code above reduces boilerplate with the SystemBase struct, here is a more explicit version without it.
Start the app
Finally, we'll start the app (app.go):
package example
import (
"fmt"
"github.com/kostayne/ecs/v2/core"
)
func main() {
ecs := core.MakeECS()
// create component & system instances
moveSys := MakeMovementSystem()
posComp := MakePositionComponent(0, 0)
// add them to ecs
player := ecs.EntityStore.New(posComp)
ecs.SystemStore.Add(moveSys)
ecs.Setup()
// run the main loop
for i := 0; i < 5; i++ {
ecs.Process()
fmt.Printf("XY: (%v, %v)\n", posComp.X, posComp.Y)
}
ecs.Cleanup()
}
Wiki
To find out more, see the documentation.
ECS
ECS is a core data structure that holds all entities and their components.
type ECS struct {
EntityStore EntityStore
SystemStore SystemStore
}
EntityStore
Use entity store to manage entities.
Manage entities
entity := ecs.EntityStore.New()
ecs.EntityStore.Get(entity.Id())
ecs.EntityStore.Remove(entity.Id())
ecs.EntityStore.GetAll(entity.Id())
Manage components
ecs.EntityStore.GetById(entity.Id())
ecs.EntityStore.AddTo(entity.Id(), MakePositionComponent(0, 0))
ecs.EntityStore.RemoveFrom(entity.Id(), "position")
Finder
Finder is a helper that allows to find entities by components or arbitrary criteria.
--- Finder Interface ---
type FinderI interface {
Get() Entity
GetMany() []Entity
Has(components ...string) FinderI
Where(predicate func(Entity) bool) FinderI
}
--- Finder Constructor ---
ecs := core.MakeECS()
finder := core.MakeFinder(&ecs.SystemStore)
--- Finder Methods ---
Finder.Has(components ...string) FinderI
Returns a finder with entities that have provided components.
entities := finder.Has("position", "velocity").GetMany()
Finder.Where(predicate func(Entity) bool) FinderI
Returns a finder with entities that match provided predicate.
func isEntityOnTheRight(e *Entity) bool {
pos := (*e.GetOne("position")).(*PositionComponent)
return pos.X > 0
}
entities := finder.Where(isEntityOnTheRight).GetMany()
Finder.GetOne() *Entity
Returns a single matched entity.
player := finder.Has("character_controller").GetOne()
Finder.GetMany() []Entity
Returns a list of matched entities.
weapons := finder.Has("weapon").GetMany()