From 3baaf1d52c3658c861c5e80e5f7910f2dd039251 Mon Sep 17 00:00:00 2001 From: genxium Date: Wed, 19 Oct 2022 15:10:11 +0800 Subject: [PATCH] Updated collider utility encapsulation for visualization subproject. --- collider_visualizer/worldColliderDisplay.go | 80 ++++++++------------ dnmshared/resolv_helper.go | 82 +++++++++++++++++++++ 2 files changed, 113 insertions(+), 49 deletions(-) create mode 100644 dnmshared/resolv_helper.go diff --git a/collider_visualizer/worldColliderDisplay.go b/collider_visualizer/worldColliderDisplay.go index f4d0466..bd4df2e 100644 --- a/collider_visualizer/worldColliderDisplay.go +++ b/collider_visualizer/worldColliderDisplay.go @@ -7,8 +7,6 @@ import ( "github.com/solarlune/resolv" "go.uber.org/zap" "image/color" - - "math" ) type WorldColliderDisplay struct { @@ -34,14 +32,11 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi spaceOffsetX := float64(spaceW) * 0.5 spaceOffsetY := float64(spaceH) * 0.5 - // TODO: Move collider y-axis transformation to a "dnmshared" playerColliderRadius := float64(32) playerColliders := make([]*resolv.Object, len(playerList)) space := resolv.NewSpace(int(spaceW), int(spaceH), 16, 16) for i, player := range playerList { - playerCollider := resolv.NewObject(player.X-playerColliderRadius+spaceOffsetX, player.Y-playerColliderRadius+spaceOffsetY, playerColliderRadius*2, playerColliderRadius*2, "Player") - playerColliderShape := resolv.NewRectangle(0, 0, playerColliderRadius*2, playerColliderRadius*2) // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset" - playerCollider.SetShape(playerColliderShape) + playerCollider := GenerateRectCollider(player.X, player.Y, playerColliderRadius*2, playerColliderRadius*2, spaceOffsetX, spaceOffsetY, "Player") // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset" Logger.Info(fmt.Sprintf("Player Collider#%d: player.X=%v, player.Y=%v, radius=%v, spaceOffsetX=%v, spaceOffsetY=%v, shape=%v; calibrationCheckX=player.X-radius+spaceOffsetX=%v", i, player.X, player.Y, playerColliderRadius, spaceOffsetX, spaceOffsetY, playerCollider.Shape, player.X-playerColliderRadius+spaceOffsetX)) playerColliders[i] = playerCollider space.Add(playerCollider) @@ -49,56 +44,43 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi barrierLocalId := 0 for _, barrierUnaligned := range barrierList { - barrier := AlignPolygon2DToBoundingBox(barrierUnaligned) - - var w float64 = 0 - var h float64 = 0 - - for i, pi := range barrier.Points { - for j, pj := range barrier.Points { - if i == j { - continue - } - if math.Abs(pj.X-pi.X) > w { - w = math.Abs(pj.X - pi.X) - } - if math.Abs(pj.Y-pi.Y) > h { - h = math.Abs(pj.Y - pi.Y) - } - } - } - - barrierColliderShape := resolv.NewConvexPolygon() - for i := 0; i < len(barrier.Points); i++ { - p := barrier.Points[i] - barrierColliderShape.AddPoints(p.X, p.Y) - } - - barrierCollider := resolv.NewObject(barrier.Anchor.X+spaceOffsetX, barrier.Anchor.Y+spaceOffsetY, w, h, "Barrier") - barrierCollider.SetShape(barrierColliderShape) - - space.Add(barrierCollider) + barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, "Barrier") Logger.Info(fmt.Sprintf("Added barrier: shape=%v", barrierCollider.Shape)) + space.Add(barrierCollider) barrierLocalId++ } world.Space = space - toTestPlayerCollider := playerColliders[0] - oldDx := 0.0 - oldDy := 180.0 - if collision := toTestPlayerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil { - toCheckBarrier := collision.Objects[0].Shape - if intersection := toTestPlayerCollider.Shape.Intersection(oldDx, oldDy, toCheckBarrier); nil != intersection { - Logger.Info(fmt.Sprintf("Collided: shape=%v, oldDx=%v, oldDy=%v, intersection.MTV=%v", toTestPlayerCollider.Shape, oldDx, oldDy, intersection.MTV)) - } else { - Logger.Info(fmt.Sprintf("Collided: shape=%v, oldDx=%v, oldDy=%v, toCheckBarrier=%v, no intersecting points", toTestPlayerCollider.Shape, oldDx, oldDy, toCheckBarrier)) - } - } else { - Logger.Info(fmt.Sprintf("Collision Test: shape=%v, oldDx=%v, oldDy=%v, not colliding with any Barrier", toTestPlayerCollider.Shape, oldDx, oldDy)) - } + moveToCollide := true + if moveToCollide { + toTestPlayerCollider := playerColliders[0] + oldDx := 0.0 + oldDy := 180.0 + dx := oldDx + dy := oldDy + if collision := toTestPlayerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil { + playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon) + barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon) + origX, origY := playerShape.Position() + playerShape.SetPosition(origX+oldDx, origY+oldDy) + if mtv := CalculateMTVForConvexPolygon(playerShape, barrierShape); mtv != nil { + Logger.Info(fmt.Sprintf("Collided: shape=%v, oldDx=%v, oldDy=%v, MTV=%v", toTestPlayerCollider.Shape, oldDx, oldDy, mtv)) + //dx, dy = mtv[0], mtv[1] + } else { + Logger.Info(fmt.Sprintf("Collided: shape=%v, oldDx=%v, oldDy=%v, toCheckBarrier=%v, not intersecting", toTestPlayerCollider.Shape, oldDx, oldDy, barrierShape)) + } - toTestPlayerCollider.Update() + playerShape.SetPosition(origX, origY) + + toTestPlayerCollider.X += dx + toTestPlayerCollider.Y += dy + } else { + Logger.Info(fmt.Sprintf("Collision Test: shape=%v, oldDx=%v, oldDy=%v, not colliding with any Barrier", toTestPlayerCollider.Shape, oldDx, oldDy)) + } + + toTestPlayerCollider.Update() + } return world } diff --git a/dnmshared/resolv_helper.go b/dnmshared/resolv_helper.go new file mode 100644 index 0000000..a1a1698 --- /dev/null +++ b/dnmshared/resolv_helper.go @@ -0,0 +1,82 @@ +package dnmshared + +import ( + "github.com/kvartborg/vector" + "github.com/solarlune/resolv" + "math" +) + +func GenerateRectCollider(origX, origY, w, h, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object { + collider := resolv.NewObject(origX-w*0.5+spaceOffsetX, origY-h*0.5+spaceOffsetY, w, h, tag) + shape := resolv.NewRectangle(0, 0, w, h) + collider.SetShape(shape) + return collider +} + +func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object { + aligned := AlignPolygon2DToBoundingBox(unalignedSrc) + var w, h float64 = 0, 0 + + shape := resolv.NewConvexPolygon() + for i, pi := range aligned.Points { + for j, pj := range aligned.Points { + if i == j { + continue + } + if math.Abs(pj.X-pi.X) > w { + w = math.Abs(pj.X - pi.X) + } + if math.Abs(pj.Y-pi.Y) > h { + h = math.Abs(pj.Y - pi.Y) + } + } + } + + for i := 0; i < len(aligned.Points); i++ { + p := aligned.Points[i] + shape.AddPoints(p.X, p.Y) + } + + collider := resolv.NewObject(aligned.Anchor.X+spaceOffsetX, aligned.Anchor.Y+spaceOffsetY, w, h, tag) + collider.SetShape(shape) + + return collider +} + +func CalculateMTVForConvexPolygon(cp *resolv.ConvexPolygon, other *resolv.ConvexPolygon) vector.Vector { + delta := vector.Vector{0, 0} + + smallest := vector.Vector{math.MaxFloat64, 0} + + for _, axis := range cp.SATAxes() { + if !cp.Project(axis).Overlapping(other.Project(axis)) { + return nil + } + + overlap := cp.Project(axis).Overlap(other.Project(axis)) + + if smallest.Magnitude() > overlap { + smallest = axis.Scale(overlap) + } + + } + + for _, axis := range other.SATAxes() { + + if !cp.Project(axis).Overlapping(other.Project(axis)) { + return nil + } + + overlap := cp.Project(axis).Overlap(other.Project(axis)) + + if smallest.Magnitude() > overlap { + smallest = axis.Scale(overlap) + } + + } + + delta[0] = smallest[0] + delta[1] = smallest[1] + + return delta +}