arche

package module
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2024 License: MIT Imports: 0 Imported by: 0

README

Arche (logo) Test status Coverage Status Go Report Card User Guide Go Reference GitHub MIT license

Arche is an archetype-based Entity Component System for Go.

Arche is designed for the use in simulation models of the Department of Ecological Modelling at the Helmholtz Centre for Environmental Research.

——

Features   •   Installation   •   Usage   •   Tools   •   Design   •   Benchmarks

Features

Installation

To use Arche in a Go project, run:

go get github.com/mlange-42/arche

Usage

Here is the classical Position/Velocity example that every ECS shows in the docs. It uses the type-safe generic API.

See the User Guide, API docs and examples for details. For more complex examples, see arche-demo.

package main

import (
	"math/rand"

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

// Position component
type Position struct {
	X float64
	Y float64
}

// Velocity component
type Velocity struct {
	X float64
	Y float64
}

func main() {
	// Create a World.
	world := ecs.NewWorld()

	// Create a component mapper.
	mapper := generic.NewMap2[Position, Velocity](&world)

	// Create entities.
	for i := 0; i < 1000; i++ {
		// Create a new Entity with components.
		entity := mapper.New()
		// Get the components
		pos, vel := mapper.Get(entity)
		// Initialize component fields.
		pos.X = rand.Float64() * 100
		pos.Y = rand.Float64() * 100
		vel.X = rand.NormFloat64()
		vel.Y = rand.NormFloat64()
	}

	// Create a generic filter.
	filter := generic.NewFilter2[Position, Velocity]()

	// Time loop.
	for t := 0; t < 1000; t++ {
		// Get a fresh query.
		query := filter.Query(&world)
		// Iterate it
		for query.Next() {
			// Component access through the Query.
			pos, vel := query.Get()
			// Update component fields.
			pos.X += vel.X
			pos.Y += vel.Y
		}
	}
}

Tools

Several tools for Arche are provided in separate modules:

  • arche-serde provides JSON serialization and deserialization for Arche's World.
  • arche-model provides a wrapper around Arche, and some common systems and resources. its purpose is to get started with prototyping and developing simulation models immediately, focussing on the model logic.
  • arche-pixel provides OpenGL graphics and live plots for Arche using the Pixel game engine.
  • arche-demo provides examples of Arche models, which can be viewed in a live demo.

Design

Unlike most other ECS implementations, Arche is designed for the development of scientific, individual-based models rather than for game development. This motivates some design decisions, with an emphasis on simplicity, safety and performance. Nevertheless, Arche can also be used for game development.

Simple core API

The ecs.World object is a pure and simple ECS implementation in the sense of a data store for entities and components, with query and iteration capabilities. More advanced features like batch operations or entity relations are provided through separate objects.

There is neither an update loop nor systems. These should be implemented by the user. For a batteries-included implementation, see module arche-model.

The packages filter and generic provide a layer around the core for richer resp. generic queries and manipulation. They are built on top of the ecs package, so they could also be implemented by a user.

Determinism

Iteration order in Arche is deterministic and reproducible. This does not mean that entities are iterated in their order of insertion, nor in the same order in successive iterations. However, given the same operations on the ecs.World, iteration order will always be the same.

Strict and panic

Arche puts an emphasis on safety and on avoiding undefined behavior. It panics on unexpected operations, like removing a dead entity, adding a component that is already present, or attempting to change a locked world. This may not seem idiomatic for Go. However, explicit error handling in performance hotspots is not an option. Neither is silent failure, given the scientific background.

Limitations

  • The number of component types per World is limited to 256. This is mainly a performance decision.
  • The number of entities alive at any one time is limited to just under 5 billion (uint32 ID).

Benchmarks

A tabular overview of the runtime cost of typical Arche ECS operations is provided under benchmarks in the Arche's User Guide.

See also the latest Benchmarks CI run.

Arche vs. other Go ECS implementations

To the best of the author's knowledge, there are only a handful of ECS implementations in Go that are serious and somewhat maintained:

Here, Arche is benchmarked against these implementations. Feel free to open an issue if you have suggestions for improvements on the benchmarking code or other engines to include.

Position/Velocity

Build:

  • Create 1000 entities with Pos{float64, float64} and Vel{float64, float64}.
  • Create 9000 entities with only Pos{float64, float64}.

Iterate:

  • Iterate all entities with Pos and Vel, and add Vel to Pos.

Benchmark code: benchmark/competition/pos_vel.

Benchmark vs. Go ECSs - Pos/Vel
Position/Velocity benchmarks of Arche (left-most) vs. other Go ECS implementations. Left panel: query iteration (log scale), right panel: world setup and entity creation.

Add/remove component

Build:

  • Create 1000 entities with Pos{float64, float64}.

Iterate:

  • Get all entities with Pos, and add Vel{float64, float64} component.
  • Get all entities with Pos and Vel, and remove Vel component.

Note: The iteration is performed once before benchmarking, to avoid biasing slower implementations through one-time allocations.

Benchmark code: benchmark/competition/add_remove.

Benchmark vs. Go ECSs - Add/remove
Add/remove component benchmarks of Arche (left-most) vs. other Go ECS implementations. Left panel: iteration, right panel: world setup and entity creation.

Arche vs. Array of Structs

The plot below shows CPU time benchmarks of Arche (black) vs. Array of Structs (AoS, red) and Array of Pointers (AoP, blue) (with structs escaped to the heap).

Arche takes a constant time of just over 2ns per entity, regardless of the memory per entity (x-axis) and the number of entities (line styles). For AoS and AoP, time per access increases with memory per entity as well as number of entities, due to cache misses.

In the given example with components of 16 bytes each, from 64 bytes per entity onwards (i.e. 4 components or 8 float64 values), Arche outperforms AoS and AoP, particularly with a large number of entities. Note that the maximum shown here corresponds to only 25 MB of entity data!

Benchmark code: benchmark/competition/array_of_structs.

Benchmark vs. AoS and AoP
CPU benchmarks of Arche (black) vs. Array of Structs (AoS, red) and Array of Pointers (AoP, blue).

Cite as

Lange, M. (2023): Arche – An archetype-based Entity Component System for Go. DOI 10.5281/zenodo.7656484, GitHub repository: https://github.com/mlange-42/arche

License

This project is distributed under the MIT license.

Documentation

Overview

Package arche is an archetype-based Entity Component System for Go.

See the sub-packages:

🕮 Also read Arche's User Guide!

Directories

Path Synopsis
Package examples contains examples for Arche.
Package examples contains examples for Arche.
base
Demonstrates the core API that uses component IDs for access.
Demonstrates the core API that uses component IDs for access.
batch_ops
Demonstrates batch-creation, manipulation and removal of entities.
Demonstrates batch-creation, manipulation and removal of entities.
events
Demonstrates the use of a EntityEvent through listeners.
Demonstrates the use of a EntityEvent through listeners.
filter
Demonstrates the logic filter API, which provides additional query flexibility on top of the core API.
Demonstrates the logic filter API, which provides additional query flexibility on top of the core API.
generic
Demonstrates the generic API, which provides type-safety and convenience over the ID-based core API.
Demonstrates the generic API, which provides type-safety and convenience over the ID-based core API.
locked_world
Demonstrates how to create, remove or alter entities, despite the World is locked during query iteration.
Demonstrates how to create, remove or alter entities, despite the World is locked during query iteration.
no_ecs
Demonstrates that ECS can be mixed with non-ECS data structures, as long as they store entities.
Demonstrates that ECS can be mixed with non-ECS data structures, as long as they store entities.
no_ecs_generic
Demonstrates that ECS can be mixed with non-ECS data structures, as long as they store entities.
Demonstrates that ECS can be mixed with non-ECS data structures, as long as they store entities.
parallel
Demonstrates outer parallelism by running multiple simulations concurrently.
Demonstrates outer parallelism by running multiple simulations concurrently.
random_access
Demonstrates generic access to arbitrary/random entities.
Demonstrates generic access to arbitrary/random entities.
random_sampling
Demonstrates random sampling of a fixed number of entities from a query using Query.EntityAt().
Demonstrates random sampling of a fixed number of entities from a query using Query.EntityAt().
readme
The minimal example from the README using generic access.
The minimal example from the README using generic access.
relations
Demonstrates entity relations
Demonstrates entity relations
resources
Demonstrates the use of "Resources".
Demonstrates the use of "Resources".
systems
Demonstrates how to implement systems.
Demonstrates how to implement systems.
world_stats
Demonstrates using world and archetype statistics.
Demonstrates using world and archetype statistics.
benchmark module
ecs
Package ecs contains Arche's core API.
Package ecs contains Arche's core API.
event
Package event contains a mask type and bit switches for listener subscriptions.
Package event contains a mask type and bit switches for listener subscriptions.
stats
Package stats provides the structs returned by ecs.World.Stats().
Package stats provides the structs returned by ecs.World.Stats().
Package filter contains Arche's advanced logic filtering API.
Package filter contains Arche's advanced logic filtering API.
Package generic contains Arche's generic API.
Package generic contains Arche's generic API.
_generate
Package generate is for generating the boilerplate code required for the generic API.
Package generate is for generating the boilerplate code required for the generic API.
Package listener provides ecs.Listener implementations (see github.com/mlange-42/arche/ecs.Listener).
Package listener provides ecs.Listener implementations (see github.com/mlange-42/arche/ecs.Listener).

Jump to

Keyboard shortcuts

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