ecs

package
v0.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 0 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func Join2

func Join2[A, B any](a *Store[A], b *Store[B], fn func(e Entity, a *A, b *B))

Join2 calls fn for every entity that has both A and B. Iteration follows a's dense order; b is looked up by entity. Do not mutate either store's membership during iteration.

func Join3

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))

Join3 calls fn for every entity that has A, B, and C.

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

func (e Entity) Generation() uint32

Generation returns how many times this slot has been recycled.

func (Entity) Index

func (e Entity) Index() uint32

Index returns the slot this entity occupies in the world.

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

func NewStore[T any](w *World) *Store[T]

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

func (s *Store[T]) Each(fn func(e Entity, c *T))

Each iterates dense storage. Mutating through c is allowed; do not Set/Remove/Clear this store during iteration.

func (*Store[T]) Get

func (s *Store[T]) Get(e Entity) (T, bool)

Get returns a copy of the component and whether it exists.

func (*Store[T]) GetMut

func (s *Store[T]) GetMut(e Entity) (*T, bool)

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.

func (*Store[T]) Has

func (s *Store[T]) Has(e Entity) bool

Has reports whether e currently has a T component.

func (*Store[T]) Len

func (s *Store[T]) Len() int

Len returns how many components are stored.

func (*Store[T]) Remove

func (s *Store[T]) Remove(e Entity)

Remove deletes the component for e if present.

func (*Store[T]) Set

func (s *Store[T]) Set(e Entity, c T) bool

Set inserts or overwrites the component for e. If e is not alive in the bound world, Set is a no-op and returns false.

type World

type World struct {
	// contains filtered or unexported fields
}

World owns entity lifetimes and notifies registered stores on destroy.

func NewWorld

func NewWorld() *World

NewWorld creates an empty world.

func (*World) Alive

func (w *World) Alive(e Entity) bool

Alive reports whether e still refers to a living entity in this world.

func (*World) Cap

func (w *World) Cap() int

Cap returns how many entity slots have been allocated (alive + free).

func (*World) Destroy

func (w *World) Destroy(e Entity)

Destroy marks an entity dead, recycles its slot, and strips components. Destroying a stale or already-dead handle is a no-op.

func (*World) Each

func (w *World) Each(fn func(Entity))

Each calls fn for every alive entity. Order is by slot index, not spawn order.

func (*World) Len

func (w *World) Len() int

Len returns how many entities are currently alive.

func (*World) Spawn

func (w *World) Spawn() Entity

Spawn creates a new alive entity.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL