From 7971198c17f48c54dcc2323661ebb989455e4f73 Mon Sep 17 00:00:00 2001 From: Stephen McQuay Date: Wed, 15 Jan 2014 21:48:47 -0800 Subject: [PATCH] Fixed collision detection in probes This was a problem before when the probe hit an object that was further than another object first. Now we traverse all objects all the time to find the closest one. \o/ --- game.go | 2 ++ projectile.go | 8 +++-- robot.go | 85 +++++++++++++++++++++++++++++++---------------- robot_test.go | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 30 deletions(-) create mode 100644 robot_test.go diff --git a/game.go b/game.go index 1e123a1..cfaf4c4 100644 --- a/game.go +++ b/game.go @@ -1,5 +1,7 @@ package main +// delete me + import ( "log" "sort" diff --git a/projectile.go b/projectile.go index 4a2f135..3d6472a 100644 --- a/projectile.go +++ b/projectile.go @@ -62,7 +62,9 @@ func (p *Projectile) Tick(g *game) { collision, _, pos := v.RectIntersection(obj.Bounds, p.Position, travel) if collision { arrived = true - p.Position = pos + if pos != nil { + p.Position = *pos + } obj.Hp -= p.Damage // if obj.Hp < 0 { // delete(g.obstacles, i) @@ -70,7 +72,9 @@ func (p *Projectile) Tick(g *game) { } } } else { - p.Position = pos + if pos != nil { + p.Position = *pos + } } if arrived || hit_player { diff --git a/robot.go b/robot.go index 8ed8389..c09e2c9 100644 --- a/robot.go +++ b/robot.go @@ -165,17 +165,28 @@ type Instruction struct { Scan *bool `json:"scan,omitempty"` } -func (r *Robot) checkCollisions(g *game, move_vector v.Vector2d) (bool, v.Point2d, *Robot) { +// returns collision, the intersection point, and the robot with whom r has +// collided, if this happened. +func (r *Robot) checkCollisions(g *game, probe v.Vector2d) (bool, *v.Point2d, *Robot) { + finalCollision := false collision := false - intersection_point := v.Point2d{X: 0, Y: 0} - bot_size := float32(5.0) - bot_polygon := v.OrientedSquare(r.Position, r.Heading, bot_size) + closest := float32(math.Inf(1)) + var intersection *v.Point2d + var finalRobot *Robot + + // TODO: this needs moved to the conf? + botSize := float32(5.0) + botPolygon := v.OrientedSquare(r.Position, r.Heading, botSize) // Check Walls - r_walls := v.AABB2d{A: v.Point2d{X: bot_size, Y: bot_size}, B: v.Point2d{X: g.width - bot_size, Y: g.height - bot_size}} - collision, _, pos := v.RectIntersection(r_walls, r.Position, move_vector) - if collision { - return collision, pos, nil + r_walls := v.AABB2d{A: v.Point2d{X: botSize, Y: botSize}, B: v.Point2d{X: g.width - botSize, Y: g.height - botSize}} + collision, _, wallIntersect := v.RectIntersection(r_walls, r.Position, probe) + if collision && wallIntersect != nil { + finalCollision = collision + if dist := r.Position.Sub(*wallIntersect).Mag(); dist < closest { + intersection = wallIntersect + closest = dist + } } // Check Other Bots @@ -184,29 +195,47 @@ func (r *Robot) checkCollisions(g *game, move_vector v.Vector2d) (bool, v.Point2 if bot.Id == r.Id { continue } - player_rect := v.OrientedSquare(bot.Position, bot.Heading, bot_size) + player_rect := v.OrientedSquare(bot.Position, bot.Heading, botSize) collision, move_collision, translation := v.PolyPolyIntersection( - bot_polygon, move_vector, player_rect) + botPolygon, probe, player_rect) if collision || move_collision { - return true, r.Position.Add(move_vector).Add(translation.Scale(1.1)), bot + finalCollision = collision + p := r.Position.Add(probe).Add(translation.Scale(1.2)) + if dist := r.Position.Sub(p).Mag(); dist < closest { + intersection = &p + closest = dist + finalRobot = bot + } + } + } + } + // Check Obstacles + for _, obj := range g.obstacles { + // collision due to motion: + collision, move_collision, translation := v.PolyPolyIntersection( + botPolygon, probe, obj.Bounds.ToPolygon()) + + if collision || move_collision { + finalCollision = collision + p := r.Position.Add(probe).Add(translation.Scale(1.1)) + if dist := r.Position.Sub(p).Mag(); dist < closest { + intersection = &p + closest = dist + } + } + + // collision due to probe + collision, _, wallIntersect := v.RectIntersection(obj.Bounds, r.Position, probe) + if collision && wallIntersect != nil { + finalCollision = collision + if dist := r.Position.Sub(*wallIntersect).Mag(); dist < closest { + intersection = wallIntersect + closest = dist } } } - // Check Obstacles - for _, obj := range g.obstacles { - collision, move_collision, translation := v.PolyPolyIntersection( - bot_polygon, move_vector, obj.Bounds.ToPolygon()) - - if collision || move_collision { - // log.Printf(" COLLISION: %v %v %v\n", collision, move_collition, translation) - // log.Printf(" DETAILS: %v %v\n", r.Position, move_vector) - // log.Printf(" INPUT: %v %v %v\n", bot_polygon, obj.Bounds.ToPolygon(), obj.Bounds) - return true, r.Position.Add(move_vector).Add(translation.Scale(1.1)), nil - } - } - - return collision, intersection_point, nil + return finalCollision, intersection, finalRobot } func (r *Robot) Tick(g *game) { @@ -287,8 +316,8 @@ func (r *Robot) Tick(g *game) { } } - if r.Position != intersection_point { - r.Position = intersection_point + if r.Position != *intersection_point { + r.Position = *intersection_point } r.Health -= dmg @@ -343,7 +372,7 @@ func (r *Robot) Tick(g *game) { probe_vector := r.Probe.Sub(r.Position) coll, pos, _ := r.checkCollisions(g, probe_vector) if coll { - r.ProbeResult = &pos + r.ProbeResult = pos } } } diff --git a/robot_test.go b/robot_test.go new file mode 100644 index 0000000..547c40e --- /dev/null +++ b/robot_test.go @@ -0,0 +1,91 @@ +package main + +import ( + v "bitbucket.org/hackerbots/vector" + "math" + "testing" +) + +func TestCollision(t *testing.T) { + tests := []struct { + g game + r Robot + target v.Vector2d + location *v.Point2d + collision bool + }{ + // should intersect with first box + { + g: game{ + width: 800, + height: 400, + obstacles: []Obstacle{ + Obstacle{ + Bounds: v.AABB2d{ + v.Point2d{200, 100}, + v.Point2d{300, 200}, + }, + }, + Obstacle{ + Bounds: v.AABB2d{ + v.Point2d{400, 200}, + v.Point2d{600, 300}, + }, + }, + }, + }, + r: Robot{ + Position: v.Point2d{100, 100}, + // Heading: v.Vector2d{1, 1}, + }, + target: v.Vector2d{900, 350}, + location: &v.Point2d{200, 138.88889}, + collision: true, + }, + // shouldn't intersect at all + { + g: game{ + width: 800, + height: 400, + obstacles: []Obstacle{ + Obstacle{ + Bounds: v.AABB2d{ + v.Point2d{200, 100}, + v.Point2d{300, 200}, + }, + }, + }, + }, + r: Robot{ + Position: v.Point2d{100, 100}, + }, + target: v.Vector2d{0, 0}, + collision: false, + }, + } + for _, test := range tests { + collision, location, _ := test.r.checkCollisions(&test.g, test.target) + if collision != test.collision { + t.Errorf("expected: %t, actual: %t", test.collision, collision) + } + if test.location == nil { + if location != nil { + t.Errorf("expected: %+v, actual: %+v", test.location, location) + } + } + if test.location != nil { + if location == nil { + t.Errorf("expected: %+v, actual: %+v", test.location, location) + } else { + if math.Abs(float64(location.X-test.location.X)) > 1e-4 || math.Abs(float64(location.Y-test.location.Y)) > 1e-4 { + t.Errorf("expected: %+v, actual: %+v", test.location, location) + } + } + } + } +} + +// XXX +func TestBotBotCollisions(t *testing.T) { + t.Skip("NYI") +}