ecs

package module
v2.0.5 Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2022 License: MIT Imports: 1 Imported by: 44

README

GoEcs

An implementation of the ECS paradigm in Go.

Usage

package main

import (
	"fmt"

	ecs "github.com/x-hgg-x/goecs"
)

func main() {
	// List of component data types
	type Shape struct{ shape string }
	type Color struct{ color string }
	type Name struct{ name string }

	// Structure for storing components
	// Several storage types are possible
	components := struct {
		Flag  *ecs.NullComponent
		Shape *ecs.SliceComponent
		Color *ecs.DenseSliceComponent
		Name  *ecs.MapComponent
		Value *ecs.SliceComponent
	}{}

	// Initialize a new manager
	manager := ecs.NewManager()

	// Create components
	components.Flag = manager.NewNullComponent()
	components.Shape = manager.NewSliceComponent()
	components.Color = manager.NewDenseSliceComponent()
	components.Name = manager.NewMapComponent()
	components.Value = manager.NewSliceComponent()

	// Create entities
	manager.NewEntity().AddComponent(components.Shape, &Shape{"square"}).AddComponent(components.Color, &Color{"red"})
	manager.NewEntity().AddComponent(components.Shape, &Shape{"circle"}).AddComponent(components.Name, &Name{"tom"}).AddComponent(components.Flag, nil)
	manager.NewEntity().AddComponent(components.Color, &Color{"blue"}).AddComponent(components.Name, &Name{"john"})

	manager.NewEntity().
		AddComponent(components.Shape, &Shape{"triangle"}).
		AddComponent(components.Color, &Color{"green"}).
		AddComponent(components.Name, &Name{"paul"}).
		AddComponent(components.Flag, nil)

	// Loop on entities which have specified components
	// The Join() method gives a bit.Set tag containing integers which can be converted to entities,
	// and we use the bit.Set.Visit() method to loop through the set.
	// The decorator ecs.Visit() is used when we want to iterate through all elements of the set.
	// It converts each set element to an entity.
	manager.Join(components.Shape, components.Name).Visit(ecs.Visit(func(entity ecs.Entity) {
		shape := components.Shape.Get(entity).(*Shape)
		name := components.Name.Get(entity).(*Name)
		fmt.Printf("Entity has the shape '%s' and the name '%s'\n", shape.shape, name.name)
	}))
	fmt.Println()

	// If we want to break the loop when some condition is met, we use the Visit() method directly
	aborted := manager.Join(components.Shape).Visit(func(index int) (skip bool) {
		shape := components.Shape.Get(ecs.Entity(index)).(*Shape)
		fmt.Printf("Entity has the shape '%s'\n", shape.shape)
		if shape.shape == "circle" {
			shape.shape = "CIRCLE"
			fmt.Printf("Entity has now the shape '%s'\n", shape.shape)
			return true
		}
		return false
	})
	fmt.Printf("Loop aborted: %v\n\n", aborted)

	// The helper function ecs.GetFirst() is useful if we want only the first entity matching a tag
	if firstEntity := ecs.GetFirst(manager.Join(components.Shape, components.Color, components.Name)); firstEntity != nil {
		shape := components.Shape.Get(*firstEntity).(*Shape)
		color := components.Color.Get(*firstEntity).(*Color)
		name := components.Name.Get(*firstEntity).(*Name)
		fmt.Printf("First matching entity has the shape '%s', the color '%s' and the name '%s'\n", shape.shape, color.color, name.name)
	}
	fmt.Println()

	// The Not() method is used when we want to exclude a particular component
	manager.Join(components.Flag.Not()).Visit(ecs.Visit(func(entity ecs.Entity) {
		fmt.Printf("Entity components: ")
		if entity.HasComponent(components.Shape) {
			fmt.Printf("Shape: '%s', ", components.Shape.Get(entity).(*Shape).shape)
		}
		if entity.HasComponent(components.Color) {
			fmt.Printf("Color: '%s', ", components.Color.Get(entity).(*Color).color)
		}
		if entity.HasComponent(components.Name) {
			fmt.Printf("Name: '%s'", components.Name.Get(entity).(*Name).name)
		}
		fmt.Println()
	}))
	fmt.Println()

	// To iterate through all entities with at least one component, we use the Join() method without any argument
	manager.Join().Visit(ecs.Visit(func(entity ecs.Entity) {
		fmt.Printf("Entity components: ")
		if entity.HasComponent(components.Shape) {
			fmt.Printf("Shape: '%s', ", components.Shape.Get(entity).(*Shape).shape)
		}
		if entity.HasComponent(components.Color) {
			fmt.Printf("Color: '%s', ", components.Color.Get(entity).(*Color).color)
		}
		if entity.HasComponent(components.Name) {
			fmt.Printf("Name: '%s'", components.Name.Get(entity).(*Name).name)
		}
		fmt.Println()
	}))
	fmt.Println()

	// If the component data is not a pointer, we can use the Set() method to change its value
	manager.NewEntity().AddComponent(components.Value, 3)
	firstEntity := *ecs.GetFirst(manager.Join(components.Value))
	fmt.Println("Old value:", components.Value.Get(firstEntity).(int))
	components.Value.Set(firstEntity, 4)
	fmt.Println("New value:", components.Value.Get(firstEntity).(int))
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Visit

func Visit(f func(entity Entity)) func(index int) bool

Visit is a decorator function for bit.Set.Visit() method

Types

type AntiComponent

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

AntiComponent is an inverted component used for filtering entities that don't have a component

type DataComponent

type DataComponent interface {
	Get(Entity) interface{}
	Set(Entity, interface{})
	Not() *AntiComponent
	// contains filtered or unexported methods
}

DataComponent is a component with data

type DenseSliceComponent

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

DenseSliceComponent uses a 2-way redirection table between entities and components, allowing to leave no gaps in the data slice. Consumes less memory if component is a big struct.

func (*DenseSliceComponent) Get

func (c *DenseSliceComponent) Get(entity Entity) interface{}

Get returns data corresponding to entity

func (*DenseSliceComponent) Not

func (c *DenseSliceComponent) Not() *AntiComponent

Not returns an inverted component used for filtering entities that don't have the component

func (*DenseSliceComponent) Set

func (c *DenseSliceComponent) Set(entity Entity, data interface{})

Set sets data corresponding to entity, or does nothing if the entity does not have the component

type Entity

type Entity int

Entity is an index

func GetFirst

func GetFirst(tag *bit.Set) *Entity

GetFirst returns a reference to the first entity matching a tag or nil if there are none

func (Entity) AddComponent

func (entity Entity) AddComponent(component DataComponent, data interface{}) Entity

AddComponent adds entity for component

func (Entity) HasComponent

func (entity Entity) HasComponent(component DataComponent) bool

HasComponent checks if component has entity

func (Entity) RemoveComponent

func (entity Entity) RemoveComponent(component DataComponent) Entity

RemoveComponent removes entity for component

type Manager

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

Manager manages components and entities

func NewManager

func NewManager() *Manager

NewManager creates a new manager

func (*Manager) DeleteAllEntities

func (manager *Manager) DeleteAllEntities()

DeleteAllEntities removes all entities for all components and reset current entity index

func (*Manager) DeleteEntities

func (manager *Manager) DeleteEntities(entities ...Entity)

DeleteEntities removes entities for all associated components

func (*Manager) DeleteEntity

func (manager *Manager) DeleteEntity(entity Entity)

DeleteEntity removes entity for all associated components

func (*Manager) Join

func (manager *Manager) Join(components ...joinable) *bit.Set

Join returns tag describing intersection of components

func (*Manager) Maintain

func (manager *Manager) Maintain(minEntities int, maxFillRatio float64)

Maintain reorders component storage to eliminate gaps and reduce memory usage. It invalidates all previous entities.

func (*Manager) NewDenseSliceComponent

func (manager *Manager) NewDenseSliceComponent() *DenseSliceComponent

NewDenseSliceComponent creates a new DenseSliceComponent

func (*Manager) NewEntity

func (manager *Manager) NewEntity() Entity

NewEntity creates a new entity

func (*Manager) NewMapComponent

func (manager *Manager) NewMapComponent() *MapComponent

NewMapComponent creates a new MapComponent

func (*Manager) NewNullComponent

func (manager *Manager) NewNullComponent() *NullComponent

NewNullComponent creates a new NullComponent

func (*Manager) NewSliceComponent

func (manager *Manager) NewSliceComponent() *SliceComponent

NewSliceComponent creates a new SliceComponent

type MapComponent

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

MapComponent uses a map for storing data. Useful for rarely used components.

func (*MapComponent) Get

func (c *MapComponent) Get(entity Entity) interface{}

Get returns data corresponding to entity

func (*MapComponent) Not

func (c *MapComponent) Not() *AntiComponent

Not returns an inverted component used for filtering entities that don't have the component

func (*MapComponent) Set

func (c *MapComponent) Set(entity Entity, data interface{})

Set sets data corresponding to entity, or does nothing if the entity does not have the component

type NullComponent

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

NullComponent contains no data and works as a simple flag.

func (*NullComponent) Get

func (c *NullComponent) Get(entity Entity) interface{}

Get returns data corresponding to entity

func (*NullComponent) Not

func (c *NullComponent) Not() *AntiComponent

Not returns an inverted component used for filtering entities that don't have the component

func (*NullComponent) Set

func (c *NullComponent) Set(entity Entity, data interface{})

Set sets data corresponding to entity, or does nothing if the entity does not have the component

type SliceComponent

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

SliceComponent uses a slice for storing data. Useful for pointer components, for small struct components or for components mostly present in entities.

func (*SliceComponent) Get

func (c *SliceComponent) Get(entity Entity) interface{}

Get returns data corresponding to entity

func (*SliceComponent) Not

func (c *SliceComponent) Not() *AntiComponent

Not returns an inverted component used for filtering entities that don't have the component

func (*SliceComponent) Set

func (c *SliceComponent) Set(entity Entity, data interface{})

Set sets data corresponding to entity, or does nothing if the entity does not have the component

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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