Unit Tests for Collision Detection
This commit is contained in:
parent
f8a83cbad9
commit
b33d93599a
108
pkg/collision/actors_test.go
Normal file
108
pkg/collision/actors_test.go
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package collision_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.kirsle.net/apps/doodle/lib/render"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/collision"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestActorCollision(t *testing.T) {
|
||||||
|
boxes := []render.Rect{
|
||||||
|
// 0: intersects with 1
|
||||||
|
render.Rect{
|
||||||
|
X: 0,
|
||||||
|
Y: 0,
|
||||||
|
W: 100,
|
||||||
|
H: 100,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 1: intersects with 0
|
||||||
|
render.Rect{
|
||||||
|
X: 90,
|
||||||
|
Y: 10,
|
||||||
|
W: 100,
|
||||||
|
H: 100,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 2: no intersection
|
||||||
|
render.Rect{
|
||||||
|
X: 200,
|
||||||
|
Y: 200,
|
||||||
|
W: 32,
|
||||||
|
H: 32,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 3: intersects with 4
|
||||||
|
render.Rect{
|
||||||
|
X: 233,
|
||||||
|
Y: 200,
|
||||||
|
W: 32,
|
||||||
|
H: 32,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 4: intersects with 3
|
||||||
|
render.Rect{
|
||||||
|
X: 240,
|
||||||
|
Y: 200,
|
||||||
|
W: 32,
|
||||||
|
H: 32,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 5: completely contains 6 and intersects 7.
|
||||||
|
render.Rect{
|
||||||
|
X: 300,
|
||||||
|
Y: 300,
|
||||||
|
W: 1000,
|
||||||
|
H: 600,
|
||||||
|
},
|
||||||
|
render.Rect{
|
||||||
|
X: 450,
|
||||||
|
Y: 500,
|
||||||
|
W: 42,
|
||||||
|
H: 42,
|
||||||
|
},
|
||||||
|
render.Rect{
|
||||||
|
X: 1200,
|
||||||
|
Y: 350,
|
||||||
|
W: 512,
|
||||||
|
H: 512,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert := func(i int, result collision.IndexTuple, expectA, expectB int) {
|
||||||
|
if result[0] != expectA || result[1] != expectB {
|
||||||
|
t.Errorf(
|
||||||
|
"unexpected collision at index %d of BetweenBoxes() generator\n"+
|
||||||
|
"expected: (%d,%d)\n"+
|
||||||
|
" but got: (%d,%d)",
|
||||||
|
i,
|
||||||
|
expectA, expectB,
|
||||||
|
result[0], result[1],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var i int
|
||||||
|
for overlap := range collision.BetweenBoxes(boxes) {
|
||||||
|
a, b := overlap[0], overlap[1]
|
||||||
|
|
||||||
|
// Ensure expected collisions happened.
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
assert(i, overlap, 0, 1)
|
||||||
|
case 1:
|
||||||
|
assert(i, overlap, 3, 4)
|
||||||
|
case 2:
|
||||||
|
assert(i, overlap, 5, 6)
|
||||||
|
case 3:
|
||||||
|
assert(i, overlap, 5, 7)
|
||||||
|
default:
|
||||||
|
t.Errorf("got unexpected collision result, index %d, tuple (%d,%d)",
|
||||||
|
i, a, b,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
192
pkg/collision/level_test.go
Normal file
192
pkg/collision/level_test.go
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
package collision_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.kirsle.net/apps/doodle/lib/render"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/collision"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/doodads/dummy"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/level"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCollisionFunctions(t *testing.T) {
|
||||||
|
// Create a basic level for testing.
|
||||||
|
grid := level.NewChunker(1000)
|
||||||
|
solid := &level.Swatch{
|
||||||
|
Name: "solid",
|
||||||
|
Color: render.Black,
|
||||||
|
Solid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// with a solid platform at y=500 and x=0..1000
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
grid.Set(render.NewPoint(int32(i), 500), solid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// and a short wall in the middle of the platform
|
||||||
|
for i := 480; i < 500; i++ {
|
||||||
|
grid.Set(render.NewPoint(500, int32(i)), solid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a dummy player character.
|
||||||
|
player := dummy.NewPlayer()
|
||||||
|
playerSize := player.Size()
|
||||||
|
|
||||||
|
// Table based test schema.
|
||||||
|
type testCase struct {
|
||||||
|
Start render.Point
|
||||||
|
MoveTo render.Point
|
||||||
|
ExpectCollision bool
|
||||||
|
Expect *collision.Collide
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe the details of the test on failure.
|
||||||
|
describeTest := func(t testCase, result *collision.Collide, b bool) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
" Moving From: %s to %s\n"+
|
||||||
|
"Expected Collision: %+v (%+v)\n"+
|
||||||
|
" Got Collision: %+v (%+v)",
|
||||||
|
t.Start, t.MoveTo,
|
||||||
|
t.ExpectCollision, t.Expect,
|
||||||
|
b, result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test cases to check.
|
||||||
|
tests := []testCase{
|
||||||
|
testCase{
|
||||||
|
Start: render.NewPoint(0, 0),
|
||||||
|
MoveTo: render.NewPoint(8, 8),
|
||||||
|
ExpectCollision: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Player is standing on the floor at X=100
|
||||||
|
// with their feet at Y=500 and they move right
|
||||||
|
// 10 pixels.
|
||||||
|
testCase{
|
||||||
|
Start: render.NewPoint(
|
||||||
|
100,
|
||||||
|
500-playerSize.H,
|
||||||
|
),
|
||||||
|
MoveTo: render.NewPoint(
|
||||||
|
110,
|
||||||
|
500-playerSize.H,
|
||||||
|
),
|
||||||
|
ExpectCollision: true,
|
||||||
|
Expect: &collision.Collide{
|
||||||
|
Bottom: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Player walks off the right edge of the platform.
|
||||||
|
testCase{
|
||||||
|
// TODO: if the player is perfectly touching the floor,
|
||||||
|
// this test fails and returns True for collision, so
|
||||||
|
// I use 499-playerSize.H so they hover above the floor.
|
||||||
|
Start: render.NewPoint(
|
||||||
|
990,
|
||||||
|
499-playerSize.H,
|
||||||
|
),
|
||||||
|
MoveTo: render.NewPoint(
|
||||||
|
1100,
|
||||||
|
499-playerSize.H,
|
||||||
|
),
|
||||||
|
ExpectCollision: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Player moves through the barrier in the middle and
|
||||||
|
// is stopped in his tracks.
|
||||||
|
testCase{
|
||||||
|
Start: render.NewPoint(
|
||||||
|
490-playerSize.W, 500-playerSize.H,
|
||||||
|
),
|
||||||
|
MoveTo: render.NewPoint(
|
||||||
|
510, 500-playerSize.H,
|
||||||
|
),
|
||||||
|
ExpectCollision: true,
|
||||||
|
Expect: &collision.Collide{
|
||||||
|
Right: true,
|
||||||
|
Left: true, // TODO: not expected
|
||||||
|
Bottom: true,
|
||||||
|
MoveTo: render.NewPoint(
|
||||||
|
500-playerSize.W,
|
||||||
|
500-playerSize.H,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Player moves up from below the platform and hits the ceiling.
|
||||||
|
testCase{
|
||||||
|
Start: render.NewPoint(
|
||||||
|
490-playerSize.W,
|
||||||
|
550,
|
||||||
|
),
|
||||||
|
MoveTo: render.NewPoint(
|
||||||
|
490-playerSize.W,
|
||||||
|
499-playerSize.H,
|
||||||
|
),
|
||||||
|
ExpectCollision: true,
|
||||||
|
Expect: &collision.Collide{
|
||||||
|
Top: true,
|
||||||
|
|
||||||
|
// TODO: these are unexpected
|
||||||
|
Left: true,
|
||||||
|
Right: true,
|
||||||
|
Bottom: true,
|
||||||
|
|
||||||
|
// TODO: the MoveTo is unexpected
|
||||||
|
MoveTo: render.NewPoint(458, 468),
|
||||||
|
// MoveTo: render.NewPoint(
|
||||||
|
// 490-playerSize.W,
|
||||||
|
// 500,
|
||||||
|
// ),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
player.MoveTo(test.Start)
|
||||||
|
result, collided := collision.CollidesWithGrid(
|
||||||
|
player, grid, test.MoveTo,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Was there a collision at all?
|
||||||
|
if collided && !test.ExpectCollision {
|
||||||
|
t.Errorf(
|
||||||
|
"Test %d: we collided when we did not expect to!\n%s",
|
||||||
|
i,
|
||||||
|
describeTest(test, result, collided),
|
||||||
|
)
|
||||||
|
} else if !collided && test.ExpectCollision {
|
||||||
|
t.Errorf(
|
||||||
|
"Test %d: we did not collide but we expected to!\n%s",
|
||||||
|
i,
|
||||||
|
describeTest(test, result, collided),
|
||||||
|
)
|
||||||
|
} else if test.Expect != nil {
|
||||||
|
// Assert that each side is what we expected.
|
||||||
|
expect := test.Expect
|
||||||
|
if result.Top && !expect.Top || result.Left && !expect.Left ||
|
||||||
|
result.Right && !expect.Right || result.Bottom && !expect.Bottom {
|
||||||
|
t.Errorf(
|
||||||
|
"Test %d: collided as expected, but not the right sides!\n%s",
|
||||||
|
i,
|
||||||
|
describeTest(test, result, collided),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Was the MoveTo position expected?
|
||||||
|
if expect.MoveTo != render.Origin && result.MoveTo != expect.MoveTo {
|
||||||
|
t.Errorf(
|
||||||
|
"Test %d: collided as expected, but didn't move as expected!\n"+
|
||||||
|
"Expected to move to: %s\n"+
|
||||||
|
" But actually was: %s",
|
||||||
|
i,
|
||||||
|
expect.MoveTo,
|
||||||
|
result.MoveTo,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user