Documentation
¶
Overview ¶
Package ecs provides ECS (Entity Component System) implementation.
The package provides effiecient way for systems to subscribe to relevant component combinations for entities and run update loops on only those entities.
Typesafety ¶
ECS package provides typesafe GetComponent, AddComponent and RemoveComponent functions with golang generics. These should work with any user provided types as long as they are first registered.
See GetComponent, AddComponent and RemoveComponent examples for more information.
Changes during updates ¶
Additions and removals of components and entities are done lazily. This also provides ecs system more performant way to handle deletions and do cleanup on larger batches.
First one is to provide consistent view of entities' components during update cycle of multiple systems. All RemoveComponent, AddComponent and RemoveEntity calls will have their effect visible only on the next update cycle. Changes to the component values themselves are always immediately visble to the other systems.
Please see RegisterSystem (UpdateBarrier) example.
Index ¶
- Constants
- func AddComponent[T any](w *World, e EntityID, c T)
- func DebugComponent[T any](w *World, e EntityID) string
- func GetComponent[T any](w *World, e EntityID) *T
- func GetSingleton[T any](w *World) *T
- func HasComponent[T any](w *World, e EntityID) bool
- func MustGetComponent[T any](w *World, e EntityID) *T
- func RegisterComponent[T any](w *World)
- func RegisterInitSystem(w *World, s InitSystem)
- func RegisterSingleton[T any](w *World, singleton *T)
- func RegisterSystem[T SystemType](w *World, s T, sig Signature)
- func RemoveComponent[T any](w *World, e EntityID)
- func RemoveEntity(w *World, e EntityID)
- func TypeID[T any](v T) uint64
- type EntityID
- type InitSystem
- type Signature
- type SystemType
- type UpdateState
- type WindowSettings
- type World
Examples ¶
Constants ¶
const ( Delete oplogKind = iota Add )
Variables ¶
This section is empty.
Functions ¶
func AddComponent ¶
AddComponent adds component of type T to the Entity.
Calling this on non-existent entity or on entity that already has the same component will panic.
Example ¶
package main
import (
"fmt"
"github.com/MatiasLyyra/mengine/ecs"
)
type Player struct {
X, Y int
}
func main() {
w := ecs.New()
ecs.RegisterComponent[Player](w)
player := ecs.NewEntity(w)
ecs.AddComponent(w, player, Player{X: 200, Y: 400})
// Note: Init must be called before added components can be queries
w.Init()
fmt.Printf("Player: %v\n", ecs.DebugComponent[Player](w, player))
}
Output: Player: {X:200 Y:400}
func DebugComponent ¶
DebugComponent returns debug print of component T on entity.
func GetComponent ¶
GetComponent returns component of type T attached to the entity.
If the entity does not have the component, GetComponent will return nil.
Example ¶
package main
import (
"fmt"
"github.com/MatiasLyyra/mengine/ecs"
)
type Player struct {
X, Y int
}
type Inventory struct {
Food int
}
func main() {
w := ecs.New()
ecs.RegisterComponent[Player](w)
ecs.RegisterComponent[Inventory](w)
player := ecs.NewEntity(w)
ecs.AddComponent(w, player, Player{X: 200, Y: 400})
w.Init()
playerComponent := ecs.GetComponent[Player](w, player)
playerComponent.X += 20
playerComponent.Y /= 2
fmt.Printf("Player: %v\n", ecs.DebugComponent[Player](w, player))
}
Output: Player: {X:220 Y:200}
func GetSingleton ¶
GetSingleton returns previously registered singleton value.
Calling this on type T that has not been registered, will panic.
func MustGetComponent ¶
MustGetComponent will return non-nil component of type T attached to the entity.
Call to this will panic, if the the component does not exist on the entity.
func RegisterComponent ¶
RegisterComponent registers new component of type T.
This needs to be called before component can be used.
func RegisterInitSystem ¶
func RegisterInitSystem(w *World, s InitSystem)
RegisterInitSystem register new InitSystem to the ecs world.
func RegisterSingleton ¶
RegisterSingleton registers singleton value to the ecs world.
Calling this multiple times with same T will overwrite the previous value.
func RegisterSystem ¶
func RegisterSystem[T SystemType](w *World, s T, sig Signature)
RegisterSystem registers new Update system the ecs world.
Example ¶
package main
import (
"fmt"
"github.com/MatiasLyyra/mengine/ecs"
)
type Player struct {
X, Y int
}
type Inventory struct {
Food int
}
type PlayerUpdateSystem struct{}
func (PlayerUpdateSystem) Update(us ecs.UpdateState) {
for _, e := range us.Entities {
player := ecs.GetComponent[Player](us.World, e)
inventory := ecs.GetComponent[Inventory](us.World, e)
fmt.Printf("Player: %+v\n", player)
fmt.Printf("Inventory: %+v\n", inventory)
}
}
func main() {
w := ecs.New()
ecs.RegisterComponent[Player](w)
ecs.RegisterComponent[Inventory](w)
// player1 entity will not get printed, as it lacks the necessary Inventory component for the system
player1 := ecs.NewEntity(w)
ecs.AddComponent(w, player1, Player{X: 200, Y: 400})
player2 := ecs.NewEntity(w)
ecs.AddComponent(w, player2, Player{X: 300, Y: 500})
ecs.AddComponent(w, player2, Inventory{Food: 6})
ecs.RegisterSystem(w, PlayerUpdateSystem{}, ecs.Sig[Player](w)|ecs.Sig[Inventory](w))
w.Init()
w.RunUpdate(0)
}
Output: Player: &{X:300 Y:500} Inventory: &{Food:6}
Example (UpdateBarrier) ¶
This demonstrates more in-depth interactions with multiple systems that add and delete components during an update.
In the example, PrintPlayerSystem will only trigger on the second update cycle. This is because component changes are only applied on the next update cycle.
package main
import (
"fmt"
"github.com/MatiasLyyra/mengine/ecs"
)
type Player struct {
X, Y int
}
type Inventory struct {
Food int
}
type ModifyPlayerSystem struct{}
func (ModifyPlayerSystem) Update(us ecs.UpdateState) {
for _, e := range us.Entities {
if !ecs.HasComponent[Inventory](us.World, e) {
ecs.AddComponent(us.World, e, Inventory{Food: 10})
}
fmt.Printf("ModifyPlayerSystem.Update Player: %+v\n", ecs.GetComponent[Player](us.World, e))
fmt.Printf("ModifyPlayerSystem.Update Inventory: %+v\n", ecs.GetComponent[Inventory](us.World, e))
}
}
type PrintPlayerSystem struct{}
func (PrintPlayerSystem) Update(us ecs.UpdateState) {
for _, e := range us.Entities {
fmt.Printf("PrintPlayerSystem.Update Player: %+v\n", ecs.GetComponent[Player](us.World, e))
fmt.Printf("PrintPlayerSystem.Update Inventory: %+v\n", ecs.GetComponent[Inventory](us.World, e))
}
}
func main() {
w := ecs.New()
ecs.RegisterComponent[Player](w)
ecs.RegisterComponent[Inventory](w)
player1 := ecs.NewEntity(w)
ecs.AddComponent(w, player1, Player{X: 200, Y: 400})
ecs.RegisterSystem(w, ModifyPlayerSystem{}, ecs.Sig[Player](w))
ecs.RegisterSystem(w, PrintPlayerSystem{}, ecs.Sig[Player](w)|ecs.Sig[Inventory](w))
w.Init()
// First update cycle
w.RunUpdate(0)
// Second update cycle
w.RunUpdate(0)
}
Output: ModifyPlayerSystem.Update Player: &{X:200 Y:400} ModifyPlayerSystem.Update Inventory: <nil> ModifyPlayerSystem.Update Player: &{X:200 Y:400} ModifyPlayerSystem.Update Inventory: &{Food:10} PrintPlayerSystem.Update Player: &{X:200 Y:400} PrintPlayerSystem.Update Inventory: &{Food:10}
func RemoveComponent ¶
RemoveComponent removes component of type T from the Entity.
Calling this on non-existent entity or on entity that does not have the component will panic.
Example ¶
package main
import (
"fmt"
"github.com/MatiasLyyra/mengine/ecs"
)
type Player struct {
X, Y int
}
type Inventory struct {
Food int
}
func main() {
w := ecs.New()
ecs.RegisterComponent[Player](w)
ecs.RegisterComponent[Inventory](w)
player := ecs.NewEntity(w)
ecs.AddComponent(w, player, Player{X: 200, Y: 400})
ecs.AddComponent(w, player, Inventory{Food: 2})
w.Init()
fmt.Printf("Player: %v\n", ecs.DebugComponent[Player](w, player))
fmt.Printf("Inventory: %v\n", ecs.DebugComponent[Inventory](w, player))
ecs.RemoveComponent[Inventory](w, player)
fmt.Printf("Inventory: %v\n", ecs.DebugComponent[Inventory](w, player))
}
Output: Player: {X:200 Y:400} Inventory: {Food:2} Inventory: <nil>
func RemoveEntity ¶
RemoveEntity removes the entity and all of its associated components.
Types ¶
type InitSystem ¶
type InitSystem interface {
Init(*World)
}
type Signature ¶
type Signature uint64
type SystemType ¶
type SystemType interface {
Update(UpdateState)
}
type UpdateState ¶
type WindowSettings ¶
type WindowSettings struct {
}