2021-12-06 01:17:15 +00:00
|
|
|
package gohan
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
2021-12-08 03:20:36 +00:00
|
|
|
"reflect"
|
2021-12-07 05:02:33 +00:00
|
|
|
"strconv"
|
2021-12-06 01:17:15 +00:00
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
)
|
|
|
|
|
2021-12-08 03:20:36 +00:00
|
|
|
var debug int
|
2021-12-06 01:17:15 +00:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
debugEnv := os.Getenv("GOHAN_DEBUG")
|
|
|
|
debugEnv = strings.TrimSpace(debugEnv)
|
|
|
|
debugEnv = strings.ToLower(debugEnv)
|
|
|
|
|
2021-12-08 03:20:36 +00:00
|
|
|
i, err := strconv.Atoi(debugEnv)
|
|
|
|
if err == nil {
|
|
|
|
debug = i
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if debugEnv == "t" || debugEnv == "y" || debugEnv == "on" || debugEnv == "yes" || debugEnv == "true" {
|
|
|
|
debug = 1
|
|
|
|
}
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
var w = newWorld()
|
|
|
|
|
|
|
|
// world represents a collection of AllEntities, components and Systems.
|
|
|
|
type world struct {
|
2021-12-06 01:17:15 +00:00
|
|
|
maxEntityID Entity
|
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
maxComponentID componentID
|
2021-12-06 01:17:15 +00:00
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
components [][]interface{} // components[Entity][componentID]Component
|
2021-12-06 01:17:15 +00:00
|
|
|
|
|
|
|
allEntities []Entity
|
|
|
|
|
|
|
|
modifiedEntities []Entity
|
|
|
|
removedEntities []Entity
|
|
|
|
|
2021-12-08 03:20:36 +00:00
|
|
|
handledModifiedEntities map[Entity]bool
|
|
|
|
|
|
|
|
// availableEntities is the set of EntityIDs available because they were
|
2021-12-06 01:17:15 +00:00
|
|
|
// removed from the game.
|
2021-12-08 03:20:36 +00:00
|
|
|
availableEntities []Entity
|
2021-12-06 01:17:15 +00:00
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
systems []System
|
|
|
|
systemEntities [][]Entity // Slice of entities matching each system.
|
|
|
|
systemNeeds [][]componentID // Slice of ComponentIDs needed by each system.
|
|
|
|
systemUses [][]componentID // Slice of ComponentIDs used by each system.
|
|
|
|
systemComponentIDs [][]componentID // Slice of ComponentIDs needed or used by each system.
|
|
|
|
systemComponentFields [][]reflect.Value // Slice of component struct fields used by each system.
|
2021-12-06 01:17:15 +00:00
|
|
|
|
|
|
|
systemReceivesUpdate []bool
|
|
|
|
systemReceivesDraw []bool
|
|
|
|
|
|
|
|
systemUpdatedEntities int
|
|
|
|
systemUpdatedEntitiesV int
|
|
|
|
systemUpdatedEntitiesT time.Time
|
|
|
|
|
|
|
|
systemDrawnEntities int
|
|
|
|
systemDrawnEntitiesV int
|
|
|
|
systemDrawnEntitiesT time.Time
|
|
|
|
|
2021-12-12 05:04:11 +00:00
|
|
|
systemComponentNames []string
|
2022-02-01 04:07:55 +00:00
|
|
|
haveSystemComponentName map[string]bool
|
2021-12-12 05:04:11 +00:00
|
|
|
|
2021-12-06 01:17:15 +00:00
|
|
|
cacheTime time.Duration
|
|
|
|
|
2021-12-10 03:39:21 +00:00
|
|
|
entityMutex sync.Mutex
|
|
|
|
componentMutex sync.Mutex
|
|
|
|
|
2021-12-06 01:17:15 +00:00
|
|
|
sync.Mutex
|
|
|
|
}
|
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
// NewWorld returns a new world.
|
|
|
|
func newWorld() *world {
|
|
|
|
w := &world{
|
2021-12-06 01:17:15 +00:00
|
|
|
cacheTime: time.Second,
|
2021-12-08 03:20:36 +00:00
|
|
|
|
|
|
|
handledModifiedEntities: make(map[Entity]bool),
|
2022-02-01 04:07:55 +00:00
|
|
|
haveSystemComponentName: make(map[string]bool),
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pad slices to match IDs starting with 1.
|
|
|
|
w.components = append(w.components, nil)
|
2021-12-12 05:04:11 +00:00
|
|
|
w.systemComponentNames = append(w.systemComponentNames, "")
|
2021-12-06 01:17:15 +00:00
|
|
|
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddSystem registers a system to start receiving Update and Draw calls.
|
2022-02-01 04:07:55 +00:00
|
|
|
func AddSystem(system System) {
|
2021-12-06 01:17:15 +00:00
|
|
|
w.Lock()
|
|
|
|
defer w.Unlock()
|
|
|
|
|
2021-12-10 03:39:21 +00:00
|
|
|
systemIndex := len(w.systems)
|
|
|
|
|
2021-12-06 01:17:15 +00:00
|
|
|
w.systems = append(w.systems, system)
|
|
|
|
w.systemReceivesUpdate = append(w.systemReceivesUpdate, true)
|
|
|
|
w.systemReceivesDraw = append(w.systemReceivesDraw, true)
|
|
|
|
w.systemEntities = append(w.systemEntities, nil)
|
2022-02-01 04:07:55 +00:00
|
|
|
w.systemComponentFields = append(w.systemComponentFields, nil)
|
2021-12-06 01:17:15 +00:00
|
|
|
|
2021-12-10 03:39:21 +00:00
|
|
|
w.entityMutex.Lock()
|
|
|
|
defer w.entityMutex.Unlock()
|
|
|
|
w.modifiedEntities = append(w.modifiedEntities, w.allEntities...)
|
2022-02-01 04:07:55 +00:00
|
|
|
|
|
|
|
sV := reflect.ValueOf(system)
|
|
|
|
sT := reflect.TypeOf(system)
|
|
|
|
if sV.Kind() == reflect.Ptr {
|
|
|
|
sV = sV.Elem()
|
|
|
|
sT = sT.Elem()
|
|
|
|
}
|
|
|
|
if sV.Kind() != reflect.Struct {
|
|
|
|
panic("system must be a struct type")
|
|
|
|
}
|
|
|
|
|
|
|
|
var usedComponentIDs []componentID
|
|
|
|
var neededComponentIDs []componentID
|
|
|
|
w.systemComponentIDs = append(w.systemComponentIDs, nil)
|
|
|
|
for i := 0; i < sT.NumField(); i++ {
|
|
|
|
field := sV.Field(i)
|
|
|
|
|
|
|
|
if !field.CanSet() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
tag := sT.Field(i).Tag.Get("gohan")
|
|
|
|
if tag == "-" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
//log.Println("SET FIELD", systemIndex, field.String(), tag, field.CanSet())
|
|
|
|
w.systemComponentFields[systemIndex] = append(w.systemComponentFields[systemIndex], field)
|
|
|
|
|
|
|
|
id := componentIDByName(field.Type().String())
|
|
|
|
if tag == "?" {
|
|
|
|
usedComponentIDs = append(usedComponentIDs, id)
|
|
|
|
} else {
|
|
|
|
neededComponentIDs = append(neededComponentIDs, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.systemComponentIDs[systemIndex] = append(w.systemComponentIDs[systemIndex], id)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.systemNeeds = append(w.systemNeeds, neededComponentIDs)
|
|
|
|
w.systemUses = append(w.systemUses, usedComponentIDs)
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
// AddSystemAfter registers a system to start receiving Update and Draw calls
|
|
|
|
// after the specified system (or systems) are called first.
|
|
|
|
func AddSystemAfter(system System, after ...System) {
|
|
|
|
gameSystems = append(gameSystems, system)
|
|
|
|
gameSystemReceivesUpdate = append(gameSystemReceivesUpdate, true)
|
|
|
|
gameSystemReceivesDraw = append(gameSystemReceivesDraw, true)
|
|
|
|
gameSystemEntities = append(gameSystemEntities, nil)
|
|
|
|
|
|
|
|
attachEntitiesToSystem(system)
|
|
|
|
}
|
|
|
|
*/
|
2021-12-08 03:20:36 +00:00
|
|
|
|
2022-06-11 08:05:47 +00:00
|
|
|
func (w *world) setSystemComponentFields(e Entity, i int) {
|
2022-02-01 04:07:55 +00:00
|
|
|
//log.Println(len(w.systemComponentFields[i]))
|
|
|
|
//log.Println(w.systemComponentFields[i])
|
|
|
|
for j, field := range w.systemComponentFields[i] {
|
|
|
|
//log.Println(j, field, field.String())
|
|
|
|
id := w.systemComponentIDs[i][j]
|
|
|
|
//log.Println("SYSTEM", i, "FIELD", j, "ID", id)
|
2022-06-11 08:05:47 +00:00
|
|
|
if w.components[e][id] == nil {
|
2022-02-01 04:07:55 +00:00
|
|
|
field.Set(reflect.Zero(field.Type()))
|
|
|
|
} else {
|
2022-06-11 08:05:47 +00:00
|
|
|
field.Set(reflect.ValueOf(w.components[e][id]))
|
2022-02-01 04:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *world) updateSystem(i int) (int, error) {
|
2022-06-13 09:36:37 +00:00
|
|
|
w.propagateEntityChanges()
|
|
|
|
|
2021-12-06 01:17:15 +00:00
|
|
|
updated := 0
|
|
|
|
for _, entity := range w.systemEntities[i] {
|
2022-06-11 08:05:47 +00:00
|
|
|
w.setSystemComponentFields(entity, i)
|
2022-02-01 04:07:55 +00:00
|
|
|
|
|
|
|
err := w.systems[i].Update(entity)
|
2021-12-06 01:17:15 +00:00
|
|
|
if err != nil {
|
2022-02-01 04:07:55 +00:00
|
|
|
if err == ErrUnregister {
|
2021-12-06 01:17:15 +00:00
|
|
|
// Unregister system from Update events.
|
|
|
|
w.systemReceivesUpdate[i] = false
|
|
|
|
return 0, nil
|
|
|
|
}
|
2021-12-08 03:20:36 +00:00
|
|
|
return 0, fmt.Errorf("failed to update %s for entity %d: %+v", w.systemName(i), entity, err)
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
updated++
|
|
|
|
}
|
|
|
|
return updated, nil
|
|
|
|
}
|
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
func (w *world) _handleRemovedEntities() {
|
2021-12-06 01:17:15 +00:00
|
|
|
for _, entity := range w.removedEntities {
|
|
|
|
// Remove from attached systems.
|
|
|
|
REMOVED:
|
|
|
|
for i := range w.systemEntities {
|
|
|
|
for j, e := range w.systemEntities[i] {
|
|
|
|
if e == entity {
|
2021-12-10 04:16:12 +00:00
|
|
|
w.systemEntities[i] = _removeAt(w.systemEntities[i], j)
|
2021-12-06 01:17:15 +00:00
|
|
|
continue REMOVED
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-08 03:20:36 +00:00
|
|
|
// Mark EntityID as available.
|
|
|
|
w.availableEntities = append(w.availableEntities, w.removedEntities...)
|
2021-12-06 01:17:15 +00:00
|
|
|
|
2021-12-08 03:20:36 +00:00
|
|
|
w.removedEntities = w.removedEntities[:0]
|
|
|
|
}
|
2021-12-06 01:17:15 +00:00
|
|
|
|
2021-12-10 03:39:21 +00:00
|
|
|
// _handleModifiedEntities handles changes to entity components by attaching
|
|
|
|
// and detaching modified entities from affected systems.
|
2022-02-01 04:07:55 +00:00
|
|
|
func (w *world) _handleModifiedEntities() {
|
2021-12-10 03:39:21 +00:00
|
|
|
if len(w.modifiedEntities) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-06 01:17:15 +00:00
|
|
|
for _, entity := range w.modifiedEntities {
|
2021-12-08 03:20:36 +00:00
|
|
|
if w.handledModifiedEntities[entity] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
w.handledModifiedEntities[entity] = true
|
|
|
|
|
2021-12-07 05:02:33 +00:00
|
|
|
for i := range w.systems {
|
2021-12-06 01:17:15 +00:00
|
|
|
systemEntityIndex := -1
|
|
|
|
for j, systemEntity := range w.systemEntities[i] {
|
|
|
|
if systemEntity == entity {
|
|
|
|
systemEntityIndex = j
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var skip bool
|
2021-12-10 03:39:21 +00:00
|
|
|
for _, componentID := range w.systemNeeds[i] {
|
2022-02-01 04:07:55 +00:00
|
|
|
c := entity.getComponent(componentID)
|
2021-12-10 03:39:21 +00:00
|
|
|
if c == nil {
|
2021-12-06 01:17:15 +00:00
|
|
|
skip = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !skip {
|
|
|
|
if systemEntityIndex != -1 {
|
|
|
|
// Already attached.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
w.systemEntities[i] = append(w.systemEntities[i], entity)
|
|
|
|
|
2021-12-08 03:20:36 +00:00
|
|
|
if debug > 1 {
|
|
|
|
log.Printf("Attached entity %d to %s.", entity, w.systemName(i))
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
} else if systemEntityIndex != -1 {
|
|
|
|
// Detach from system.
|
2021-12-10 04:16:12 +00:00
|
|
|
w.systemEntities[i] = _removeAt(w.systemEntities[i], systemEntityIndex)
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-12-08 03:20:36 +00:00
|
|
|
|
|
|
|
for k := range w.handledModifiedEntities {
|
|
|
|
delete(w.handledModifiedEntities, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.modifiedEntities = w.modifiedEntities[:0]
|
|
|
|
}
|
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
func (w *world) propagateEntityChanges() {
|
2021-12-10 03:39:21 +00:00
|
|
|
w.entityMutex.Lock()
|
|
|
|
defer w.entityMutex.Unlock()
|
2021-12-08 03:20:36 +00:00
|
|
|
|
|
|
|
w._handleRemovedEntities()
|
|
|
|
w._handleModifiedEntities()
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update updates the game state.
|
2022-02-01 04:07:55 +00:00
|
|
|
func Update() error {
|
2021-12-06 01:17:15 +00:00
|
|
|
w.Lock()
|
|
|
|
defer w.Unlock()
|
|
|
|
|
|
|
|
var t time.Time
|
2021-12-08 03:20:36 +00:00
|
|
|
if debug != 0 {
|
2021-12-06 01:17:15 +00:00
|
|
|
t = time.Now()
|
|
|
|
}
|
2021-12-08 03:20:36 +00:00
|
|
|
|
2021-12-06 01:17:15 +00:00
|
|
|
var entitiesUpdated int
|
|
|
|
for i, registered := range w.systemReceivesUpdate {
|
|
|
|
if !registered {
|
|
|
|
continue
|
|
|
|
}
|
2022-06-13 09:36:37 +00:00
|
|
|
|
2021-12-06 01:17:15 +00:00
|
|
|
updated, err := w.updateSystem(i)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
entitiesUpdated += updated
|
|
|
|
|
2021-12-08 03:20:36 +00:00
|
|
|
if debug != 0 {
|
2022-06-13 09:36:37 +00:00
|
|
|
log.Printf("- %s: %d updated in %.2fms.", w.systemName(i), updated, float64(time.Since(t).Microseconds())/1000)
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
w.systemUpdatedEntities = entitiesUpdated
|
|
|
|
|
2021-12-08 03:20:36 +00:00
|
|
|
if debug != 0 {
|
|
|
|
log.Printf("Handled %d entity updates in %.2fms.", entitiesUpdated, float64(time.Since(t).Microseconds())/1000)
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-09 03:47:34 +00:00
|
|
|
// CurrentUpdates returns the number of System Update calls required to update
|
|
|
|
// the game state. Because entities may be handled by more than one System,
|
|
|
|
// this number may be higher than the number of active entities.
|
2022-02-01 04:07:55 +00:00
|
|
|
func CurrentUpdates() int {
|
2021-12-06 01:17:15 +00:00
|
|
|
if time.Since(w.systemUpdatedEntitiesT) >= w.cacheTime {
|
|
|
|
w.systemUpdatedEntitiesV = w.systemUpdatedEntities
|
|
|
|
w.systemUpdatedEntitiesT = time.Now()
|
|
|
|
}
|
|
|
|
return w.systemUpdatedEntitiesV
|
|
|
|
}
|
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
func (w *world) drawSystem(i int, screen *ebiten.Image) (int, error) {
|
2022-06-13 09:36:37 +00:00
|
|
|
w.propagateEntityChanges()
|
|
|
|
|
2021-12-06 01:17:15 +00:00
|
|
|
var drawn int
|
|
|
|
for _, entity := range w.systemEntities[i] {
|
2022-06-11 08:05:47 +00:00
|
|
|
w.setSystemComponentFields(entity, i)
|
2022-02-01 04:07:55 +00:00
|
|
|
|
|
|
|
err := w.systems[i].Draw(entity, screen)
|
2021-12-06 01:17:15 +00:00
|
|
|
if err != nil {
|
2022-02-01 04:07:55 +00:00
|
|
|
if err == ErrUnregister {
|
2021-12-06 01:17:15 +00:00
|
|
|
// Unregister system from Draw events.
|
|
|
|
w.systemReceivesDraw[i] = false
|
|
|
|
return 0, nil
|
|
|
|
}
|
2021-12-08 03:20:36 +00:00
|
|
|
return 0, fmt.Errorf("failed to draw %s for entity %d: %+v", w.systemName(i), entity, err)
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
drawn++
|
|
|
|
}
|
|
|
|
return drawn, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw draws the game on to the screen.
|
2022-02-01 04:07:55 +00:00
|
|
|
func Draw(screen *ebiten.Image) error {
|
2021-12-06 01:17:15 +00:00
|
|
|
w.Lock()
|
|
|
|
defer w.Unlock()
|
|
|
|
|
2021-12-08 03:20:36 +00:00
|
|
|
var t time.Time
|
|
|
|
if debug != 0 {
|
|
|
|
t = time.Now()
|
|
|
|
}
|
|
|
|
|
2021-12-06 01:17:15 +00:00
|
|
|
var entitiesDrawn int
|
|
|
|
for i, registered := range w.systemReceivesDraw {
|
|
|
|
if !registered {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
drawn, err := w.drawSystem(i, screen)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
entitiesDrawn += drawn
|
2021-12-08 03:20:36 +00:00
|
|
|
|
|
|
|
if debug != 0 {
|
2022-06-13 09:36:37 +00:00
|
|
|
log.Printf("- %s: %d drawn in %.2fms.", w.systemName(i), drawn, float64(time.Since(t).Microseconds())/1000)
|
2021-12-08 03:20:36 +00:00
|
|
|
}
|
2021-12-06 01:17:15 +00:00
|
|
|
}
|
|
|
|
w.systemDrawnEntities = entitiesDrawn
|
2021-12-08 03:20:36 +00:00
|
|
|
|
|
|
|
if debug != 0 {
|
|
|
|
log.Printf("Handled %d entity draws in %.2fms.", entitiesDrawn, float64(time.Since(t).Microseconds())/1000)
|
|
|
|
}
|
2021-12-06 01:17:15 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-09 03:47:34 +00:00
|
|
|
// CurrentDraws returns the number of System Draw calls required to draw the
|
|
|
|
// game on to the screen. Because entities may be handled by more than one
|
|
|
|
// System, this number may be higher than the number of active entities.
|
2022-02-01 04:07:55 +00:00
|
|
|
func CurrentDraws() int {
|
2021-12-06 01:17:15 +00:00
|
|
|
if time.Since(w.systemDrawnEntitiesT) >= w.cacheTime {
|
|
|
|
w.systemDrawnEntitiesV = w.systemDrawnEntities
|
|
|
|
w.systemDrawnEntitiesT = time.Now()
|
|
|
|
}
|
|
|
|
return w.systemDrawnEntitiesV
|
|
|
|
}
|
2021-12-10 03:39:21 +00:00
|
|
|
|
2021-12-10 04:16:12 +00:00
|
|
|
// Preallocate creates and then immediately removes the specified number of entities.
|
2021-12-10 03:39:21 +00:00
|
|
|
// Because Gohan reuses removed entities, this has the effect of pre-allocating
|
|
|
|
// the memory used later to create entities normally. Pre-allocating enough
|
|
|
|
// entities to run your application after its systems has been added, but
|
|
|
|
// before any entities are created, will provide the greatest performance boost.
|
2022-02-01 04:07:55 +00:00
|
|
|
func Preallocate(entities int) {
|
2021-12-10 03:39:21 +00:00
|
|
|
if len(w.availableEntities) >= entities {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
e := make([]Entity, entities)
|
|
|
|
for i := 0; i < entities; i++ {
|
2022-02-01 04:07:55 +00:00
|
|
|
e[i] = NewEntity()
|
2021-12-10 03:39:21 +00:00
|
|
|
}
|
|
|
|
for i := 0; i < entities; i++ {
|
2022-02-01 04:07:55 +00:00
|
|
|
e[i].Remove()
|
2021-12-10 03:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-12 05:04:11 +00:00
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
func uniqueComponentIDs(v []componentID) []componentID {
|
|
|
|
var list []componentID
|
|
|
|
keys := make(map[componentID]bool)
|
2021-12-12 05:04:11 +00:00
|
|
|
for _, entry := range v {
|
|
|
|
if _, value := keys[entry]; !value {
|
|
|
|
keys[entry] = true
|
|
|
|
list = append(list, entry)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
func (w *world) componentName(id componentID) string {
|
2021-12-12 05:04:11 +00:00
|
|
|
if int(id) < len(w.systemComponentNames) {
|
|
|
|
return w.systemComponentNames[id]
|
|
|
|
}
|
|
|
|
return strconv.Itoa(int(id))
|
|
|
|
}
|
|
|
|
|
2022-02-01 04:07:55 +00:00
|
|
|
func (w *world) systemName(i int) string {
|
2021-12-12 05:04:11 +00:00
|
|
|
if i < len(w.systems) {
|
|
|
|
return getName(w.systems[i])
|
|
|
|
}
|
|
|
|
return strconv.Itoa(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getName(v interface{}) string {
|
|
|
|
t := reflect.TypeOf(v)
|
|
|
|
if t.Kind() == reflect.Ptr {
|
|
|
|
return strings.Title(t.Elem().Name())
|
|
|
|
} else if t.Kind() == reflect.Struct {
|
|
|
|
return strings.Title(t.Name())
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
2022-02-01 04:07:55 +00:00
|
|
|
|
|
|
|
// Reset removes all entities, components and systems.
|
|
|
|
func Reset() {
|
|
|
|
old := w
|
|
|
|
old.Lock()
|
|
|
|
|
|
|
|
w = newWorld()
|
|
|
|
|
|
|
|
old.Unlock()
|
|
|
|
}
|