ecs

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: May 11, 2024 License: MIT Imports: 7 Imported by: 0

README

Entity Component System

A very simple, reflect.Type-based, opinionated ECS for quick usage (but without optimization).

Installation

go get github.com/elipZis/ecs

Usage

Create a new world with world := ecs.New(). This is your starting point.

Components

Components can be any struct you define, for example

type PositionComponent struct {
    X int
    Y int
}

type VelocityComponent struct {
    DX int
    DY int
}

You can create components on their own or embedded in other structs

type Player struct {
    PositionComponent
    VelocityComponent
    OtherComponent
    
    name string
}
Systems

A system must embed the EntitySystem and implement the System interface Run(ecs *ecs.ECS, dt time.Duration) function:

type MoveSystem struct {
   EntitySystem
}

func (this *MoveSystem) Run(ecs *ecs.ECS, dt time.Duration) {
   ...
}

Add systems to the world via e.g. world.AddSystem(&MoveSystem{}, &PositionComponent{}, &VelocityComponent{})

  • First is the function to run
  • Following are N component types to listen on
    • The system will only be invoked, if the registered component types match those of an entity

Note: Systems must be registered before entities!

Priority

By default all systems have a priority of 0. If you want to order systems, you can implement

Priority() int

The higher the number, the earlier the system is called.

Components & Entities

Inside a system you can access the ECS itself and you can get all components. The entity system itself knows about all their own entity ids and can iterate on these. For example:

func (this *MoveSystem) Run(ecs *ECS, dt time.Duration) {
	pos := ecs.GetComponents(PositionComponent{})
	vel := ecs.GetComponents(VelocityComponent{})

	for _, entityId := range this.entities {
		p := pos[entityId].(*PositionComponent)
		v := vel[entityId].(*VelocityComponent)
		p.X += v.DX
		p.Y += v.DY
	}
}

or

func (this *MoveSystem) Run(ecs *ECS, dt time.Duration) {
    positions := ecs.GetComponentsFor[*component.PositionComponent](e)
    velocities := ecs.GetComponents(VelocityComponent{})
    
    for _, entityId := range this.entities {
        p := positions[entityId]
        v := velocities[entityId]
        p.X += v.DX
        p.Y += v.DY
    }
}

There are several helper functions to provide access to the different components, context, entities etc.

Entities

To create and register a new entity, call

world.CreateEntity(&player.PositionComponent, &player.VelocityComponent, &player.OtherComponent)

It returns the entity with id and components, unique to this world.

The entity and their components will be injected into all systems, intersecting the component type combination. More is ok, less does not match!

Remove Entity

To remove an entity, call e.g. ecs.RemoveEntity(id uint64) on the world or in a system. If you remove an entity, it will be marked to be removed before the next iteration.

To immediately remove an entity, with consequences for subsequent systems, call ecs.RemoveEntityNow(id uint64).

Context

Via world.AddContext(...) you can add anything as context, available globally to all systems to query for via world.GetContext(...).

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

Notes

This package is heavily inspired by

Kudos, shout-out and thanks to them 🙏

Credits

Disclaimer

This source and the whole package comes without a warranty. It may or may not harm your computer. Please use with care. Any damage cannot be related back to the author. The source has been tested on a virtual environment and scanned for viruses and has passed all tests. It is not optimized for production, speed or memory consumption but just a personal open-source package.

License

The MIT License (MIT). Please see License File for more information.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetComponentFor

func GetComponentFor[T any](components map[uint64]any, eId uint64) T

GetComponentFor is a casting helper to return a typed component by entity id

func GetComponentsFor

func GetComponentsFor[T any](ecs *ECS) map[uint64]T

GetComponentsFor creates a typed map of the components

func GetContextFor added in v0.2.0

func GetContextFor[T any](ecs *ECS) T

GetContextFor is a convenience generic call for easier type

func GetEntityComponent

func GetEntityComponent[T any](ecs *ECS, eId uint64) T

GetEntityComponent is a typed helper to get a cast entity component from the ECS

Types

type BaseEntity

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

func NewEntity

func NewEntity(counter *atomic.Uint64) (this *BaseEntity)

func (*BaseEntity) AddComponent

func (this *BaseEntity) AddComponent(component any)

func (*BaseEntity) AddComponents

func (this *BaseEntity) AddComponents(component ...any)

func (*BaseEntity) GetComponents

func (this *BaseEntity) GetComponents() []any

func (*BaseEntity) Id

func (this *BaseEntity) Id() uint64

func (*BaseEntity) MarshalJSON

func (this *BaseEntity) MarshalJSON() ([]byte, error)

MarshalJSON to expose the id without exporting it, as it may not be altered!

type Component

type Component interface {
}

type ComponentStorage

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

func NewComponentStorage

func NewComponentStorage(ecs *ECS) (this *ComponentStorage)

func (*ComponentStorage) AddComponent

func (this *ComponentStorage) AddComponent(e Entity, components ...any)

AddComponent stores the given components

func (*ComponentStorage) Clear

func (this *ComponentStorage) Clear()

func (*ComponentStorage) GetComponents

func (this *ComponentStorage) GetComponents(componentType any) map[uint64]interface{}

GetComponents by given type

func (*ComponentStorage) RemoveComponent

func (this *ComponentStorage) RemoveComponent(e Entity, components ...any)

RemoveComponent deletes the given components from their respective types and entity

type ECS

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

func New

func New() (this *ECS)

New creates a new synchronous ecs world

func NewParallel added in v0.3.0

func NewParallel() (this *ECS)

NewParallel creates a new asynchronous ecs world (costs, do not use if you don't need it)

func (*ECS) AddContext

func (this *ECS) AddContext(c any) *ECS

AddContext attaches any service, map or other interfaces to this ECS (there can only be one per type)

func (*ECS) AddEntity

func (this *ECS) AddEntity(e any) Entity

AddEntity via reflection of embedded structs

func (*ECS) AddSystem

func (this *ECS) AddSystem(s System, types ...any) *ECS

AddSystem attaches the given system to this ECS under the given types

func (*ECS) Clear

func (this *ECS) Clear()

Clear nils all entities from this world

func (*ECS) CreateEntity

func (this *ECS) CreateEntity(components ...any) Entity

CreateEntity scaffolds a new entity with the given components

func (*ECS) GetComponents

func (this *ECS) GetComponents(componentType any) map[uint64]interface{}

GetComponents by given type

func (*ECS) GetContext

func (this *ECS) GetContext(c any) any

GetContext returns a context from the ECS (there can only be one per type)

func (*ECS) GetEntity

func (this *ECS) GetEntity(id uint64) Entity

GetEntity returns the id referenced entity of this ECS

func (*ECS) RemoveEntity

func (this *ECS) RemoveEntity(id uint64)

RemoveEntity marks an entity for deletion in the next iteration, to not affect the current run

func (*ECS) RemoveEntityNow

func (this *ECS) RemoveEntityNow(id uint64)

RemoveEntityNow detaches the entity now, no matter of more systems are running

func (*ECS) RemoveSystem

func (this *ECS) RemoveSystem(s System) *ECS

RemoveSystem deletes the given system from this ECS

func (*ECS) Update

func (this *ECS) Update(dt time.Duration) *ECS

Update calls all systems to run and do their stuff

type Entity

type Entity interface {
	Id() uint64
	GetComponents() []any
}

type EntitySystem

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

func (*EntitySystem) AttachEntity

func (this *EntitySystem) AttachEntity(e Entity)

func (*EntitySystem) DetachEntity

func (this *EntitySystem) DetachEntity(e Entity)

func (*EntitySystem) Entities

func (this *EntitySystem) Entities() []uint64

func (*EntitySystem) Priority

func (this *EntitySystem) Priority() int

Priority assigns this system importance - higher=better

type System

type System interface {
	Run(ecs *ECS, dt time.Duration)
	AttachEntity(e Entity)
	DetachEntity(e Entity)
	Priority() int
}

type SystemStorage

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

func NewParallelSystemStorage added in v0.3.0

func NewParallelSystemStorage(ecs *ECS) (this *SystemStorage)

NewParallelSystemStorage creates a parallel-systems tracking storage (costs, do not use if you don't need it)

func NewSystemStorage

func NewSystemStorage(ecs *ECS, parallel bool) (this *SystemStorage)

func (*SystemStorage) AddSystem

func (this *SystemStorage) AddSystem(system System, types ...any) []reflect.Type

AddSystem stores the given system under every type to this storage

func (*SystemStorage) All

func (this *SystemStorage) All() []System

All returns all systems

func (*SystemStorage) AllParallel added in v0.3.0

func (this *SystemStorage) AllParallel() [][]System

AllParallel returns all systems grouped by parallelity

func (*SystemStorage) Clear

func (this *SystemStorage) Clear()

Clear nils all systems

func (*SystemStorage) QuerySystems

func (this *SystemStorage) QuerySystems(types ...any) []System

QuerySystems returns all systems matching all given types connotations

func (*SystemStorage) RemoveSystem

func (this *SystemStorage) RemoveSystem(system System)

RemoveSystem slices the given system out of every type from this storage

Jump to

Keyboard shortcuts

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