Browse Source
These functions return information particularly useful when debugging and profiling an application.main
19 changed files with 407 additions and 78 deletions
@ -1,4 +1,34 @@
@@ -1,4 +1,34 @@
|
||||
/* |
||||
Package gohan provides an Entity Component System framework for Ebiten. |
||||
|
||||
Entity |
||||
|
||||
A general-purpose object, which consists of a unique ID, starting with 1. |
||||
|
||||
Component |
||||
|
||||
The raw data for one aspect of an object, and how it interacts with the world. |
||||
Each component is assigned a unique ID, starting with 1. |
||||
|
||||
System |
||||
|
||||
Each system runs continuously, performing actions on every Entity that fits |
||||
each systems' set of required matching components. |
||||
|
||||
Component Design Guidelines |
||||
|
||||
Components are located in a separate package, typically named component. They |
||||
should be public (start with an uppercase letter) and may have any number of |
||||
publicly accessible data fields. They should not have any logic (i.e. game code) |
||||
within them, as all logic should be implemented within a System. |
||||
|
||||
System Design Guidelines |
||||
|
||||
Systems are located in a separate package, typically named system. They should |
||||
be private (start with a lowercase letter) and offer an instantiation function |
||||
named as follows: NewSystemNameHere(). Data should be stored within components |
||||
attached to one or more entities, rather than within the systems themselves. |
||||
References to components must not be maintained outside each Update and Draw |
||||
call, or else the application will encounter race conditions. |
||||
*/ |
||||
package gohan |
||||
|
@ -1,13 +1,61 @@
@@ -1,13 +1,61 @@
|
||||
package gohan |
||||
|
||||
import ( |
||||
"sync" |
||||
"time" |
||||
) |
||||
|
||||
// EntityID is an entity identifier.
|
||||
type EntityID int |
||||
|
||||
var nextEntityID EntityID |
||||
|
||||
var entityMutex sync.Mutex |
||||
|
||||
// NextEntityID returns the next available EntityID.
|
||||
func NextEntityID() EntityID { |
||||
entityID := nextEntityID |
||||
entityMutex.Lock() |
||||
defer entityMutex.Unlock() |
||||
|
||||
nextEntityID++ |
||||
return entityID |
||||
allEntities = append(allEntities, nextEntityID) |
||||
return nextEntityID |
||||
} |
||||
|
||||
// RemoveEntity removes the provided Entity, and all of its components.
|
||||
func RemoveEntity(entity EntityID) { |
||||
entityMutex.Lock() |
||||
defer entityMutex.Unlock() |
||||
|
||||
for i, e := range allEntities { |
||||
if e == entity { |
||||
allEntities = append(allEntities[:i], allEntities[i+1:]...) |
||||
removedEntities = append(removedEntities, e) |
||||
return |
||||
} |
||||
} |
||||
} |
||||
|
||||
func wasRemoved(entity EntityID) bool { |
||||
entityMutex.Lock() |
||||
defer entityMutex.Unlock() |
||||
|
||||
for _, e := range allEntities { |
||||
if e == entity { |
||||
return false |
||||
} |
||||
} |
||||
return true |
||||
} |
||||
|
||||
var numEntities int |
||||
var numEntitiesT time.Time |
||||
|
||||
// ActiveEntities returns the number of currently active entities.
|
||||
func ActiveEntities() int { |
||||
if time.Since(numEntitiesT) >= time.Second { |
||||
numEntities = len(allEntities) |
||||
numEntitiesT = time.Now() |
||||
} |
||||
return numEntities |
||||
} |
||||
|
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
//go:build example
|
||||
// +build example
|
||||
|
||||
package system |
||||
|
||||
import ( |
||||
"log" |
||||
"os" |
||||
"path" |
||||
"runtime/pprof" |
||||
|
||||
"code.rocketnine.space/tslocum/gohan" |
||||
"github.com/hajimehoshi/ebiten/v2" |
||||
"github.com/hajimehoshi/ebiten/v2/inpututil" |
||||
) |
||||
|
||||
type profileSystem struct { |
||||
player gohan.EntityID |
||||
cpuProfile *os.File |
||||
} |
||||
|
||||
func NewProfileSystem(player gohan.EntityID) *profileSystem { |
||||
return &profileSystem{ |
||||
player: player, |
||||
} |
||||
} |
||||
|
||||
func (s *profileSystem) Matches(e gohan.EntityID) bool { |
||||
return e == s.player |
||||
} |
||||
|
||||
func (s *profileSystem) Update(e gohan.EntityID) error { |
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyP) { |
||||
if s.cpuProfile == nil { |
||||
log.Println("CPU profiling started...") |
||||
|
||||
homeDir, err := os.UserHomeDir() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
s.cpuProfile, err = os.Create(path.Join(homeDir, "gohan.prof")) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
err = pprof.StartCPUProfile(s.cpuProfile) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} else { |
||||
pprof.StopCPUProfile() |
||||
|
||||
s.cpuProfile.Close() |
||||
s.cpuProfile = nil |
||||
|
||||
log.Println("CPU profiling stopped") |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (s *profileSystem) Draw(_ gohan.EntityID, _ *ebiten.Image) error { |
||||
return gohan.ErrSystemWithoutDraw |
||||
} |
Loading…
Reference in new issue