Entity Component System framework for Ebitengine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
3.0 KiB

package gohan
import (
"math"
"testing"
"github.com/hajimehoshi/ebiten/v2"
)
type movementSystem struct {
Position *positionComponent
Velocity *velocityComponent
}
func (s *movementSystem) Update(e Entity) error {
s.Position.X, s.Position.Y = s.Position.X+s.Velocity.X, s.Position.Y+s.Velocity.Y
return nil
}
func (s *movementSystem) Draw(e Entity, screen *ebiten.Image) error {
return nil
}
func TestWorld(t *testing.T) {
const iterations = 1024
_, e, positionComponentID, velocityComponentID := newTestWorld()
entities := make([]Entity, iterations)
position := e.getComponent(positionComponentID).(*positionComponent)
velocity := e.getComponent(velocityComponentID).(*velocityComponent)
expectedX, expectedY := position.X+(velocity.X*iterations), position.Y+(velocity.Y*iterations)
for i := 0; i < iterations; i++ {
entities[i] = NewEntity()
if i > 0 {
if !entities[i-1].Remove() {
t.Errorf("failed to remove entity %d", entities[i-1])
}
}
err := Update()
if err != nil {
t.Fatal(err)
}
}
// Fetch component again to ensure consistency.
position = e.getComponent(positionComponentID).(*positionComponent)
if round(position.X) != round(expectedX) || round(position.Y) != round(expectedY) {
t.Errorf("failed to update system: expected position (%f,%f), got (%f,%f)", expectedX, expectedY, position.X, position.Y)
}
}
func BenchmarkUpdateWorldInactive(b *testing.B) {
newTestWorld()
b.StopTimer()
b.ResetTimer()
b.ReportAllocs()
b.StartTimer()
for i := 0; i < b.N; i++ {
err := Update()
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkUpdateWorldActive(b *testing.B) {
newTestWorld()
b.StopTimer()
b.ResetTimer()
b.ReportAllocs()
b.StartTimer()
for i := 0; i < b.N; i++ {
e := NewEntity()
if !e.Remove() {
b.Errorf("failed to remove entity %d", e)
}
err := Update()
if err != nil {
b.Fatal(err)
}
}
}
func newTestWorld() (w *world, firstEntity Entity, positionComponentID componentID, velocityComponentID componentID) {
Reset()
movement := &movementSystem{}
AddSystem(movement)
const testEntities = 4096
for i := 0; i < testEntities; i++ {
ent := NewEntity()
if i == 0 {
firstEntity = ent
}
position := &positionComponent{
componentID: positionComponentID,
X: 108,
Y: 0,
}
ent.AddComponent(position)
positionComponentID = componentID(1)
velocity := &velocityComponent{
componentID: velocityComponentID,
X: -0.1,
Y: 0.2,
}
ent.AddComponent(velocity)
velocityComponentID = componentID(2)
}
return w, firstEntity, positionComponentID, velocityComponentID
}
// Round values to eliminate floating point precision errors. This is only
// necessary during testing because we validate the final values.
func round(f float64) float64 {
return math.Round(f*10) / 10
}
// Note: Because drawing a System is functionally the same as updating a System,
// as only an extra argument is passed, there are no drawing tests or benchmarks.