mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 11:48:56 +00:00
Updated test cases for frontend-backend-collision-reconciliation.
This commit is contained in:
parent
cff31d295c
commit
0f4d067c06
@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/kvartborg/vector"
|
|
||||||
"github.com/solarlune/resolv"
|
"github.com/solarlune/resolv"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -1151,7 +1150,7 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor
|
baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor
|
||||||
oldDx, oldDy := baseChange * float64(decodedInput[0]), baseChange * float64(decodedInput[1])
|
oldDx, oldDy := baseChange*float64(decodedInput[0]), baseChange*float64(decodedInput[1])
|
||||||
dx, dy := oldDx, oldDy
|
dx, dy := oldDx, oldDy
|
||||||
|
|
||||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||||
@ -1159,27 +1158,13 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
|
|||||||
if collision := playerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil {
|
if collision := playerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil {
|
||||||
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
|
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon)
|
barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon)
|
||||||
origX, origY := playerShape.Position()
|
if overlapped, pushbackX, pushbackY := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped {
|
||||||
playerShape.SetPosition(origX+oldDx, origY+oldDy)
|
Logger.Info(fmt.Sprintf("Collided & overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v, pushbackX=%v, pushbackY=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
|
||||||
if colliding := IsPolygonPairColliding(playerShape, barrierShape, nil); colliding {
|
dx -= pushbackX
|
||||||
Logger.Info(fmt.Sprintf("Collided: playerShape=%v, oldDx=%v, oldDy=%v", playerShape, oldDx, oldDy))
|
dy -= pushbackY
|
||||||
overlapResult := &SatResult{
|
} else {
|
||||||
Overlap: 0,
|
Logger.Info(fmt.Sprintf("Collider BUT not overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape)))
|
||||||
OverlapX: 0,
|
|
||||||
OverlapY: 0,
|
|
||||||
AContainedInB: true,
|
|
||||||
BContainedInA: true,
|
|
||||||
Axis: vector.Vector{0, 0},
|
|
||||||
}
|
}
|
||||||
e := vector.Vector{oldDx, oldDy}.Unit()
|
|
||||||
if separatableAlongMovement := IsPolygonPairSeparatedByDir(playerShape, barrierShape, e, overlapResult); !separatableAlongMovement {
|
|
||||||
pushbackX, pushbackY := overlapResult.Overlap*overlapResult.OverlapX, overlapResult.Overlap*overlapResult.OverlapY
|
|
||||||
Logger.Info(fmt.Sprintf("Collided: playerShape=%v, oldDx=%v, oldDy=%v, toCheckBarrier=%v, pushbackX=%v, pushbackY=%v", playerShape, oldDx, oldDy, barrierShape, pushbackX, pushbackY))
|
|
||||||
dx, dy = oldDx-pushbackX, oldDy-pushbackY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
playerShape.SetPosition(origX, origY)
|
|
||||||
}
|
}
|
||||||
playerCollider.X += dx
|
playerCollider.X += dx
|
||||||
playerCollider.Y += dy
|
playerCollider.Y += dy
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/kvartborg/vector"
|
|
||||||
"github.com/solarlune/resolv"
|
"github.com/solarlune/resolv"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"image/color"
|
"image/color"
|
||||||
@ -56,43 +55,22 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
|||||||
moveToCollide := true
|
moveToCollide := true
|
||||||
if moveToCollide {
|
if moveToCollide {
|
||||||
toTestPlayerCollider := playerColliders[0]
|
toTestPlayerCollider := playerColliders[0]
|
||||||
oldDx, oldDy := 135.0, 135.0
|
oldDx, oldDy := -2.98, -50.0
|
||||||
dx, dy := oldDx, oldDy
|
dx, dy := oldDx, oldDy
|
||||||
if collision := toTestPlayerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil {
|
if collision := toTestPlayerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil {
|
||||||
playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon)
|
playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon)
|
barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon)
|
||||||
origX, origY := playerShape.Position()
|
if overlapped, pushbackX, pushbackY := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped {
|
||||||
playerShape.SetPosition(origX+oldDx, origY+oldDy)
|
Logger.Info(fmt.Sprintf("Collided & overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v, pushbackX=%v, pushbackY=%v", toTestPlayerCollider.X, toTestPlayerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
|
||||||
if colliding := IsPolygonPairColliding(playerShape, barrierShape, nil); colliding {
|
dx -= pushbackX
|
||||||
Logger.Info(fmt.Sprintf("Collided: playerShape=%v, oldDx=%v, oldDy=%v", playerShape, oldDx, oldDy))
|
dy -= pushbackY
|
||||||
overlapResult := &SatResult{
|
|
||||||
Overlap: 0,
|
|
||||||
OverlapX: 0,
|
|
||||||
OverlapY: 0,
|
|
||||||
AContainedInB: true,
|
|
||||||
BContainedInA: true,
|
|
||||||
Axis: vector.Vector{0, 0},
|
|
||||||
}
|
|
||||||
e := vector.Vector{oldDx, oldDy}.Unit()
|
|
||||||
if separatableAlongMovement := IsPolygonPairSeparatedByDir(playerShape, barrierShape, e, overlapResult); !separatableAlongMovement {
|
|
||||||
pushbackX, pushbackY := overlapResult.Overlap*overlapResult.OverlapX, overlapResult.Overlap*overlapResult.OverlapY
|
|
||||||
Logger.Info(fmt.Sprintf("Collided: playerShape=%v, oldDx=%v, oldDy=%v, toCheckBarrier=%v, pushbackX=%v, pushbackY=%v", playerShape, oldDx, oldDy, barrierShape, pushbackX, pushbackY))
|
|
||||||
dx, dy = oldDx-pushbackX, oldDy-pushbackY
|
|
||||||
} else {
|
} else {
|
||||||
Logger.Info(fmt.Sprintf("Not Collided: playerShape=%v, oldDx=%v, oldDy=%v, toCheckBarrier=%v, e=%v", playerShape, oldDx, oldDy, barrierShape, e))
|
Logger.Info(fmt.Sprintf("Collider BUT not overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v", toTestPlayerCollider.X, toTestPlayerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape)))
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Logger.Info(fmt.Sprintf("Not collided: playerShape=%v, oldDx=%v, oldDy=%v, toCheckBarrier=%v", playerShape, oldDx, oldDy, barrierShape))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playerShape.SetPosition(origX, origY)
|
|
||||||
|
|
||||||
toTestPlayerCollider.X += dx
|
toTestPlayerCollider.X += dx
|
||||||
toTestPlayerCollider.Y += dy
|
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()
|
toTestPlayerCollider.Update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
package dnmshared
|
package dnmshared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/kvartborg/vector"
|
"github.com/kvartborg/vector"
|
||||||
"github.com/solarlune/resolv"
|
"github.com/solarlune/resolv"
|
||||||
"math"
|
"math"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
|
||||||
|
var s []string = make([]string, len(body.Points))
|
||||||
|
for i, p := range body.Points {
|
||||||
|
s[i] = fmt.Sprintf("[%v, %v]", p[0]+body.X, p[1]+body.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("[%s]", strings.Join(s, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
func GenerateRectCollider(origX, origY, w, h, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
|
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)
|
collider := resolv.NewObject(origX-w*0.5+spaceOffsetX, origY-h*0.5+spaceOffsetY, w, h, tag)
|
||||||
shape := resolv.NewRectangle(0, 0, w, h)
|
shape := resolv.NewRectangle(0, 0, w, h)
|
||||||
@ -43,6 +54,28 @@ func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceO
|
|||||||
return collider
|
return collider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CalcPushbacks(oldDx, oldDy float64, playerShape, barrierShape *resolv.ConvexPolygon) (bool, float64, float64) {
|
||||||
|
origX, origY := playerShape.Position()
|
||||||
|
defer func() {
|
||||||
|
playerShape.SetPosition(origX, origY)
|
||||||
|
}()
|
||||||
|
playerShape.SetPosition(origX+oldDx, origY+oldDy)
|
||||||
|
overlapResult := &SatResult{
|
||||||
|
Overlap: 0,
|
||||||
|
OverlapX: 0,
|
||||||
|
OverlapY: 0,
|
||||||
|
AContainedInB: true,
|
||||||
|
BContainedInA: true,
|
||||||
|
Axis: vector.Vector{0, 0},
|
||||||
|
}
|
||||||
|
if overlapped := IsPolygonPairOverlapped(playerShape, barrierShape, overlapResult); overlapped {
|
||||||
|
pushbackX, pushbackY := overlapResult.Overlap*overlapResult.OverlapX, overlapResult.Overlap*overlapResult.OverlapY
|
||||||
|
return true, pushbackX, pushbackY
|
||||||
|
} else {
|
||||||
|
return false, 0, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type SatResult struct {
|
type SatResult struct {
|
||||||
Overlap float64
|
Overlap float64
|
||||||
OverlapX float64
|
OverlapX float64
|
||||||
@ -52,7 +85,7 @@ type SatResult struct {
|
|||||||
Axis vector.Vector
|
Axis vector.Vector
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsPolygonPairColliding(a, b *resolv.ConvexPolygon, result *SatResult) bool {
|
func IsPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool {
|
||||||
aCnt, bCnt := len(a.Points), len(b.Points)
|
aCnt, bCnt := len(a.Points), len(b.Points)
|
||||||
// Single point case
|
// Single point case
|
||||||
if 1 == aCnt && 1 == bCnt {
|
if 1 == aCnt && 1 == bCnt {
|
||||||
@ -64,7 +97,7 @@ func IsPolygonPairColliding(a, b *resolv.ConvexPolygon, result *SatResult) bool
|
|||||||
|
|
||||||
if 1 < aCnt {
|
if 1 < aCnt {
|
||||||
for _, axis := range a.SATAxes() {
|
for _, axis := range a.SATAxes() {
|
||||||
if IsPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
|
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +105,7 @@ func IsPolygonPairColliding(a, b *resolv.ConvexPolygon, result *SatResult) bool
|
|||||||
|
|
||||||
if 1 < bCnt {
|
if 1 < bCnt {
|
||||||
for _, axis := range b.SATAxes() {
|
for _, axis := range b.SATAxes() {
|
||||||
if IsPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
|
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,10 +114,26 @@ func IsPolygonPairColliding(a, b *resolv.ConvexPolygon, result *SatResult) bool
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, result *SatResult) bool {
|
func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, result *SatResult) bool {
|
||||||
|
/*
|
||||||
|
[WARNING] This function is deliberately made private, it shouldn't be used alone (i.e. not along the norms of a polygon), otherwise the pushbacks calculated would be meaningless.
|
||||||
|
|
||||||
|
Consider the following example
|
||||||
|
a: {
|
||||||
|
anchor: [1337.19 1696.74]
|
||||||
|
points: [[0 0] [24 0] [24 24] [0 24]]
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
anchor: [1277.72 1570.56]
|
||||||
|
points: [[642.57 319.16] [0 319.16] [5.73 0] [643.75 0.90]]
|
||||||
|
}
|
||||||
|
|
||||||
|
e = (-2.98, 1.49).Unit()
|
||||||
|
*/
|
||||||
|
|
||||||
var aStart, aEnd, bStart, bEnd float64 = math.MaxFloat64, -math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64
|
var aStart, aEnd, bStart, bEnd float64 = math.MaxFloat64, -math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64
|
||||||
for _, p := range a.Points {
|
for _, p := range a.Points {
|
||||||
dot := p.X()*e.X() + p.Y()*e.Y()
|
dot := (p.X()+a.X)*e.X() + (p.Y()+a.Y)*e.Y()
|
||||||
|
|
||||||
if aStart > dot {
|
if aStart > dot {
|
||||||
aStart = dot
|
aStart = dot
|
||||||
@ -96,7 +145,7 @@ func IsPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, re
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range b.Points {
|
for _, p := range b.Points {
|
||||||
dot := p.X()*e.X() + p.Y()*e.Y()
|
dot := (p.X()+b.X)*e.X() + (p.Y()+b.Y)*e.Y()
|
||||||
|
|
||||||
if bStart > dot {
|
if bStart > dot {
|
||||||
bStart = dot
|
bStart = dot
|
||||||
|
39
frontend/assets/scripts/collision_test_nodejs.js
Normal file
39
frontend/assets/scripts/collision_test_nodejs.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
const collisions = require('./modules/Collisions');
|
||||||
|
|
||||||
|
const collisionSys = new collisions.Collisions();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Backend result reference
|
||||||
|
|
||||||
|
2022-10-22T12:11:25.156+0800 INFO collider_visualizer/worldColliderDisplay.go:77 Collided: player.X=1257.665, player.Y=1415.335, oldDx=-2.98, oldDy=-50, playerShape=&{[[0 0] [64 0] [64 64] [0 64]] 1254.685 1365.335 true}, toCheckBarrier=&{[[628.626 54.254500000000064] [0 56.03250000000003] [0.42449999999999477 1.1229999999999905] [625.9715000000001 0]] 1289.039 1318.0805 true}, pushbackX=-0.15848054013127655, pushbackY=-56.03205175509715, result=&{56.03227587710039 -0.0028283794946841584 -0.9999960001267175 false false [0.9988052279193613 -0.04886836073527201]}
|
||||||
|
*/
|
||||||
|
function polygonStr(body) {
|
||||||
|
let coords = [];
|
||||||
|
let cnt = body._coords.length;
|
||||||
|
for (let ix = 0, iy = 1; ix < cnt; ix += 2, iy += 2) {
|
||||||
|
coords.push([body._coords[ix], body._coords[iy]]);
|
||||||
|
}
|
||||||
|
return JSON.stringify(coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
const playerCollider = collisionSys.createPolygon(1257.665, 1415.335, [[0, 0], [64, 0], [64, 64], [0, 64]]);
|
||||||
|
const barrierCollider = collisionSys.createPolygon(1289.039, 1318.0805, [[628.626, 54.254500000000064], [0, 56.03250000000003], [0.42449999999999477, 1.1229999999999905], [625.9715000000001, 0]]);
|
||||||
|
|
||||||
|
const oldDx = -2.98;
|
||||||
|
const oldDy = -50.0;
|
||||||
|
|
||||||
|
playerCollider.x += oldDx;
|
||||||
|
playerCollider.y += oldDy;
|
||||||
|
|
||||||
|
collisionSys.update();
|
||||||
|
const result = collisionSys.createResult();
|
||||||
|
|
||||||
|
const potentials = playerCollider.potentials();
|
||||||
|
|
||||||
|
let overlapCheckId = 0;
|
||||||
|
for (const barrier of potentials) {
|
||||||
|
if (!playerCollider.collides(barrier, result)) continue;
|
||||||
|
const pushbackX = result.overlap * result.overlap_x;
|
||||||
|
const pushbackY = result.overlap * result.overlap_y;
|
||||||
|
console.log("For overlapCheckId=" + overlapCheckId + ", the overlap: a=", polygonStr(result.a), ", b=", polygonStr(result.b), ", pushbackX=", pushbackX, ", pushbackY=", pushbackY);
|
||||||
|
}
|
9
frontend/assets/scripts/collision_test_nodejs.js.meta
Normal file
9
frontend/assets/scripts/collision_test_nodejs.js.meta
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.5",
|
||||||
|
"uuid": "fce86138-76fc-44d5-8eac-2731b3b0cefd",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user