Files
aego/tests/integration/platformer_shadow_outline_test.go
sanspie 5eb1832907
ci-platforms / darwin amd64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 15m28s
ci / release (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci / checks (push) Successful in 7m47s
Complete Stage 12 for v0.12.0
2026-11-05 11:46:53 +03:00

114 lines
3.3 KiB
Go

package integration
import (
"fmt"
"image/png"
"os"
"testing"
"aego/asset/silhouette"
"aego/core/ids"
"aego/core/mathx"
"aego/modules/physics/components"
"aego/modules/render2d"
"aego/scene"
)
func TestPlatformerCharacterShadowOutlines(t *testing.T) {
pf := openPlatformer(t)
s := pf.app.Scene
pf.step(1)
resolver, ok := s.Resolver().(interface {
SpriteShadow(ids.AssetID) (silhouette.Shape, bool)
})
if !ok {
t.Fatal("platformer resolver cannot provide frame alpha shadows")
}
for _, name := range []string{"Player", "EnemyLeft", "EnemyRight", "Enemy3", "FastEnemy4", "Enemy5", "FastEnemy5"} {
t.Run(name, func(t *testing.T) {
h := s.Find(s.Root(), name)
occluder, ok := scene.Get[render2d.LightOccluder2D](s, h)
if !ok || occluder.Source != render2d.OccluderSpriteAlpha || occluder.Layers != 2 || occluder.Shape != "" {
t.Fatalf("expected sprite-frame alpha shadow, got %+v", occluder)
}
sprite, ok := scene.Get[render2d.SpriteRenderer](s, h)
if !ok || sprite.Sprite.ID.IsZero() {
t.Fatalf("missing current sprite frame: %+v", sprite)
}
if shape, ok := resolver.SpriteShadow(sprite.Sprite.ID); !ok || shape.W <= 0 || shape.H <= 0 || len(shape.Parts) == 0 {
t.Fatalf("current sprite frame did not resolve to alpha shadow geometry: %+v", shape)
}
if name != "Player" {
box, ok := scene.Get[components.BoxCollider2D](s, h)
if !ok || box.Size != mathx.V2(.9, .9) {
t.Fatalf("enemy physics box changed: %+v", box)
}
}
})
}
shape := s.Find(pf.player, "Shape")
capsule, ok := scene.Get[components.CapsuleCollider2D](s, shape)
if !ok || capsule.Length != 1 || capsule.Radius != .3 {
t.Fatalf("player physics capsule changed: %+v", capsule)
}
}
func TestPlatformerRunnerAlphaShadowEveryFrame(t *testing.T) {
pf := openPlatformer(t)
f, err := os.Open(pf.app.Pipeline.Abs("assets/surfaces/runner.png"))
if err != nil {
t.Fatal(err)
}
defer f.Close()
image, err := png.Decode(f)
if err != nil {
t.Fatal(err)
}
if image.Bounds().Dx() != 128 || image.Bounds().Dy() != 16 {
t.Fatalf("unexpected runner sheet size %v", image.Bounds())
}
unique := map[string]bool{}
for frame := 0; frame < 8; frame++ {
id, ok := pf.app.Pipeline.Lookup(fmt.Sprintf("assets/surfaces/runner.png#runner_%03d", frame))
if !ok {
t.Fatalf("runner frame %d missing from imported assets", frame)
}
shape, ok := pf.app.Loader.SpriteShadow(id)
if !ok || shape.W != 16 || shape.H != 16 {
t.Fatalf("runner frame %d has no prepared alpha shape: %+v", frame, shape)
}
unique[fmt.Sprint(shape.Parts)] = true
for _, flip := range []bool{false, true} {
for y := 0; y < 16; y++ {
for x := 0; x < 16; x++ {
coverage := 0
for _, r := range shape.Parts {
left := r.X
if flip {
left = shape.W - r.X - r.W
}
if x >= left && x < left+r.W && y >= r.Y && y < r.Y+r.H {
coverage++
}
}
sourceX := x
if flip {
sourceX = 15 - x
}
_, _, _, alpha := image.At(frame*16+sourceX, y).RGBA()
want := 0
if alpha>>8 >= 128 {
want = 1
}
if coverage != want {
t.Fatalf("frame %d flip=%v pixel %d,%d: coverage=%d, want=%d", frame, flip, x, y, coverage, want)
}
}
}
}
}
if len(unique) < 2 {
t.Fatal("all animation frames produced the same shadow shape")
}
}