ecs

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: May 29, 2023 License: MIT Imports: 7 Imported by: 72

Documentation

Overview

Package ecs contains Arche's core API.

See the top-level module github.com/mlange-42/arche for an overview.

Outline

Index

Examples

Constants

View Source
const MaskTotalBits = 128

MaskTotalBits is the size of Mask in bits.

It is the maximum number of component types that may exist in any World.

Variables

This section is empty.

Functions

func GetResource added in v0.5.0

func GetResource[T any](w *World) *T

GetResource returns a pointer to the given resource type in the world.

Returns nil if there is no such resource.

Uses reflection. For more efficient access, see World.Resources, and github.com/mlange-42/arche/generic.Resource.Get for a generic variant. These methods are more than 20 times faster than the GetResource function.

See also AddResource.

Example
world := ecs.NewWorld()

myRes := Position{100, 100}

ecs.AddResource(&world, &myRes)
res := ecs.GetResource[Position](&world)
fmt.Println(res)
Output:

&{100 100}

Types

type Batch added in v0.6.0

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

Batch is a helper to perform batched operations on the world.

Create using World.Batch.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID, velID)
builder.NewBatch(100)

world.Batch().Remove(ecs.All(posID, velID), velID)
world.Batch().RemoveEntities(ecs.All(posID))
Output:

func (*Batch) Add added in v0.8.0

func (b *Batch) Add(filter Filter, comps ...ID)

Add adds components to many entities, matching a filter.

Panics:

  • when called with components that can't be added because they are already present.
  • when called on a locked world. Do not use during Query iteration!

See also Batch.AddQ and World.Add.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID)
builder.NewBatch(100)

filter := ecs.All(posID)
world.Batch().Add(filter, velID)
Output:

func (*Batch) AddQ added in v0.8.0

func (b *Batch) AddQ(filter Filter, comps ...ID) Query

AddQ adds components to many entities, matching a filter. It returns a query over the affected entities.

Panics:

  • when called with components that can't be added because they are already present.
  • when called on a locked world. Do not use during Query iteration!

See also Batch.Add and World.Add.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID)
builder.NewBatch(100)

filter := ecs.All(posID)
query := world.Batch().AddQ(filter, velID)

for query.Next() {
	pos := (*Position)(query.Get(posID))
	pos.X = 100
}
Output:

func (*Batch) Exchange added in v0.8.0

func (b *Batch) Exchange(filter Filter, add []ID, rem []ID)

Exchange exchanges components for many entities, matching a filter.

Panics:

  • when called with components that can't be added or removed because they are already present/not present, respectively.
  • when called on a locked world. Do not use during Query iteration!

See also Batch.ExchangeQ and World.Exchange.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID)
builder.NewBatch(100)

filter := ecs.All(posID)
world.Batch().Exchange(
	filter,          // Filter
	[]ecs.ID{velID}, // Add components
	[]ecs.ID{posID}, // Remove components
)
Output:

func (*Batch) ExchangeQ added in v0.8.0

func (b *Batch) ExchangeQ(filter Filter, add []ID, rem []ID) Query

ExchangeQ exchanges components for many entities, matching a filter. It returns a query over the affected entities.

Panics:

  • when called with components that can't be added or removed because they are already present/not present, respectively.
  • when called on a locked world. Do not use during Query iteration!

See also Batch.Exchange and World.Exchange.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID)
builder.NewBatch(100)

filter := ecs.All(posID)
query := world.Batch().ExchangeQ(
	filter,          // Filter
	[]ecs.ID{velID}, // Add components
	[]ecs.ID{posID}, // Remove components
)

for query.Next() {
	vel := (*Velocity)(query.Get(velID))
	vel.X = 100
}
Output:

func (*Batch) Remove added in v0.8.0

func (b *Batch) Remove(filter Filter, comps ...ID)

Remove removes components from many entities, matching a filter.

Panics:

  • when called with components that can't be removed because they are not present.
  • when called on a locked world. Do not use during Query iteration!

See also Batch.RemoveQ and World.Remove.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID, velID)
builder.NewBatch(100)

filter := ecs.All(posID, velID)
world.Batch().Remove(filter, velID)
Output:

func (*Batch) RemoveEntities added in v0.6.0

func (b *Batch) RemoveEntities(filter Filter) int

RemoveEntities removes and recycles all entities matching a filter.

Returns the number of removed entities.

Panics when called on a locked world. Do not use during Query iteration!

See also World.RemoveEntity

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)

builder := ecs.NewBuilder(&world, posID)
builder.NewBatch(100)

filter := ecs.All(posID)
world.Batch().RemoveEntities(filter)
Output:

func (*Batch) RemoveQ added in v0.8.0

func (b *Batch) RemoveQ(filter Filter, comps ...ID) Query

RemoveQ removes components from many entities, matching a filter. It returns a query over the affected entities.

Panics:

  • when called with components that can't be removed because they are not present.
  • when called on a locked world. Do not use during Query iteration!

See also Batch.Remove and World.Remove.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID, velID)
builder.NewBatch(100)

filter := ecs.All(posID, velID)
query := world.Batch().RemoveQ(filter, velID)

for query.Next() {
	pos := (*Position)(query.Get(posID))
	pos.X = 100
}
Output:

func (*Batch) SetRelation added in v0.8.0

func (b *Batch) SetRelation(filter Filter, comp ID, target Entity)

SetRelation sets the Relation target for many entities, matching a filter.

Panics:

  • when called for a missing component.
  • when called for a component that is not a relation.
  • when called on a locked world. Do not use during Query iteration!

See also Relations.Set and Relations.SetBatch.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
childID := ecs.ComponentID[ChildOf](&world)

target := world.NewEntity()

builder := ecs.NewBuilder(&world, posID, childID)
builder.NewBatch(100)

filter := ecs.All(childID)
world.Batch().SetRelation(filter, childID, target)
Output:

func (*Batch) SetRelationQ added in v0.8.0

func (b *Batch) SetRelationQ(filter Filter, comp ID, target Entity) Query

SetRelationQ sets the Relation target for many entities, matching a filter. It returns a query over the affected entities.

Panics:

  • when called for a missing component.
  • when called for a component that is not a relation.
  • when called on a locked world. Do not use during Query iteration!

See also Relations.Set and Relations.SetBatch.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
childID := ecs.ComponentID[ChildOf](&world)

target := world.NewEntity()

builder := ecs.NewBuilder(&world, posID, childID)
builder.NewBatch(100)

filter := ecs.All(childID)
query := world.Batch().SetRelationQ(filter, childID, target)

for query.Next() {
	pos := (*Position)(query.Get(posID))
	pos.X = 100
}
Output:

type Builder added in v0.8.0

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

Builder for more flexible and batched entity creation.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID, velID)

_ = builder.New()
Output:

func NewBuilder added in v0.8.0

func NewBuilder(w *World, comps ...ID) *Builder

NewBuilder creates a builder from component IDs.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID, velID)

_ = builder.New()
Output:

func NewBuilderWith added in v0.8.0

func NewBuilderWith(w *World, comps ...Component) *Builder

NewBuilderWith creates a builder from component pointers.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

components := []ecs.Component{
	{ID: posID, Comp: &Position{X: 0, Y: 0}},
	{ID: velID, Comp: &Velocity{X: 10, Y: 2}},
}

builder := ecs.NewBuilderWith(&world, components...)

_ = builder.New()
Output:

func (*Builder) Add added in v0.8.0

func (b *Builder) Add(entity Entity, target ...Entity)

Add the builder's components to an entity.

The optional argument can be used to set the target Entity for the Builder's Relation. See Builder.WithRelation.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID, velID)

entity := world.NewEntity()
builder.Add(entity)
Output:

func (*Builder) New added in v0.8.0

func (b *Builder) New(target ...Entity) Entity

New creates an entity.

The optional argument can be used to set the target Entity for the Builder's Relation. See Builder.WithRelation.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID, velID)

_ = builder.New()
Output:

func (*Builder) NewBatch added in v0.8.0

func (b *Builder) NewBatch(count int, target ...Entity)

NewBatch creates many entities.

The optional argument can be used to set the target Entity for the Builder's Relation. See Builder.WithRelation.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID, velID)

builder.NewBatch(1000)
Output:

func (*Builder) NewBatchQ added in v0.8.0

func (b *Builder) NewBatchQ(count int, target ...Entity) Query

NewBatchQ creates many entities and returns a query over them.

The optional argument can be used to set the target Entity for the Builder's Relation. See Builder.WithRelation.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

builder := ecs.NewBuilder(&world, posID, velID)

query := builder.NewBatchQ(1000)

for query.Next() {
	// initialize components of the newly created entities
}
Output:

func (*Builder) WithRelation added in v0.8.0

func (b *Builder) WithRelation(comp ID) *Builder

WithRelation sets the Relation component for the builder.

Use in conjunction with the optional target argument of Builder.New, Builder.NewBatch and Builder.NewBatchQ.

See Relation for details and examples.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
childID := ecs.ComponentID[ChildOf](&world)

target := world.NewEntity()

builder := ecs.NewBuilder(&world, posID, childID).
	WithRelation(childID)

builder.New(target)
Output:

type Cache added in v0.6.0

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

Cache provides Filter caching to speed up queries.

Access it using World.Cache.

For registered filters, the relevant archetypes are tracked internally, so that there are no mask checks required during iteration. This is particularly helpful to avoid query iteration slowdown by a very high number of archetypes. If the number of archetypes exceeds approx. 50-100, uncached filters experience a slowdown. The relative slowdown increases with lower numbers of entities queried (noticeable below a few thousand entities). Cached filters avoid this slowdown.

The overhead of tracking cached filters internally is very low, as updates are required only when new archetypes are created.

Example
world := NewWorld()
posID := ComponentID[Position](&world)

filter := All(posID)
cached := world.Cache().Register(filter)
query := world.Query(&cached)

for query.Next() {
	// ...
}
Output:

func (*Cache) Register added in v0.6.0

func (c *Cache) Register(f Filter) CachedFilter

Register a Filter.

Use the returned CachedFilter to construct queries:

filter := All(posID, velID)
cached := world.Cache().Register(&filter)
query := world.Query(&cached)

func (*Cache) Unregister added in v0.6.0

func (c *Cache) Unregister(f *CachedFilter) Filter

Unregister a filter.

Returns the original filter.

type CachedFilter added in v0.6.0

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

CachedFilter is a filter that is cached by the world.

Create a cached filter from any other filter using Cache.Register. For details on caching, see Cache.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)

filter := ecs.All(posID)
cached := world.Cache().Register(filter)

query := world.Query(&cached)

for query.Next() {
	// ...
}
Output:

func (*CachedFilter) Matches added in v0.6.0

func (f *CachedFilter) Matches(bits Mask) bool

Matches the filter against a mask.

type Component added in v0.2.0

type Component struct {
	ID   ID          // Component ID.
	Comp interface{} // The component, as a pointer to a struct.
}

Component is a component ID/pointer pair.

It is a helper for World.Assign, World.NewEntityWith and NewBuilderWith. It is not related to how components are implemented in Arche.

type Config added in v0.1.3

type Config struct {
	// Capacity increment for archetypes and the entity index.
	// The default value is 128.
	CapacityIncrement int
	// Capacity increment for archetypes with a relation component.
	// The default value is CapacityIncrement.
	RelationCapacityIncrement int
}

Config provides configuration for an ECS World.

Example
package main

import (
	"github.com/mlange-42/arche/ecs"
)

func main() {
	config :=
		ecs.NewConfig().
			WithCapacityIncrement(1024).       // Optionally set capacity increment
			WithRelationCapacityIncrement(128) // Optionally set capacity increment for relations

	world := ecs.NewWorld(config)

	world.NewEntity()
}
Output:

func NewConfig added in v0.1.3

func NewConfig() Config

NewConfig creates a new default World configuration.

func (Config) WithCapacityIncrement added in v0.1.3

func (c Config) WithCapacityIncrement(inc int) Config

WithCapacityIncrement return a new Config with CapacityIncrement set. Use with method chaining.

func (Config) WithRelationCapacityIncrement added in v0.8.0

func (c Config) WithRelationCapacityIncrement(inc int) Config

WithRelationCapacityIncrement return a new Config with RelationCapacityIncrement set. Use with method chaining.

type Entity

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

Entity identifier. Holds an entity ID and it's generation for recycling.

Entities are only created via the World, using World.NewEntity or World.NewEntityWith. Batch creation of entities is possible via Builder.

Entities are intended to be stored and passed around via copy, not via pointers.

Example
world := NewWorld()

posID := ComponentID[Position](&world)
velID := ComponentID[Velocity](&world)

e1 := world.NewEntity()
e2 := world.NewEntity(posID, velID)

fmt.Println(e1.IsZero(), e2.IsZero())
Output:

false false

func (Entity) IsZero added in v0.1.1

func (e Entity) IsZero() bool

IsZero returns whether this entity is the reserved zero entity.

Example
world := NewWorld()

var e1 Entity
var e2 Entity = world.NewEntity()

fmt.Println(e1.IsZero(), e2.IsZero())
Output:

true false

type EntityEvent added in v0.4.0

type EntityEvent struct {
	Entity                  Entity // The entity that was changed.
	OldMask, NewMask        Mask   // The old and new component masks.
	Added, Removed, Current []ID   // Components added, removed, and after the change. DO NOT MODIFY!
	AddedRemoved            int    // Whether the entity itself was added (> 0), removed (< 0), or only changed (= 0).
	OldTarget, NewTarget    Entity // Old and new target entity
	TargetChanged           bool   // Whether this is (only) a change of the relation target.
}

EntityEvent contains information about component changes to an Entity.

To receive change events, register a function func(e *EntityEvent) with World.SetListener.

Events notified are entity creation, removal and changes to the component composition. Events are emitted immediately after the change is applied.

Except for removed entities, events are always fired when the World is in an unlocked state. Events for removed entities are fired right before removal of the entity, to allow for inspection of it's components. Therefore, the World is in a locked state during entity removal events.

Events for batch-creation of entities using a Builder are fired after all entities are created. For batch methods that return a Query, events are fired after the Query is closed (or fully iterated). This allows the World to be in an unlocked state, and notifies after potential entity initialization.

Note that the event pointer received by the listener function should not be stored, as the instance behind the pointer might be reused for further notifications.

Example
package main

import (
	"fmt"

	"github.com/mlange-42/arche/ecs"
)

func main() {
	world := ecs.NewWorld()

	listener := func(evt *ecs.EntityEvent) {
		fmt.Println(evt)
	}
	world.SetListener(listener)

	world.NewEntity()
}
Output:

&{{1 0} {0 0} {0 0} [] [] [] 1 {0 0} {0 0} false}

func (*EntityEvent) EntityAdded added in v0.4.0

func (e *EntityEvent) EntityAdded() bool

EntityAdded reports whether the entity was newly added.

func (*EntityEvent) EntityRemoved added in v0.4.0

func (e *EntityEvent) EntityRemoved() bool

EntityRemoved reports whether the entity was removed.

type Filter added in v0.4.0

type Filter interface {
	// Matches the filter against a mask, i.e. a component composition.
	Matches(bits Mask) bool
}

Filter is the interface for logic filters. Filters are required to query entities using World.Query.

See Mask, MaskFilter anf RelationFilter for basic filters. For type-safe generics queries, see package github.com/mlange-42/arche/generic. For advanced filtering, see package github.com/mlange-42/arche/filter.

type ID

type ID = uint8

ID is the component identifier type.

func ComponentID

func ComponentID[T any](w *World) ID

ComponentID returns the ID for a component type via generics. Registers the type if it is not already registered.

The number of unique component types per World is limited to MaskTotalBits.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)

world.NewEntity(posID)
Output:

func TypeID added in v0.4.0

func TypeID(w *World, tp reflect.Type) ID

TypeID returns the ID for a component type. Registers the type if it is not already registered.

The number of unique component types per World is limited to MaskTotalBits.

Example
world := ecs.NewWorld()
posID := ecs.TypeID(&world, reflect.TypeOf(Position{}))

world.NewEntity(posID)
Output:

type Mask

type Mask struct {
	Lo uint64 // First 64 bits of the mask
	Hi uint64 // Second 64 bits of the mask
}

Mask is a 128 bit bitmask. It is also a Filter for including certain components.

Use All to create a mask for a list of component IDs. A mask can be further specified using Mask.Without or Mask.Exclusive.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

filter := ecs.All(posID, velID)
query := world.Query(filter)

for query.Next() {
	// ...
}
Output:

func All added in v0.4.0

func All(ids ...ID) Mask

All creates a new Mask from a list of IDs. Matches all entities that have the respective components, and potentially further components.

See also Mask.Without and Mask.Exclusive

If any ID is greater than or equal to MaskTotalBits, it will not be added to the mask.

func (*Mask) Contains

func (b *Mask) Contains(other Mask) bool

Contains reports if the other mask is a subset of this mask.

func (*Mask) ContainsAny added in v0.4.0

func (b *Mask) ContainsAny(other Mask) bool

ContainsAny reports if any bit of the other mask is in this mask.

func (Mask) Exclusive added in v0.6.0

func (b Mask) Exclusive() MaskFilter

Exclusive creates a MaskFilter which filters for exactly the mask's components. Matches only entities that have exactly the given components, and no other.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

filter := ecs.All(posID, velID).Exclusive()
query := world.Query(&filter)

for query.Next() {
	// ...
}
Output:

func (*Mask) Get

func (b *Mask) Get(bit ID) bool

Get reports whether the bit at the given index ID is set.

Returns false for bit >= MaskTotalBits.

func (*Mask) IsZero added in v0.4.0

func (b *Mask) IsZero() bool

IsZero returns whether no bits are set in the mask.

func (Mask) Matches added in v0.4.0

func (b Mask) Matches(bits Mask) bool

Matches the mask as filter against another mask.

func (*Mask) Not added in v0.6.0

func (b *Mask) Not() Mask

Not returns the inversion of this mask.

func (*Mask) Reset

func (b *Mask) Reset()

Reset the mask setting all bits to false.

func (*Mask) Set

func (b *Mask) Set(bit ID, value bool)

Set sets the state of bit at the given index.

Has no effect for bit >= MaskTotalBits.

func (*Mask) TotalBitsSet

func (b *Mask) TotalBitsSet() int

TotalBitsSet returns how many bits are set in this mask.

func (Mask) Without added in v0.4.0

func (b Mask) Without(comps ...ID) MaskFilter

Without creates a MaskFilter which filters for including the mask's components, and excludes the components given as arguments.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

filter := ecs.All(posID).Without(velID)
query := world.Query(&filter)

for query.Next() {
	// ...
}
Output:

type MaskFilter added in v0.4.0

type MaskFilter struct {
	Include Mask // Components to include.
	Exclude Mask // Components to exclude.
}

MaskFilter is a Filter for including and excluding certain components.

See All, Mask.Without and Mask.Exclusive.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

filter := ecs.All(posID).Without(velID)
query := world.Query(&filter)

for query.Next() {
	// ...
}
Output:

func (*MaskFilter) Matches added in v0.4.0

func (f *MaskFilter) Matches(bits Mask) bool

Matches the filter against a mask.

type Query

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

Query is an iterator to iterate entities, filtered by a Filter.

Create queries through the World using World.Query.

See also the generic alternatives github.com/mlange-42/arche/generic.Query1, github.com/mlange-42/arche/generic.Query2, etc. For advanced filtering, see package github.com/mlange-42/arche/filter.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

filter := ecs.All(posID, velID)
query := world.Query(filter)
for query.Next() {
	pos := (*Position)(query.Get(posID))
	vel := (*Velocity)(query.Get(velID))
	pos.X += vel.X
	pos.Y += vel.Y
}
Output:

func (*Query) Close added in v0.1.2

func (q *Query) Close()

Close closes the Query and unlocks the world.

Automatically called when iteration finishes. Needs to be called only if breaking out of the query iteration.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
world.NewEntity(posID)

query := world.Query(ecs.All(posID))
cnt := query.Count()
fmt.Println(cnt)

query.Close()
Output:

1

func (*Query) Count added in v0.1.3

func (q *Query) Count() int

Count counts the entities matching this query.

Involves a small overhead of iterating through archetypes when called the first time. However, this is still much faster than manual counting via iteration.

Does not close the query.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
world.NewEntity(posID)

query := world.Query(ecs.All(posID))
cnt := query.Count()
fmt.Println(cnt)

query.Close()
Output:

1

func (*Query) Entity

func (q *Query) Entity() Entity

Entity returns the entity at the iterator's position.

func (*Query) Get

func (q *Query) Get(comp ID) unsafe.Pointer

Get returns the pointer to the given component at the iterator's position.

func (*Query) Has

func (q *Query) Has(comp ID) bool

Has returns whether the current entity has the given component.

func (*Query) Mask added in v0.4.0

func (q *Query) Mask() Mask

Mask returns the archetype Mask for the Entity at the iterator's current position.

Can be used for fast checks of the entity composition, e.g. using a Filter.

func (*Query) Next

func (q *Query) Next() bool

Next proceeds to the next Entity in the Query.

Returns false if no next entity could be found.

func (*Query) Relation added in v0.8.0

func (q *Query) Relation(comp ID) Entity

Relation returns the target entity for an entity relation.

Panics if the entity does not have the given component, or if the component is not a Relation.

func (*Query) Step added in v0.4.4

func (q *Query) Step(step int) bool

Step advances the query iterator by the given number of entities.

Query.Step(1) is equivalent to Query.Next().

This method, used together with Query.Count, can be useful for the selection of random entities.

type Relation added in v0.8.0

type Relation struct{}

Relation is a marker for entity relation components. It must be embedded as first field of a component that represent an entity relation.

Entity relations allow for fast queries using entity relationships. E.g. to iterate over all entities that are the child of a certain parent entity.

Currently, each entity can only have a single relation component.

See also RelationFilter, World.Relations, Relations.Get, Relations.Set and Builder.WithRelation.

Example
package main

import (
	"fmt"

	"github.com/mlange-42/arche/ecs"
)

type ChildOf struct {
	ecs.Relation
}

func main() {
	world := ecs.NewWorld()
	childID := ecs.ComponentID[ChildOf](&world)

	// Create a target/parent entity for the relation.
	parent := world.NewEntity()

	// Create a child entity with a relation to the parent.
	childBuilder := ecs.NewBuilder(&world, childID).WithRelation(childID)
	child := childBuilder.New(parent)

	// Get the relation target of the child.
	_ = world.Relations().Get(child, childID)

	// Filter for the relation.
	filter := ecs.NewRelationFilter(ecs.All(childID), parent)

	query := world.Query(&filter)
	for query.Next() {
		fmt.Println(
			query.Entity(),
			query.Relation(childID),
		)
	}
}
Output:

{2 0} {1 0}

type RelationFilter added in v0.8.0

type RelationFilter struct {
	Filter Filter // Components filter.
	Target Entity // Relation target entity.
}

RelationFilter is a Filter for a Relation target, in addition to components.

See Relation for details and examples.

Example
world := ecs.NewWorld()
childID := ecs.ComponentID[ChildOf](&world)

target := world.NewEntity()

builder := ecs.NewBuilder(&world, childID).WithRelation(childID)
builder.NewBatch(100, target)

filter := ecs.NewRelationFilter(ecs.All(childID), target)

query := world.Query(&filter)
for query.Next() {
	// ...
}
Output:

func NewRelationFilter added in v0.8.0

func NewRelationFilter(filter Filter, target Entity) RelationFilter

NewRelationFilter creates a new RelationFilter. It is a Filter for a Relation target, in addition to components.

func (*RelationFilter) Matches added in v0.8.0

func (f *RelationFilter) Matches(bits Mask) bool

Matches the filter against a mask.

type Relations added in v0.8.0

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

Relations provides access to entity Relation targets.

Access it using World.Relations.

Example
world := ecs.NewWorld()

relID := ecs.ComponentID[ChildOf](&world)

parent := world.NewEntity()
child := world.NewEntity(relID)

world.Relations().Set(child, relID, parent)
fmt.Println(world.Relations().Get(child, relID))
Output:

{1 0}

func (*Relations) Get added in v0.8.0

func (r *Relations) Get(entity Entity, comp ID) Entity

Get returns the target entity for an entity relation.

Panics:

  • when called for a removed (and potentially recycled) entity.
  • when called for a missing component.
  • when called for a component that is not a relation.

See Relation for details and examples.

func (*Relations) GetUnchecked added in v0.8.0

func (r *Relations) GetUnchecked(entity Entity, comp ID) Entity

GetUnchecked returns the target entity for an entity relation.

GetUnchecked is an optimized version of Relations.Get. Does not check if the entity is alive or that the component ID is applicable.

func (*Relations) Set added in v0.8.0

func (r *Relations) Set(entity Entity, comp ID, target Entity)

Set sets the target entity for an entity relation.

Panics:

  • when called for a removed (and potentially recycled) entity.
  • when called for a removed (and potentially recycled) target.
  • when called for a missing component.
  • when called for a component that is not a relation.
  • when called on a locked world. Do not use during Query iteration!

See Relation for details and examples.

func (*Relations) SetBatch added in v0.8.0

func (r *Relations) SetBatch(filter Filter, comp ID, target Entity)

SetBatch sets the Relation target for many entities, matching a filter.

Panics:

  • when called for a missing component.
  • when called for a component that is not a relation.
  • when called on a locked world. Do not use during Query iteration!

See also Relations.Set, Relations.SetBatchQ and Batch.SetRelation.

Example
world := ecs.NewWorld()

relID := ecs.ComponentID[ChildOf](&world)

parent := world.NewEntity()
ecs.NewBuilder(&world, relID).NewBatch(100)

filter := ecs.All(relID)
world.Relations().SetBatch(filter, relID, parent)
Output:

func (*Relations) SetBatchQ added in v0.8.0

func (r *Relations) SetBatchQ(filter Filter, comp ID, target Entity) Query

SetBatchQ sets the Relation target for many entities, matching a filter. Returns a query over all affected entities.

Panics:

  • when called for a missing component.
  • when called for a component that is not a relation.
  • when called on a locked world. Do not use during Query iteration!

See also Relations.Set, Relations.SetBatch and Batch.SetRelation.

Example
world := ecs.NewWorld()

relID := ecs.ComponentID[ChildOf](&world)

parent := world.NewEntity()
ecs.NewBuilder(&world, relID).NewBatch(100)

filter := ecs.All(relID)
query := world.Relations().SetBatchQ(filter, relID, parent)
fmt.Println(query.Count())
query.Close()
Output:

100

type ResID added in v0.5.0

type ResID = uint8

ResID is the resource identifier type.

func AddResource added in v0.5.0

func AddResource[T any](w *World, res *T) ResID

AddResource adds a resource to the world. Returns the ID for the added resource.

Panics if there is already such a resource.

Uses reflection. For more efficient access, see World.Resources, and github.com/mlange-42/arche/generic.Resource.Add for a generic variant.

The number of resources per World is limited to MaskTotalBits.

Example
world := ecs.NewWorld()

myRes := Position{100, 100}
ecs.AddResource(&world, &myRes)

res := ecs.GetResource[Position](&world)
fmt.Println(res)
Output:

&{100 100}

func ResourceID added in v0.5.0

func ResourceID[T any](w *World) ResID

ResourceID returns the ResID for a resource type via generics. Registers the type if it is not already registered.

The number of resources per World is limited to MaskTotalBits.

Example
world := ecs.NewWorld()
resID := ecs.ResourceID[Position](&world)

world.Resources().Add(resID, &Position{100, 100})
Output:

type Resources added in v0.6.0

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

Resources manage a world's resources.

Access it using World.Resources.

Example
world := NewWorld()

resID := ResourceID[Position](&world)

myRes := Position{100, 100}
world.Resources().Add(resID, &myRes)

res := (world.Resources().Get(resID)).(*Position)
fmt.Println(res)

if world.Resources().Has(resID) {
	world.Resources().Remove(resID)
}
Output:

&{100 100}

func (*Resources) Add added in v0.6.0

func (r *Resources) Add(id ResID, res any)

Add a resource to the world. The resource should always be a pointer.

Panics if there is already a resource of the given type.

See also github.com/mlange-42/arche/generic.Resource.Add for a generic variant.

func (*Resources) Get added in v0.6.0

func (r *Resources) Get(id ResID) interface{}

Get returns a pointer to the resource of the given type.

Returns nil if there is no such resource.

See also github.com/mlange-42/arche/generic.Resource.Get for a generic variant.

func (*Resources) Has added in v0.6.0

func (r *Resources) Has(id ResID) bool

Has returns whether the world has the given resource.

See also github.com/mlange-42/arche/generic.Resource.Has for a generic variant.

func (*Resources) Remove added in v0.6.0

func (r *Resources) Remove(id ResID)

Remove a resource from the world.

Panics if there is no resource of the given type.

See also github.com/mlange-42/arche/generic.Resource.Remove for a generic variant.

type World

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

World is the central type holding entity and component data, as well as resources.

The World provides all the basic ECS functionality of Arche, like World.Query, World.NewEntity, World.Add, World.Remove or World.RemoveEntity.

For more advanced functionality, see World.Relations, World.Resources, World.Batch, World.Cache and Builder.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

_ = world.NewEntity(posID, velID)
Output:

func NewWorld

func NewWorld(config ...Config) World

NewWorld creates a new World from an optional Config.

Uses the default Config if called without an argument. Accepts zero or one arguments.

Example
package main

import (
	"github.com/mlange-42/arche/ecs"
)

func main() {
	defaultWorld := ecs.NewWorld()

	configWorld := ecs.NewWorld(
		ecs.NewConfig().
			WithCapacityIncrement(1024).
			WithRelationCapacityIncrement(64),
	)

	_, _ = defaultWorld, configWorld
}
Output:

func (*World) Add

func (w *World) Add(entity Entity, comps ...ID)

Add adds components to an Entity.

Panics:

  • when called for a removed (and potentially recycled) entity.
  • when called with components that can't be added because they are already present.
  • when called on a locked world. Do not use during Query iteration!

Note that calling a method with varargs in Go causes a slice allocation. For maximum performance, pre-allocate a slice of component IDs and pass it using ellipsis:

// fast
world.Add(entity, idA, idB, idC)
// even faster
world.Add(entity, ids...)

See also World.Exchange. See also the generic variants under github.com/mlange-42/arche/generic.Map1, etc.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

e := world.NewEntity()

world.Add(e, posID, velID)
Output:

func (*World) Alive

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

Alive reports whether an entity is still alive.

Example
package main

import (
	"fmt"

	"github.com/mlange-42/arche/ecs"
)

func main() {
	world := ecs.NewWorld()

	e := world.NewEntity()
	fmt.Println(world.Alive(e))

	world.RemoveEntity(e)
	fmt.Println(world.Alive(e))
}
Output:

true
false

func (*World) Assign added in v0.2.0

func (w *World) Assign(entity Entity, comps ...Component)

Assign assigns multiple components to an Entity, using pointers for the content.

The components in the Comp field of Component must be pointers. The passed pointers are no valid references to the assigned memory!

Panics:

  • when called for a removed (and potentially recycled) entity.
  • when called with components that can't be added because they are already present.
  • when called on a locked world. Do not use during Query iteration!

See also the generic variants under github.com/mlange-42/arche/generic.Map1, etc.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

e := world.NewEntity()

world.Assign(e,
	ecs.Component{ID: posID, Comp: &Position{X: 0, Y: 0}},
	ecs.Component{ID: velID, Comp: &Velocity{X: 10, Y: 2}},
)
Output:

func (*World) Batch added in v0.6.0

func (w *World) Batch() *Batch

Batch creates a Batch processing helper. It provides the functionality to manipulate large numbers of entities in batches, which is more efficient than handling them one by one.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)

filter := ecs.All(posID)
world.Batch().RemoveEntities(filter)
Output:

func (*World) Cache added in v0.6.0

func (w *World) Cache() *Cache

Cache returns the Cache of the world, for registering filters.

See Cache for details on filter caching.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)

filter := ecs.All(posID)
cached := world.Cache().Register(filter)
query := world.Query(&cached)

for query.Next() {
	// handle entities...
}
Output:

func (*World) ComponentType added in v0.7.0

func (w *World) ComponentType(id ID) (reflect.Type, bool)

ComponentType returns the reflect.Type for a given component ID, as well as whether the ID is in use.

func (*World) Exchange added in v0.2.0

func (w *World) Exchange(entity Entity, add []ID, rem []ID)

Exchange adds and removes components in one pass. This is more efficient than subsequent use of World.Add and World.Remove.

Panics:

  • when called for a removed (and potentially recycled) entity.
  • when called with components that can't be added or removed because they are already present/not present, respectively.
  • when called on a locked world. Do not use during Query iteration!

See also the generic variants under github.com/mlange-42/arche/generic.Exchange.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

e := world.NewEntity(posID)

world.Exchange(e, []ecs.ID{velID}, []ecs.ID{posID})
Output:

func (*World) Get

func (w *World) Get(entity Entity, comp ID) unsafe.Pointer

Get returns a pointer to the given component of an Entity. Returns nil if the entity has no such component.

Panics when called for a removed (and potentially recycled) entity.

See World.GetUnchecked for an optimized version for static entities. See also github.com/mlange-42/arche/generic.Map.Get for a generic variant.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)

e := world.NewEntity(posID)

pos := (*Position)(world.Get(e, posID))
pos.X, pos.Y = 10, 5
Output:

func (*World) GetUnchecked added in v0.7.0

func (w *World) GetUnchecked(entity Entity, comp ID) unsafe.Pointer

GetUnchecked returns a pointer to the given component of an Entity. Returns nil if the entity has no such component.

GetUnchecked is an optimized version of World.Get, for cases where entities are static or checked with World.Alive in user code. It can also be used after getting another component of the same entity with World.Get.

Panics when called for a removed entity, but not for a recycled entity.

See also github.com/mlange-42/arche/generic.Map.Get for a generic variant.

func (*World) Has

func (w *World) Has(entity Entity, comp ID) bool

Has returns whether an Entity has a given component.

Panics when called for a removed (and potentially recycled) entity.

See World.HasUnchecked for an optimized version for static entities. See also github.com/mlange-42/arche/generic.Map.Has for a generic variant.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)

e := world.NewEntity(posID)

if world.Has(e, posID) {
	world.Remove(e, posID)
}
Output:

func (*World) HasUnchecked added in v0.7.0

func (w *World) HasUnchecked(entity Entity, comp ID) bool

HasUnchecked returns whether an Entity has a given component.

HasUnchecked is an optimized version of World.Has, for cases where entities are static or checked with World.Alive in user code.

Panics when called for a removed entity, but not for a recycled entity.

See also github.com/mlange-42/arche/generic.Map.Has for a generic variant.

func (*World) IsLocked added in v0.1.2

func (w *World) IsLocked() bool

IsLocked returns whether the world is locked by any queries.

func (*World) Mask added in v0.4.0

func (w *World) Mask(entity Entity) Mask

Mask returns the archetype Mask for the given Entity.

Can be used for fast checks of the entity composition, e.g. using a Filter.

func (*World) NewEntity

func (w *World) NewEntity(comps ...ID) Entity

NewEntity returns a new or recycled Entity. The given component types are added to the entity.

Panics when called on a locked world. Do not use during Query iteration!

Note that calling a method with varargs in Go causes a slice allocation. For maximum performance, pre-allocate a slice of component IDs and pass it using ellipsis:

// fast
world.NewEntity(idA, idB, idC)
// even faster
world.NewEntity(ids...)

For more advanced and batched entity creation, see Builder. See also the generic variants under github.com/mlange-42/arche/generic.Map1, etc.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

_ = world.NewEntity(posID, velID)
Output:

func (*World) NewEntityWith added in v0.4.0

func (w *World) NewEntityWith(comps ...Component) Entity

NewEntityWith returns a new or recycled Entity. The given component values are assigned to the entity.

The components in the Comp field of Component must be pointers. The passed pointers are no valid references to the assigned memory!

Panics when called on a locked world. Do not use during Query iteration!

For more advanced and batched entity creation, see Builder. See also the generic variants under github.com/mlange-42/arche/generic.Map1, etc.

Example
world := ecs.NewWorld()

posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

_ = world.NewEntityWith(
	ecs.Component{ID: posID, Comp: &Position{X: 0, Y: 0}},
	ecs.Component{ID: velID, Comp: &Velocity{X: 10, Y: 2}},
)
Output:

func (*World) Query

func (w *World) Query(filter Filter) Query

Query creates a Query iterator.

Locks the world to prevent changes to component compositions. The lock is released automatically when the query finishes iteration, or when Query.Close is called. The number of simultaneous locks (and thus open queries) at a given time is limited to MaskTotalBits.

To create a Filter for querying, see All, Mask.Without, Mask.Exclusive and RelationFilter.

For type-safe generics queries, see package github.com/mlange-42/arche/generic. For advanced filtering, see package github.com/mlange-42/arche/filter.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

filter := ecs.All(posID, velID)
query := world.Query(filter)
for query.Next() {
	pos := (*Position)(query.Get(posID))
	vel := (*Velocity)(query.Get(velID))
	pos.X += vel.X
	pos.Y += vel.Y
}
Output:

func (*World) Relations added in v0.8.0

func (w *World) Relations() *Relations

Relations returns the Relations of the world, for accessing entity Relation targets.

See Relations for details.

Example
world := ecs.NewWorld()

relID := ecs.ComponentID[ChildOf](&world)

parent := world.NewEntity()
child := world.NewEntity(relID)

world.Relations().Set(child, relID, parent)
fmt.Println(world.Relations().Get(child, relID))
Output:

{1 0}

func (*World) Remove

func (w *World) Remove(entity Entity, comps ...ID)

Remove removes components from an entity.

Panics:

  • when called for a removed (and potentially recycled) entity.
  • when called with components that can't be removed because they are not present.
  • when called on a locked world. Do not use during Query iteration!

See also World.Exchange. See also the generic variants under github.com/mlange-42/arche/generic.Map1, etc.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)
velID := ecs.ComponentID[Velocity](&world)

e := world.NewEntity(posID, velID)

world.Remove(e, posID, velID)
Output:

func (*World) RemoveEntity added in v0.4.0

func (w *World) RemoveEntity(entity Entity)

RemoveEntity removes an Entity, making it eligible for recycling.

Panics when called on a locked world or for an already removed entity. Do not use during Query iteration!

Example
package main

import (
	"github.com/mlange-42/arche/ecs"
)

func main() {
	world := ecs.NewWorld()
	e := world.NewEntity()
	world.RemoveEntity(e)
}
Output:

func (*World) Reset added in v0.6.0

func (w *World) Reset()

Reset removes all entities and resources from the world.

Does NOT free reserved memory, remove archetypes, clear the registry, clear cached filters, etc. However, it removes archetypes with a relation component that is not zero.

Can be used to run systematic simulations without the need to re-allocate memory for each run. Accelerates re-populating the world by a factor of 2-3.

Example
package main

import (
	"github.com/mlange-42/arche/ecs"
)

func main() {
	world := ecs.NewWorld()
	_ = world.NewEntity()

	world.Reset()
}
Output:

func (*World) Resources added in v0.6.0

func (w *World) Resources() *Resources

Resources of the world.

Resources are component-like data that is not associated to an entity, but unique to the world.

Example
world := ecs.NewWorld()

resID := ecs.ResourceID[Position](&world)

myRes := Position{}
world.Resources().Add(resID, &myRes)

res := (world.Resources().Get(resID)).(*Position)
res.X, res.Y = 10, 5
Output:

func (*World) Set added in v0.3.0

func (w *World) Set(entity Entity, id ID, comp interface{}) unsafe.Pointer

Set overwrites a component for an Entity, using the given pointer for the content.

The passed component must be a pointer. Returns a pointer to the assigned memory. The passed in pointer is not a valid reference to that memory!

Panics:

  • when called for a removed (and potentially recycled) entity.
  • if the entity does not have a component of that type.
  • when called on a locked world. Do not use during Query iteration!

See also github.com/mlange-42/arche/generic.Map.Set for a generic variant.

Example
world := ecs.NewWorld()
posID := ecs.ComponentID[Position](&world)

e := world.NewEntity(posID)

world.Set(e, posID, &Position{X: 0, Y: 0})
Output:

func (*World) SetListener added in v0.4.0

func (w *World) SetListener(listener func(e *EntityEvent))

SetListener sets a listener callback func(e *EntityEvent) for the world. The listener is immediately called on every ecs.Entity change. Replaces the current listener. Call with nil to remove a listener.

For details, see EntityEvent.

Example
package main

import (
	"fmt"

	"github.com/mlange-42/arche/ecs"
)

func main() {
	world := ecs.NewWorld()

	listener := func(evt *ecs.EntityEvent) {
		fmt.Println(evt)
	}
	world.SetListener(listener)

	world.NewEntity()
}
Output:

&{{1 0} {0 0} {0 0} [] [] [] 1 {0 0} {0 0} false}

func (*World) Stats added in v0.4.0

func (w *World) Stats() *stats.WorldStats

Stats reports statistics for inspecting the World.

The underlying stats.WorldStats object is re-used and updated between calls. The returned pointer should thus not be stored for later analysis. Rather, the required data should be extracted immediately.

Example
package main

import (
	"fmt"

	"github.com/mlange-42/arche/ecs"
)

func main() {
	world := ecs.NewWorld()
	stats := world.Stats()
	fmt.Println(stats.Entities.String())
}
Output:

Entities -- Used: 0, Recycled: 0, Total: 0, Capacity: 128

Directories

Path Synopsis
Package stats provides the structs returned by ecs.World.Stats().
Package stats provides the structs returned by ecs.World.Stats().

Jump to

Keyboard shortcuts

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