117 lines
2.7 KiB
Go
117 lines
2.7 KiB
Go
package gohan
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
)
|
|
|
|
type movementSystem struct {
|
|
positionComponentID ComponentID
|
|
velocityComponentID ComponentID
|
|
}
|
|
|
|
func (s *movementSystem) Name() string {
|
|
return "Movement"
|
|
}
|
|
|
|
func (s *movementSystem) Needs() []ComponentID {
|
|
return []ComponentID{
|
|
s.positionComponentID,
|
|
s.velocityComponentID,
|
|
}
|
|
}
|
|
|
|
func (s *movementSystem) Uses() []ComponentID {
|
|
return nil
|
|
}
|
|
|
|
func (s *movementSystem) Update(ctx *Context) error {
|
|
position := ctx.Component(s.positionComponentID).(*positionComponent)
|
|
velocity := ctx.Component(s.velocityComponentID).(*velocityComponent)
|
|
|
|
position.X, position.Y = position.X+velocity.X, position.Y+velocity.Y
|
|
return nil
|
|
}
|
|
|
|
func (s *movementSystem) Draw(ctx *Context, screen *ebiten.Image) error {
|
|
return nil
|
|
}
|
|
|
|
func TestWorld(t *testing.T) {
|
|
const iterations = 1024
|
|
|
|
w, e, positionComponentID, velocityComponentID := newTestWorld()
|
|
|
|
position := w.Component(e, positionComponentID).(*positionComponent)
|
|
velocity := w.Component(e, velocityComponentID).(*velocityComponent)
|
|
|
|
expectedX, expectedY := position.X+(velocity.X*iterations), position.Y+(velocity.Y*iterations)
|
|
|
|
for i := 0; i < iterations; i++ {
|
|
err := w.Update()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Fetch component again to ensure consistency.
|
|
position = w.Component(e, 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 BenchmarkUpdateWorld(b *testing.B) {
|
|
w, _, _, _ := newTestWorld()
|
|
|
|
b.StopTimer()
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
b.StartTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
err := w.Update()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func newTestWorld() (w *World, e Entity, positionComponentID ComponentID, velocityComponentID ComponentID) {
|
|
w = NewWorld()
|
|
|
|
e = w.NewEntity()
|
|
|
|
positionComponentID = w.NewComponentID()
|
|
position := &positionComponent{
|
|
componentID: positionComponentID,
|
|
X: 108,
|
|
Y: 0,
|
|
}
|
|
w.AddComponent(e, position)
|
|
|
|
velocityComponentID = w.NewComponentID()
|
|
velocity := &velocityComponent{
|
|
componentID: velocityComponentID,
|
|
X: -0.1,
|
|
Y: 0.2,
|
|
}
|
|
w.AddComponent(e, velocity)
|
|
|
|
movement := &movementSystem{
|
|
positionComponentID: positionComponentID,
|
|
velocityComponentID: velocityComponentID,
|
|
}
|
|
w.AddSystem(movement)
|
|
|
|
return w, e, 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
|
|
}
|