Documentation
¶
Overview ¶
Package ecs is a hand-rolled Entity Component System for Gomag.
Design goals:
- Pure Go — no CGO, no reflection in the hot path
- Fat value components stored densely (sparse-set)
- Generational entity IDs so stale handles are cheap to detect
- Systems are just functions; this package owns data, not game logic
Components are plain structs. Prefer fewer, fatter types (e.g. one Enemy blob with health, path progress, and rewards) over micro-components. That fits tower-defense and most 2D games, and keeps Go code readable.
Typical usage:
w := ecs.NewWorld()
enemies := ecs.NewStore[Enemy](w)
transforms := ecs.NewStore[Transform](w)
e := w.Spawn()
enemies.Set(e, Enemy{Health: 100, Speed: 40})
transforms.Set(e, Transform{X: 10, Y: 20})
enemies.Each(func(e ecs.Entity, enemy *Enemy) {
// mutate in place
})
Index ¶
- func Join2[A, B any](a *Store[A], b *Store[B], fn func(e Entity, a *A, b *B))
- func Join3[A, B, C any](a *Store[A], b *Store[B], c *Store[C], fn func(e Entity, a *A, b *B, c *C))
- type Entity
- type Store
- func (s *Store[T]) Clear()
- func (s *Store[T]) Each(fn func(e Entity, c *T))
- func (s *Store[T]) Get(e Entity) (T, bool)
- func (s *Store[T]) GetMut(e Entity) (*T, bool)
- func (s *Store[T]) Has(e Entity) bool
- func (s *Store[T]) Len() int
- func (s *Store[T]) Remove(e Entity)
- func (s *Store[T]) Set(e Entity, c T) bool
- type World
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Entity ¶
type Entity uint64
Entity is a generational handle into a World.
The low 32 bits are the slot index; the high 32 bits are the generation. Destroying an entity bumps the generation so old handles fail Alive checks.
const Nil Entity = 0
Nil is the zero entity handle. It is never alive. Worlds start generations at 1 so a real entity is never equal to Nil.
func (Entity) Generation ¶
Generation returns how many times this slot has been recycled.
type Store ¶
type Store[T any] struct { // contains filtered or unexported fields }
Store is a sparse-set of fat component values of type T.
Components live in a packed dense slice for iteration locality. Missing components are absent from the set — there is no zero-value sentinel.
Prefer registering stores with NewStore so World.Destroy removes components.
func NewStore ¶
NewStore creates a component store bound to w. Destroying an entity in w automatically removes its T component.
func (*Store[T]) Clear ¶
func (s *Store[T]) Clear()
Clear removes every component without affecting entity lifetimes.
func (*Store[T]) Each ¶
Each iterates dense storage. Mutating through c is allowed; do not Set/Remove/Clear this store during iteration.
func (*Store[T]) GetMut ¶
GetMut returns a pointer into dense storage for in-place mutation. The pointer is invalid after any Set/Remove/Clear on this store, or after World.Destroy of any entity that touches this store.
type World ¶
type World struct {
// contains filtered or unexported fields
}
World owns entity lifetimes and notifies registered stores on destroy.
func (*World) Destroy ¶
Destroy marks an entity dead, recycles its slot, and strips components. Destroying a stale or already-dead handle is a no-op.