ecs

package module
v0.7.8 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2021 License: MIT Imports: 2 Imported by: 0

README

ECS - Entity Component System

Go Report Card BCH compliance

Build your own Game-Engine based on the Entity Component System concept in Golang

The architectural pattern of an ECS is mostly used in game development, to provide long-term maintainability and extendability of large, dynamic systems.

Overview

An Entity is basically a composition of different components and has an ID.
A Component contains only the state or data of one specific aspect like health, position, velocity etc.
A System handles the behaviour or logic of the components. A movement system uses the position and velocity to implement an entities movement.

Table of Contents

Goals

  • Provide an easy-to-use framework to build a game engine from scratch.
  • No dependencies to other modules or specific game libraries - Feel free to use what fits your needs.
  • Minimum overhead - use only what is really needed.
  • Plugins to offer unlimited room for improvements.
  • Interoperability between non-Go libraries and Go via Main/Do.

Installation

From Source

go get -u github.com/andygeiss/ecs

Steps to start

In the first step we have to be clear about what our game engine should do. The main task is to make sure that we have all the essential components that are necessary for the technical and logical aspects are responsible, are combined with each other.

An Entity Component System (ECS) helps us to do just that, as the logical components (data) such as entities and their components can be separated from the actual logic. One of the advantages of this is that we can implement and test the game mechanics independently of the rest. So let's start...

We decide to use 2D and define the three most important components:

  • Position
  • Size
  • Velocity

We store these as components.go (Example: here).

In the next step, the three most important systems implement

  • Collision
  • Movement
  • Rendering

We store these as systems.go (Example: here).

The collision and movement system contains the actual game mechanics:

func (m *Collision) Process(em *ecs.EntityManager) (state int) {
	for _, entity := range em.FilterByMask(MaskPosition | MaskVelocity) {
		position := entity.Get(MaskPosition).(*Position)
		velocity := entity.Get(MaskVelocity).(*Velocity)
		if position.X >= m.width || position.X <= 0 {
			velocity.X = -velocity.X
		}
		if position.Y >= m.height || position.Y <= 0 {
			velocity.Y = -velocity.Y
		}
	}
	return ecs.StateEngineContinue
}

The rendering system must be adapted to a specific game library. In our example we have used SDL. In the example of Pong we used Raylib.

Finally we create a main.go file (Example: here) and link the systems together:

func run() {
	em := ecs.NewEntityManager()
	em.Add(generateEntities(1000)...)
	sm := ecs.NewSystemManager()
	sm.Add(
		engine.NewMovement(),
		engine.NewCollision(Width, Height),
		engine.NewRendering(Width, Height, "ECS with SDL Demo"),
	)
	ecs.Run(em, sm)
}

func main() {
	ecs.Main(func() {
		run()
	})
}

stats

Documentation

Index

Constants

View Source
const (
	StateEngineContinue = 0
	StateEngineStop     = 1
	Version             = "v0.0.68"
)

Variables

This section is empty.

Functions

func Do

func Do(fn func())

Do runs the given function in the main OS thread. This is necessary for non-Go library functions that depend on per-thread state.

func Main

func Main(worker func())

Main prepares Go for running Cgo calls in a separate worker function safely by locking the main OS thread to the current Goroutine.

func Run

func Run(em *EntityManager, sm *SystemManager, tick int)

Run simplifies the engine usage by calling the Setup(), Run() and Teardown() internally.

Types

type Component

type Component interface {
	Mask() uint64
}

Component contains only the data (no behaviour at all). The Name() method must be implemented, because the EntityManager uses it to filter the entities by component names.

type Engine added in v0.7.8

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

engine is simple a composition of an EntityManager and a SystemManager. It handles the stages Setup(), Run() and Teardown() for all the systems.

func NewEngine

func NewEngine(entityManager *EntityManager, systemManager *SystemManager) *Engine

NewEngine creates a new Engine and returns its address.

func (*Engine) Run added in v0.7.8

func (e *Engine) Run(tick int)

Run calls the Process() method for each System until ShouldEngineStop is set to true.

func (*Engine) Setup added in v0.7.8

func (e *Engine) Setup()

Setup calls the Setup() method for each System and initializes ShouldEngineStop and ShouldEnginePause with false.

func (*Engine) Teardown added in v0.7.8

func (e *Engine) Teardown()

Teardown calls the Teardown() method for each System.

type Entity

type Entity struct {
	Components []Component `json:"components"`
	Id         string      `json:"id"`
	Masked     uint64      `json:"masked"`
}

Entity is simply a composition of one or more Components with an Id.

func NewEntity

func NewEntity(id string, components []Component) *Entity

NewEntity creates a new entity and pre-calculates the component maskSlice.

func (*Entity) Add

func (e *Entity) Add(cn ...Component)

Add a component.

func (*Entity) Get

func (e *Entity) Get(mask uint64) Component

Get a component by its bitmask.

func (*Entity) Has added in v0.7.7

func (e *Entity) Has(mask uint64) bool

Check if entity has a component.

func (*Entity) ID

func (e *Entity) ID() string

ID ...

func (*Entity) Mask

func (e *Entity) Mask() uint64

Mask returns a pre-calculated maskSlice to identify the Components.

func (*Entity) Remove

func (e *Entity) Remove(mask uint64)

Remove a component by using its maskSlice.

type EntityManager

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

EntityManager handles the access to each entity.

func NewEntityManager

func NewEntityManager() *EntityManager

NewEntityManager creates a new EntityManager and returns its address.

func (*EntityManager) Add

func (m *EntityManager) Add(entities ...*Entity)

Add entries to the manager.

func (*EntityManager) Entities

func (m *EntityManager) Entities() (entities []*Entity)

Entities returns all the entities.

func (*EntityManager) FilterByMask

func (m *EntityManager) FilterByMask(mask uint64) (entities []*Entity)

FilterBy returns the mapped entities, which Components name matched.

func (*EntityManager) Get

func (m *EntityManager) Get(id string) (entity *Entity)

Get a specific entity by Id.

func (*EntityManager) Remove

func (m *EntityManager) Remove(entity *Entity)

Remove a specific entity.

type Plugin

type Plugin func(em *EntityManager) (state int)

Plugin is a function which handles a specific kind of functionality by using an EntityManager to gain access to the entities.

type System

type System interface {
	Setup(entityManager *EntityManager)
	Process(entityManager *EntityManager, dt float64) (state int)
	Teardown()
}

System implements the behaviour of an entity by modifying the state, which is stored in each component of the entity.

type SystemManager

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

SystemManager handles the access to each system.

func NewSystemManager

func NewSystemManager() *SystemManager

NewSystemManager creates a new SystemManager and returns its address.

func (*SystemManager) Add

func (m *SystemManager) Add(systems ...System)

Add systems to the SystemManager.

func (*SystemManager) Systems

func (m *SystemManager) Systems() []System

Systems returns the system, which are internally stored.

Jump to

Keyboard shortcuts

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