volt

package module
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: Mar 18, 2025 License: Apache-2.0 Imports: 6 Imported by: 13

README

Volt

GitHub go.mod Go version License Go Reference Go Report Card Tests Codecov GitHub Issues or Pull Requests GitHub Issues or Pull Requests

Volt is an ECS(entity-component-system) oriented for games development with Go. It is inspired by the documentation available here: https://github.com/SanderMertens/ecs-faq

There is many ways to write an ECS, and Volt is based on the Archetype paradigm.

Knowledge

Entity

An entity is the end object in a game (e.g. a character). It is only defined by its identifier called EntityId. This identifier is randomly generated, its type uint64 avoiding to generate twice the same id. It is also required to set a name for each entity, only used to easily retrieve them when required.

Looking at the benchmark, a scene can handle between 100.000 to 1.000.000 depending on your machine and the complexity of the project. But of course, the lower the better, as it will allow the project to run on slower computers.

Component

An entity is composed from 1 to N Component(s). It is a structure of properties, and should not contain any logic by itself (meaning no functions). The Components are manipulated by Systems.

A Component is defined by its ComponentId, ranging between [0;2048].

System

A system is a specialized tool that fetches entities, filtered by their Components, and transforms the datas. For example: the audio could be managed by a system, or the graphics managed by a render system.

Volt does not directly implements Systems, but allows you to create Queries that you can use in your own specific tools.

Query

A Query is a search tool for the set of entities that possess (at least) the list of ComponentId provided. It is then possible to iterate over the result of this search within a System, in order to manipulate the Components.

Archetype

In an ECS (Entity-Component-System), an Archetype is the set of Entities that share the same ComponentId. The Archetype itself is not publicly exposed, but is instead managed internally and represents a major structure within Volt.

Storage

Using the Structure Of Arrays (SoA) paradigm, Components are persisted in a dedicated storage for each ComponentId. This allows for cache hits during read phases within Query iterations, resulting in significantly improved performance compared to an Array of Structures (AoS) model.

Basic Usage

  • Create a World to contain all the datas
world := volt.CreateWorld()
  • Create your components, and implement the ComponentInterface with GetComponentId(). Your ComponentId should range between [0;2048].
const (
    transformComponentId = iota
)

type transformComponent struct {
x, y, z float64
}

func (t transformComponent) GetComponentId() volt.ComponentId {
    return transformComponentId
}
type transformConfiguration struct {
	x, y, z float64
}
  • Register the component for the world to use it. The BuilderFn is optional, it allows to initialize and customize the component at its creation.
volt.RegisterComponent[transformComponent](world, &ComponentConfig[transformComponent]{BuilderFn: func(component any, configuration any) {
    conf := configuration.(*transformConfiguration)
    transformComponent := component.(*transformComponent)
	transformComponent.x = conf.x
	transformComponent.y = conf.y
	transformComponent.z = conf.z
}})
  • Create the entity
entityId := world.CreateEntity("entityName")

Important: the entity name MUST be unique.

  • Add the component to the entity
component := volt.ConfigureComponent[transformComponent](&scene.World, transformConfiguration{x: 1.0, y: 2.0, z: 3.0})
volt.AddComponent(&scene.World, entity, component)
  • Remove the component to the entity
err := RemoveComponent[testTransform](world, entityId)
if err != nil {
	fmt.Println(err)
}
  • Delete the entity
world.RemoveEntity(entityId)

Searching for an entity

  • Knowing an entity by its name, you can get its identifier:
entityId := world.SearchEntity("entityName")
  • The reversed search is also possible, fetching its name by its idenfier:
entityName := world.GetEntityName(entityId)

Queries

The most powerful feature is the possibility to query entities with a given set of Components. For example, in the Rendering system of the game engine, a query will fetch only for the entities having a Mesh & Transform:

query := volt.CreateQuery2[transformComponent, meshComponent](world, volt.QueryConfiguration{OptionalComponents: []volt.OptionalComponent{meshComponentId}})
for result := range query.Foreach(nil) {
    transformData(result.A)
}

The Foreach function receives a function to pre-filter the results, and returns an iterator.

For faster performances, you can use concurrency with the function ForeachChannel:

query := volt.CreateQuery2[transformComponent, meshComponent](world, volt.QueryConfiguration{OptionalComponents: []volt.OptionalComponent{meshComponentId}})
queryChannel := query.ForeachChannel(1000, nil)

runWorkers(4, func(workerId int) {
    for results := range queryChannel {
        for result := range results {
            transformData(result.A)
        }
    }
})

func runWorkers(workersNumber int, worker func(int)) {
  var wg sync.WaitGroup
  
  for i := range workersNumber {
    i := i
    wg.Add(1)
    go func(worker func(int)) {
        defer wg.Done()
        worker(i)
    }(worker)
  }
  
  wg.Wait()
}

Queries exist for 1 to 8 Components.

You can also get the number of entities, without looping on each:

total := query.Count()

Or get the entities identifiers as a slice:

entitiesIds := query.FetchAll()

Tags

Tags are considered like any other Component internally, except they have no structure/value attached. They cannot be fetched using functions like GetComponent. Due to their simpler form, they do not need to be registered.

Tags are useful to categorize your entities.

e.g. "NPC", "STATIC", "DISABLED". For example, if you want to fetch only static content, you can query through the tag "STATIC". The Query will return only the entities tagged, in a faster way than applying the filter function in Query.Foreach to check on each entities if they are static.

e.g. to fetch only static entities:

const TAG_STATIC_ID = iota + volt.TAGS_INDICES
query := volt.CreateQuery2[transformComponent, meshComponent](world, volt.QueryConfiguration{Tags: []volt.TagId{TAG_STATIC_ID}})
for result := range query.Foreach(nil) {
    transformData(result.A)
}

Important: the TagIds should start from volt.TAGS_INDICES, allowing a range from [2048; 65535] for TagIds.

You can Add a Tag, check if an entity Has a Tag, or Remove it:

world.AddTag(TAG_STATIC_ID, entityId)
world.HasTag(TAG_STATIC_ID, entityId)
world.RemoveTag(TAG_STATIC_ID, entityId)

Benchmark

Few ECS tools exist for Go. Arche and unitoftime/ecs are probably the most looked at, and the most optimized. In the benchmark folder, this module is compared to both of them.

The given results were produced by a ryzen 7 5800x, with 100.000 entities:

goos: linux goarch: amd64 pkg: benchmark cpu: AMD Ryzen 7 5800X 8-Core Processor

Benchmark Iterations ns/op B/op Allocs/op
BenchmarkCreateEntityArche-16 171 6948273 11096966 61
BenchmarkIterateArche-16 2704 426795 354 4
BenchmarkAddArche-16 279 4250519 120089 100000
BenchmarkRemoveArche-16 249 4821120 100000 100000
BenchmarkCreateEntityUECS-16 34 37943381 49119549 200146
BenchmarkIterateUECS-16 3885 287027 128 3
BenchmarkAddUECS-16 30 38097927 4620476 100004
BenchmarkRemoveUECS-16 40 31008811 3302536 100000
BenchmarkCreateEntityVolt-16 49 27246822 41214216 200259
BenchmarkIterateVolt-16 3651 329858 264 9
BenchmarkIterateConcurrentlyVolt-16 10000 102732 3330 93
BenchmarkAddVolt-16 54 22508281 4597363 300001
BenchmarkRemoveVolt-16 72 17219355 400001 100000

These results show a few things:

  • Arche is the fastest tool for writes operations. In our game development though we would rather lean towards fastest read operations, because the games loops will read way more often than write.
  • Unitoftime/ecs is the fastest tool for read operations on one thread only, but the writes are currently way slower than Arche and Volt (except on the Create benchmark).
  • Volt is a good compromise, an in-between: fast enough add/remove operations, and almost as fast as Arche and UECS for reads on one thread. Volt uses the new iterators from go1.23, which in their current implementation are slower than using a function call in the for-loop inside the Query (as done in UECS). This means, if the Go team finds a way to improve the performances from the iterators, we can hope to acheive near performances as UECS.
  • Thanks to the iterators, Volt provides a simple way to use goroutines for read operations. The data is received through a channel of iterator. As seen in the results, though not totally comparable, this allows way faster reading operations than any other implementation, and to use all the CPU capabilities to perform hard work on the components.
  • It might be doable to use goroutines in Arche and UECS, but I could not find this feature natively? Creating chunks of the resulted slices would generate a lot of memory allocations and is not desirable.
Other benchmarks

The creator and maintainer of Arche has published more complex benchmarks available here: https://github.com/mlange-42/go-ecs-benchmarks

What is to come next ?

  • Tags (zero sized types) are useful to query entities with specific features: for example, in a renderer, to get only the entities with the boolean isCulled == false. This would hugely reduce the loops operations in some scenarios. Currently we can use the filters on the iterators, but it does not avoid the fact that every entity (with the given components) is looped by the renderer.
  • For now the system is not designed to manage writes on a concurrent way: it means it is not safe to add/remove components in queries using multiples threads/goroutines. I need to figure out how to implement this, though I never met the need for this feature myself.

Sources

Contributing Guidelines

See how to contribute.

Licence

This project is distributed under the Apache 2.0 licence.

Documentation

Overview

Package volt is an ECS for game development, based on the Archetype paradigm.

Index

Constants

View Source
const COMPONENTS_INDICES = 0
View Source
const TAGS_INDICES = 2048

Variables

This section is empty.

Functions

func AddComponent

func AddComponent[T ComponentInterface](world *World, entityId EntityId, component T) error

AddComponent adds the component T to the existing EntityId.

It returns an error if:

  • the entity does not exist
  • the entity has the component
  • an internal error occurs

func AddComponents2

func AddComponents2[A, B ComponentInterface](world *World, entityId EntityId, a A, b B) error

AddComponents2 adds the components A, B to the existing EntityId.

It returns an error if:

  • the entity does not exist
  • the entity has one of the component
  • an internal error occurs

This solution is faster than an atomic solution.

func AddComponents3

func AddComponents3[A, B, C ComponentInterface](world *World, entityId EntityId, a A, b B, c C) error

AddComponents3 adds the components A, B, C to the existing EntityId.

It returns an error if:

  • the entity does not exist
  • the entity has one of the component
  • an internal error occurs

This solution is faster than an atomic solution.

func AddComponents4

func AddComponents4[A, B, C, D ComponentInterface](world *World, entityId EntityId, a A, b B, c C, d D) error

AddComponents4 adds the components A, B, C, D to the existing EntityId.

It returns an error if:

  • the entity does not exist
  • the entity has one of the component
  • an internal error occurs

This solution is faster than an atomic solution.

func AddComponents5

func AddComponents5[A, B, C, D, E ComponentInterface](world *World, entityId EntityId, a A, b B, c C, d D, e E) error

AddComponents5 adds the components A, B, C, D, E to the existing EntityId.

It returns an error if:

  • the entity does not exist
  • the entity has one of the component
  • an internal error occurs

This solution is faster than an atomic solution.

func AddComponents6

func AddComponents6[A, B, C, D, E, F ComponentInterface](world *World, entityId EntityId, a A, b B, c C, d D, e E, f F) error

AddComponents6 adds the components A, B, C, D, E, F to the existing EntityId.

It returns an error if:

  • the entity does not exist
  • the entity has one of the component
  • an internal error occurs

This solution is faster than an atomic solution.

func AddComponents7

func AddComponents7[A, B, C, D, E, F, G ComponentInterface](world *World, entityId EntityId, a A, b B, c C, d D, e E, f F, g G) error

AddComponents7 adds the components A, B, C, D, E, F, G to the existing EntityId.

It returns an error if:

  • the entity does not exist
  • the entity has one of the component
  • an internal error occurs

This solution is faster than an atomic solution.

func AddComponents8

func AddComponents8[A, B, C, D, E, F, G, H ComponentInterface](world *World, entityId EntityId, a A, b B, c C, d D, e E, f F, g G, h H) error

AddComponents8 adds the components A, B, C, D, E, F, G, H to the existing EntityId.

It returns an error if:

  • the entity does not exist
  • the entity has one of the component
  • an internal error occurs

This solution is faster than an atomic solution.

func ConfigureComponent

func ConfigureComponent[T ComponentInterface](world *World, conf any) T

ConfigureComponent configures a Component of type T using the build function related to it.

The parameter conf contains all the data required for the configuration.

func GetComponent

func GetComponent[T ComponentInterface](world *World, entityId EntityId) *T

GetComponent returns a pointer to the component T owned by the entity.

If the entity does not have the component, it returns nil

func RegisterComponent

func RegisterComponent[T ComponentInterface](world *World, config ComponentConfigInterface)

RegisterComponent adds a component T to the registry of the given World.

Once the component is registered, it can be added to an entity.

func RemoveComponent

func RemoveComponent[T ComponentInterface](world *World, entityId EntityId) error

RemoveComponent removes the component to EntityId.

It returns an error if the EntityId does not have the component.

Types

type ArchetypesComponentsEntities

type ArchetypesComponentsEntities[T ComponentInterface] map[archetypeId][]T

type ComponentBuilder

type ComponentBuilder func(component any, configuration any)

ComponentBuilder is the function called to set the properties of a given component.

A type assertion is required on component and configuration parameters.

type ComponentConfig

type ComponentConfig[T ComponentInterface] struct {
	BuilderFn ComponentBuilder
	// contains filtered or unexported fields
}

Configuration for a component T.

BuilderFn defines the function called to set a new component.

type ComponentConfigInterface

type ComponentConfigInterface interface {
	// contains filtered or unexported methods
}

ComponentConfigInterface is the interface defining the method required to create a new Component.

type ComponentId

type ComponentId smallId

Component identifier in the register.

type ComponentIdConf added in v1.6.0

type ComponentIdConf struct {
	ComponentId
	// contains filtered or unexported fields
}

type ComponentInterface

type ComponentInterface interface {
	GetComponentId() ComponentId
}

ComponentInterface is the interface for all the Components.

It wraps the GetComponentId method, that returns a Component identifier.

type ComponentsRegister

type ComponentsRegister map[ComponentId]ComponentConfigInterface

type ComponentsStorage

type ComponentsStorage[T ComponentInterface] struct {
	// contains filtered or unexported fields
}

type EntityId

type EntityId id

Entity identifier in the world.

func CreateEntityWithComponents2

func CreateEntityWithComponents2[A, B ComponentInterface](world *World, name string, a A, b B) (EntityId, error)

CreateEntityWithComponents2 creates an entity in World; It sets the components A, B to the entity, for faster performances than the atomic version.

func CreateEntityWithComponents3

func CreateEntityWithComponents3[A, B, C ComponentInterface](world *World, name string, a A, b B, c C) (EntityId, error)

CreateEntityWithComponents3 creates an entity in World;

It sets the components A, B, C to the entity, for faster performances than the atomic version.

func CreateEntityWithComponents4

func CreateEntityWithComponents4[A, B, C, D ComponentInterface](world *World, name string, a A, b B, c C, d D) (EntityId, error)

CreateEntityWithComponents4 creates an entity in World;

It sets the components A, B, C, D to the entity, for faster performances than the atomic version.

func CreateEntityWithComponents5

func CreateEntityWithComponents5[A, B, C, D, E ComponentInterface](world *World, name string, a A, b B, c C, d D, e E) (EntityId, error)

CreateEntityWithComponents5 creates an entity in World;

It sets the components A, B, C, D, E to the entity, for faster performances than the atomic version.

func CreateEntityWithComponents6

func CreateEntityWithComponents6[A, B, C, D, E, F ComponentInterface](world *World, name string, a A, b B, c C, d D, e E, f F) (EntityId, error)

CreateEntityWithComponents6 creates an entity in World;

It sets the components A, B, C, D, E, F to the entity, for faster performances than the atomic version.

func CreateEntityWithComponents7

func CreateEntityWithComponents7[A, B, C, D, E, F, G ComponentInterface](world *World, name string, a A, b B, c C, d D, e E, f F, g G) (EntityId, error)

CreateEntityWithComponents7 creates an entity in World;

It sets the components A, B, C, D, E, F, G to the entity, for faster performances than the atomic version.

func CreateEntityWithComponents8

func CreateEntityWithComponents8[A, B, C, D, E, F, G, H ComponentInterface](world *World, name string, a A, b B, c C, d D, e E, f F, g G, h H) (EntityId, error)

CreateEntityWithComponents8 creates an entity in World;

It sets the components A, B, C, D, E, F, G, H to the entity, for faster performances than the atomic version.

type OptionalComponent

type OptionalComponent ComponentId

Optional ComponentId for Queries.

type Query1

type Query1[A ComponentInterface] struct {
	World *World
	// contains filtered or unexported fields
}

Query for 1 component type.

func CreateQuery1

func CreateQuery1[A ComponentInterface](world *World, queryConfiguration QueryConfiguration) Query1[A]

CreateQuery1 returns a new Query1, with component A.

func (*Query1[A]) Count

func (query *Query1[A]) Count() int

Count returns the total of entities fetched for Query1.

func (*Query1[A]) Foreach

func (query *Query1[A]) Foreach(filterFn func(QueryResult1[A]) bool) iter.Seq[QueryResult1[A]]

Foreach returns an iterator of QueryResult1 for all the entities with component A to which filterFn function returns true.

func (*Query1[A]) ForeachChannel

func (query *Query1[A]) ForeachChannel(chunkSize int, filterFn func(QueryResult1[A]) bool) <-chan iter.Seq[QueryResult1[A]]

ForeachChannel returns a channel of iterators of QueryResult1 for all the entities with component A to which filterFn function returns true.

The parameter chunkSize defines the size of each iterators.

func (*Query1[A]) GetComponentsIds

func (query *Query1[A]) GetComponentsIds() []ComponentId

func (*Query1[A]) GetEntitiesIds

func (query *Query1[A]) GetEntitiesIds() []EntityId

GetEntitiesIds returns a slice of all the EntityId fetched for Query1.

type Query2

type Query2[A, B ComponentInterface] struct {
	World *World
	// contains filtered or unexported fields
}

Query for 2 components type.

func CreateQuery2

func CreateQuery2[A, B ComponentInterface](world *World, queryConfiguration QueryConfiguration) Query2[A, B]

CreateQuery2 returns a new Query2, with components A, B.

func (*Query2[A, B]) Count

func (query *Query2[A, B]) Count() int

Count returns the total of entities fetched for Query2.

func (*Query2[A, B]) Foreach

func (query *Query2[A, B]) Foreach(filterFn func(QueryResult2[A, B]) bool) iter.Seq[QueryResult2[A, B]]

Foreach returns an iterator of QueryResult2 for all the entities with components A, B to which filterFn function returns true.

func (*Query2[A, B]) ForeachChannel

func (query *Query2[A, B]) ForeachChannel(chunkSize int, filterFn func(QueryResult2[A, B]) bool) <-chan iter.Seq[QueryResult2[A, B]]

ForeachChannel returns a channel of iterators of QueryResult2 for all the entities with components A, B to which filterFn function returns true.

The parameter chunkSize defines the size of each iterators.

func (*Query2[A, B]) GetComponentsIds

func (query *Query2[A, B]) GetComponentsIds() []ComponentId

func (*Query2[A, B]) GetEntitiesIds

func (query *Query2[A, B]) GetEntitiesIds() []EntityId

GetEntitiesIds returns a slice of all the EntityId fetched for Query2.

type Query3

type Query3[A, B, C ComponentInterface] struct {
	World *World
	// contains filtered or unexported fields
}

Query for 3 components type.

func CreateQuery3

func CreateQuery3[A, B, C ComponentInterface](world *World, queryConfiguration QueryConfiguration) Query3[A, B, C]

CreateQuery3 returns a new Query3, with components A, B, C.

func (*Query3[A, B, C]) Count

func (query *Query3[A, B, C]) Count() int

Count returns the total of entities fetched for Query3.

func (*Query3[A, B, C]) Foreach

func (query *Query3[A, B, C]) Foreach(filterFn func(QueryResult3[A, B, C]) bool) iter.Seq[QueryResult3[A, B, C]]

Foreach returns an iterator of QueryResult3 for all the entities with components A, B, C to which filterFn function returns true.

func (*Query3[A, B, C]) ForeachChannel

func (query *Query3[A, B, C]) ForeachChannel(chunkSize int, filterFn func(QueryResult3[A, B, C]) bool) <-chan iter.Seq[QueryResult3[A, B, C]]

ForeachChannel returns a channel of iterators of QueryResult3 for all the entities with components A, B, C to which filterFn function returns true.

The parameter chunkSize defines the size of each iterators.

func (*Query3[A, B, C]) GetComponentsIds

func (query *Query3[A, B, C]) GetComponentsIds() []ComponentId

func (*Query3[A, B, C]) GetEntitiesIds

func (query *Query3[A, B, C]) GetEntitiesIds() []EntityId

GetEntitiesIds returns a slice of all the EntityId fetched for Query3.

type Query4

type Query4[A, B, C, D ComponentInterface] struct {
	World *World
	// contains filtered or unexported fields
}

Query for 4 components type.

func CreateQuery4

func CreateQuery4[A, B, C, D ComponentInterface](world *World, queryConfiguration QueryConfiguration) Query4[A, B, C, D]

CreateQuery4 returns a new Query4, with components A, B, C, D.

func (*Query4[A, B, C, D]) Count

func (query *Query4[A, B, C, D]) Count() int

Count returns the total of entities fetched for Query4.

func (*Query4[A, B, C, D]) Foreach

func (query *Query4[A, B, C, D]) Foreach(filterFn func(QueryResult4[A, B, C, D]) bool) iter.Seq[QueryResult4[A, B, C, D]]

Foreach returns an iterator of QueryResult4 for all the entities with components A, B, C, D to which filterFn function returns true.

func (*Query4[A, B, C, D]) ForeachChannel

func (query *Query4[A, B, C, D]) ForeachChannel(chunkSize int, filterFn func(QueryResult4[A, B, C, D]) bool) <-chan iter.Seq[QueryResult4[A, B, C, D]]

ForeachChannel returns a channel of iterators of QueryResult4 for all the entities with components A, B, C, D to which filterFn function returns true.

The parameter chunkSize defines the size of each iterators.

func (*Query4[A, B, C, D]) GetComponentsIds

func (query *Query4[A, B, C, D]) GetComponentsIds() []ComponentId

func (*Query4[A, B, C, D]) GetEntitiesIds

func (query *Query4[A, B, C, D]) GetEntitiesIds() []EntityId

GetEntitiesIds returns a slice of all the EntityId fetched for Query4.

type Query5

type Query5[A, B, C, D, E ComponentInterface] struct {
	World *World
	// contains filtered or unexported fields
}

Query for 5 components type.

func CreateQuery5

func CreateQuery5[A, B, C, D, E ComponentInterface](world *World, queryConfiguration QueryConfiguration) Query5[A, B, C, D, E]

CreateQuery5 returns a new Query5, with components A, B, C, D, E.

func (*Query5[A, B, C, D, E]) Count

func (query *Query5[A, B, C, D, E]) Count() int

Count returns the total of entities fetched for Query5.

func (*Query5[A, B, C, D, E]) Foreach

func (query *Query5[A, B, C, D, E]) Foreach(filterFn func(QueryResult5[A, B, C, D, E]) bool) iter.Seq[QueryResult5[A, B, C, D, E]]

Foreach returns an iterator of QueryResult5 for all the entities with components A, B, C, D, E to which filterFn function returns true.

func (*Query5[A, B, C, D, E]) ForeachChannel

func (query *Query5[A, B, C, D, E]) ForeachChannel(chunkSize int, filterFn func(QueryResult5[A, B, C, D, E]) bool) <-chan iter.Seq[QueryResult5[A, B, C, D, E]]

ForeachChannel returns a channel of iterators of QueryResult5 for all the entities with components A, B, C, D, E to which filterFn function returns true.

The parameter chunkSize defines the size of each iterators.

func (*Query5[A, B, C, D, E]) GetComponentsIds

func (query *Query5[A, B, C, D, E]) GetComponentsIds() []ComponentId

func (*Query5[A, B, C, D, E]) GetEntitiesIds

func (query *Query5[A, B, C, D, E]) GetEntitiesIds() []EntityId

GetEntitiesIds returns a slice of all the EntityId fetched for Query5.

type Query6

type Query6[A, B, C, D, E, F ComponentInterface] struct {
	World *World
	// contains filtered or unexported fields
}

Query for 6 components type.

func CreateQuery6

func CreateQuery6[A, B, C, D, E, F ComponentInterface](world *World, queryConfiguration QueryConfiguration) Query6[A, B, C, D, E, F]

CreateQuery6 returns a new Query6, with components A, B, C, D, E, F.

func (*Query6[A, B, C, D, E, F]) Count

func (query *Query6[A, B, C, D, E, F]) Count() int

Count returns the total of entities fetched for Query6.

func (*Query6[A, B, C, D, E, F]) Foreach

func (query *Query6[A, B, C, D, E, F]) Foreach(filterFn func(QueryResult6[A, B, C, D, E, F]) bool) iter.Seq[QueryResult6[A, B, C, D, E, F]]

Foreach returns an iterator of QueryResult6 for all the entities with components A, B, C, D, E, F to which filterFn function returns true.

func (*Query6[A, B, C, D, E, F]) ForeachChannel

func (query *Query6[A, B, C, D, E, F]) ForeachChannel(chunkSize int, filterFn func(QueryResult6[A, B, C, D, E, F]) bool) <-chan iter.Seq[QueryResult6[A, B, C, D, E, F]]

ForeachChannel returns a channel of iterators of QueryResult6 for all the entities with components A, B, C, D, E, F to which filterFn function returns true.

The parameter chunkSize defines the size of each iterators.

func (*Query6[A, B, C, D, E, F]) GetComponentsIds

func (query *Query6[A, B, C, D, E, F]) GetComponentsIds() []ComponentId

func (*Query6[A, B, C, D, E, F]) GetEntitiesIds

func (query *Query6[A, B, C, D, E, F]) GetEntitiesIds() []EntityId

GetEntitiesIds returns a slice of all the EntityId fetched for Query6.

type Query7

type Query7[A, B, C, D, E, F, G ComponentInterface] struct {
	World *World
	// contains filtered or unexported fields
}

Query for 7 components type.

func CreateQuery7

func CreateQuery7[A, B, C, D, E, F, G ComponentInterface](world *World, queryConfiguration QueryConfiguration) Query7[A, B, C, D, E, F, G]

CreateQuery7 returns a new Query7, with components A, B, C, D, E, F, G.

func (*Query7[A, B, C, D, E, F, G]) Count

func (query *Query7[A, B, C, D, E, F, G]) Count() int

Count returns the total of entities fetched for Query7.

func (*Query7[A, B, C, D, E, F, G]) Foreach

func (query *Query7[A, B, C, D, E, F, G]) Foreach(filterFn func(QueryResult7[A, B, C, D, E, F, G]) bool) iter.Seq[QueryResult7[A, B, C, D, E, F, G]]

Foreach returns an iterator of QueryResult7 for all the entities with components A, B, C, D, E, F, G to which filterFn function returns true.

func (*Query7[A, B, C, D, E, F, G]) ForeachChannel

func (query *Query7[A, B, C, D, E, F, G]) ForeachChannel(chunkSize int, filterFn func(QueryResult7[A, B, C, D, E, F, G]) bool) <-chan iter.Seq[QueryResult7[A, B, C, D, E, F, G]]

ForeachChannel returns a channel of iterators of QueryResult7 for all the entities with components A, B, C, D, E, F, G to which filterFn function returns true.

The parameter chunkSize defines the size of each iterators.

func (*Query7[A, B, C, D, E, F, G]) GetComponentsIds

func (query *Query7[A, B, C, D, E, F, G]) GetComponentsIds() []ComponentId

func (*Query7[A, B, C, D, E, F, G]) GetEntitiesIds

func (query *Query7[A, B, C, D, E, F, G]) GetEntitiesIds() []EntityId

GetEntitiesIds returns a slice of all the EntityId fetched for Query7.

type Query8

type Query8[A, B, C, D, E, F, G, H ComponentInterface] struct {
	World *World
	// contains filtered or unexported fields
}

Query for 8 components type.

func CreateQuery8

func CreateQuery8[A, B, C, D, E, F, G, H ComponentInterface](world *World, queryConfiguration QueryConfiguration) Query8[A, B, C, D, E, F, G, H]

CreateQuery8 returns a new Query8, with components A, B, C, D, E, F, G, H.

func (*Query8[A, B, C, D, E, F, G, H]) Count

func (query *Query8[A, B, C, D, E, F, G, H]) Count() int

Count returns the total of entities fetched for Query8.

func (*Query8[A, B, C, D, E, F, G, H]) Foreach

func (query *Query8[A, B, C, D, E, F, G, H]) Foreach(filterFn func(QueryResult8[A, B, C, D, E, F, G, H]) bool) iter.Seq[QueryResult8[A, B, C, D, E, F, G, H]]

Foreach returns an iterator of QueryResult8 for all the entities with components A, B, C, D, E, F, G, H to which filterFn function returns true.

func (*Query8[A, B, C, D, E, F, G, H]) ForeachChannel

func (query *Query8[A, B, C, D, E, F, G, H]) ForeachChannel(chunkSize int, filterFn func(QueryResult8[A, B, C, D, E, F, G, H]) bool) <-chan iter.Seq[QueryResult8[A, B, C, D, E, F, G, H]]

ForeachChannel returns a channel of iterators of QueryResult8 for all the entities with components A, B, C, D, E, F, G, H to which filterFn function returns true.

The parameter chunkSize defines the size of each iterators.

func (*Query8[A, B, C, D, E, F, G, H]) GetComponentsIds

func (query *Query8[A, B, C, D, E, F, G, H]) GetComponentsIds() []ComponentId

func (*Query8[A, B, C, D, E, F, G, H]) GetEntitiesIds

func (query *Query8[A, B, C, D, E, F, G, H]) GetEntitiesIds() []EntityId

GetEntitiesIds returns a slice of all the EntityId fetched for Query8.

type QueryConfiguration added in v1.4.0

type QueryConfiguration struct {
	Tags               []TagId
	OptionalComponents []OptionalComponent
}

type QueryResult1

type QueryResult1[A ComponentInterface] struct {
	EntityId EntityId
	A        *A
}

Result returned for Query1.

type QueryResult2

type QueryResult2[A, B ComponentInterface] struct {
	EntityId EntityId
	A        *A
	B        *B
}

Result returned for Query2.

type QueryResult3

type QueryResult3[A, B, C ComponentInterface] struct {
	EntityId EntityId
	A        *A
	B        *B
	C        *C
}

Result returned for Query3.

type QueryResult4

type QueryResult4[A, B, C, D ComponentInterface] struct {
	EntityId EntityId
	A        *A
	B        *B
	C        *C
	D        *D
}

Result returned for Query4.

type QueryResult5

type QueryResult5[A, B, C, D, E ComponentInterface] struct {
	EntityId EntityId
	A        *A
	B        *B
	C        *C
	D        *D
	E        *E
}

Result returned for Query5.

type QueryResult6

type QueryResult6[A, B, C, D, E, F ComponentInterface] struct {
	EntityId EntityId
	A        *A
	B        *B
	C        *C
	D        *D
	E        *E
	F        *F
}

Result returned for Query6.

type QueryResult7

type QueryResult7[A, B, C, D, E, F, G ComponentInterface] struct {
	EntityId EntityId
	A        *A
	B        *B
	C        *C
	D        *D
	E        *E
	F        *F
	G        *G
}

Result returned for Query7.

type QueryResult8

type QueryResult8[A, B, C, D, E, F, G, H ComponentInterface] struct {
	EntityId EntityId
	A        *A
	B        *B
	C        *C
	D        *D
	E        *E
	F        *F
	G        *G
	H        *H
}

Result returned for Query8.

type TagId added in v1.4.0

type TagId = ComponentId

type World

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

World representation, container of all the data related to entities and their Components.

func CreateWorld

func CreateWorld(initialCapacity int) *World

CreateWorld returns a pointer to a new World.

It preallocates initialCapacity in memory.

func (*World) AddComponent

func (world *World) AddComponent(entityId EntityId, componentId ComponentId, conf any) error

AddComponent adds the component with ComponentId to the EntityId.

This non-generic version is adapted for when generics are not available, though might be slower. It returns an error if:

  • the entity already has the componentId
  • the componentId is not registered in the World
  • an internal error occurs

func (*World) AddComponents added in v1.6.0

func (world *World) AddComponents(entityId EntityId, componentsIdsConfs ...ComponentIdConf) error

AddComponents adds variadic components to the EntityId.

This non-generic version is adapted for when generics are not available, though might be slower. It returns an error if:

  • the entity already has the components Ids
  • the componentsIds are not registered in the World
  • an internal error occurs

func (*World) AddTag added in v1.4.0

func (world *World) AddTag(tagId TagId, entityId EntityId) error

AddTag adds a TagId to a given EntityId. This function returns an error if: - The id is lower than the valid range (< TAGS_INDICES) - The Tag is already owned

func (*World) Count added in v1.2.0

func (world *World) Count() int

Count returns the number of entities in World.

func (*World) CreateEntity

func (world *World) CreateEntity(name string) EntityId

CreateEntity creates a new Entity in World; It is linked to no Component.

func (*World) GetComponent

func (world *World) GetComponent(entityId EntityId, componentId ComponentId) (any, error)

GetComponent returns the component with ComponentId for EntityId.

This non-generic version is adapted for when generics are not available, though might be slower and requires a type assertion. It returns an error if:

  • the ComponentId is not registered in the World
  • the entity does not have the component

func (*World) GetEntityName

func (world *World) GetEntityName(entityId EntityId) string

GetEntityName returns the name of an EntityId. If not found, returns an empty string.

func (*World) HasComponents

func (world *World) HasComponents(entityId EntityId, componentsIds ...ComponentId) bool

HasComponents returns whether the entity has the given variadic list of ComponentId.

It returns false if at least one ComponentId is not owned.

func (*World) HasTag added in v1.4.0

func (world *World) HasTag(tagId TagId, entityId EntityId) bool

HasTag returns a boolean, to check if an EntityId owns a Tag.

func (*World) PublishEntity

func (world *World) PublishEntity(entityId EntityId)

PublishEntity calls the callback setted in SetEntityAddedFn.

func (*World) RemoveComponent

func (world *World) RemoveComponent(entityId EntityId, componentId ComponentId) error

RemoveComponent removes the component with ComponentId from the EntityId.

This non-generic version is adapted for when generics are not available, though might be slower. It returns an error if:

  • the entity does not have the component
  • the ComponentId is not registered in the World

func (*World) RemoveEntity

func (world *World) RemoveEntity(entityId EntityId)

RemoveEntity removes all the data related to an Entity.

It calls the callback setted in SetEntityRemovedFn beforehand, so that the callback still has access to the data.

func (*World) RemoveTag added in v1.4.0

func (world *World) RemoveTag(tagId TagId, entityId EntityId) error

RemoveTags removes a Tag for a given EntityId. It returns an error if: - The entity does not exists. - The entity already owns the Tag.

func (*World) SearchEntity

func (world *World) SearchEntity(name string) EntityId

SearchEntity returns the EntityId named by name. If not found, returns 0.

func (*World) SetComponentAddedFn

func (world *World) SetComponentAddedFn(componentAddedFn func(entityId EntityId, componentId ComponentId))

SetComponentAddedFn sets a callback for when a component is added to an entity.

func (*World) SetComponentRemovedFn

func (world *World) SetComponentRemovedFn(componentRemovedFn func(entityId EntityId, componentId ComponentId))

SetComponentRemovedFn sets a callback for when a component is removed.

func (*World) SetEntityAddedFn

func (world *World) SetEntityAddedFn(entityAddedFn func(entityId EntityId))

SetEntityAddedFn sets a callback for when a new entity is added.

func (*World) SetEntityName

func (world *World) SetEntityName(entityId EntityId, name string)

SetEntityName sets the name for an EntityId.

func (*World) SetEntityRemovedFn

func (world *World) SetEntityRemovedFn(entityRemovedFn func(entityId EntityId))

SetEntityRemovedFn sets a callback for when an entity is removed.

Jump to

Keyboard shortcuts

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