Merge pull request #4 from genxium/backend_collision_pushback

Backend collision pushback synchronization.
This commit is contained in:
Wing 2022-10-22 13:52:52 +08:00 committed by GitHub
commit c69aa25353
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 183 additions and 124 deletions

View File

@ -7,7 +7,6 @@ import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/solarlune/resolv" "github.com/solarlune/resolv"
"go.uber.org/zap" "go.uber.org/zap"
"math"
"math/rand" "math/rand"
. "server/common" . "server/common"
"server/common/utils" "server/common/utils"
@ -329,7 +328,7 @@ func (pR *Room) ChooseStage() error {
var barrierLocalIdInBattle int32 = 0 var barrierLocalIdInBattle int32 = 0
for _, polygon2DUnaligned := range barrierPolygon2DList { for _, polygon2DUnaligned := range barrierPolygon2DList {
polygon2D := AlignPolygon2DToBoundingBox(polygon2DUnaligned) polygon2D := AlignPolygon2DToBoundingBox(polygon2DUnaligned)
/* /*
// For debug-printing only. // For debug-printing only.
Logger.Info("ChooseStage printing polygon2D for barrierPolygon2DList", zap.Any("barrierLocalIdInBattle", barrierLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points)) Logger.Info("ChooseStage printing polygon2D for barrierPolygon2DList", zap.Any("barrierLocalIdInBattle", barrierLocalIdInBattle), zap.Any("polygon2D.Anchor", polygon2D.Anchor), zap.Any("polygon2D.Points", polygon2D.Points))
@ -1151,19 +1150,21 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
continue continue
} }
baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor baseChange := player.Speed * pR.RollbackEstimatedDt * decodedInputSpeedFactor
dx := baseChange * float64(decodedInput[0]) oldDx, oldDy := baseChange*float64(decodedInput[0]), baseChange*float64(decodedInput[1])
dy := baseChange * float64(decodedInput[1]) dx, dy := oldDx, oldDy
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := pR.CollisionSysMap[collisionPlayerIndex] playerCollider := pR.CollisionSysMap[collisionPlayerIndex]
if collision := playerCollider.Check(dx, dy, "Barrier"); collision != nil { if collision := playerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil {
changeWithCollision := collision.ContactWithObject(collision.Objects[0]) playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
Logger.Info(fmt.Sprintf("Collided: roomId=%v, playerId=%v, orig dx=%v, orig dy=%v, proposed new dx =%v, proposed new dy=%v", pR.Id, player.Id, dx, dy, changeWithCollision.X(), changeWithCollision.Y())) barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon)
// FIXME: Use a mechanism equivalent to that of the frontend! if overlapped, pushbackX, pushbackY := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped {
// dx = changeWithCollision.X() 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))
// dy = changeWithCollision.Y() dx -= pushbackX
dx = 0 dy -= pushbackY
dy = 0 } else {
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)))
}
} }
playerCollider.X += dx playerCollider.X += dx
playerCollider.Y += dy playerCollider.Y += dy
@ -1200,12 +1201,10 @@ func (pR *Room) refreshColliders() {
spaceOffsetX := float64(spaceW) * 0.5 spaceOffsetX := float64(spaceW) * 0.5
spaceOffsetY := float64(spaceH) * 0.5 spaceOffsetY := float64(spaceH) * 0.5
minStep := int(3) // the approx minimum distance a player can move per frame minStep := int(3) // the approx minimum distance a player can move per frame
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled
for _, player := range pR.Players { for _, player := range pR.Players {
playerCollider := resolv.NewObject(player.X-playerColliderRadius+spaceOffsetX, player.Y-playerColliderRadius+spaceOffsetY, playerColliderRadius*2, playerColliderRadius*2) playerCollider := GenerateRectCollider(player.X, player.Y, playerColliderRadius*2, playerColliderRadius*2, spaceOffsetX, spaceOffsetY, "Player")
playerColliderShape := resolv.NewCircle(+playerColliderRadius, +playerColliderRadius, playerColliderRadius)
playerCollider.SetShape(playerColliderShape)
space.Add(playerCollider) space.Add(playerCollider)
// Keep track of the collider in "pR.CollisionSysMap" // Keep track of the collider in "pR.CollisionSysMap"
joinIndex := player.JoinIndex joinIndex := player.JoinIndex
@ -1215,31 +1214,8 @@ func (pR *Room) refreshColliders() {
} }
for _, barrier := range pR.Barriers { for _, barrier := range pR.Barriers {
boundaryUnaligned := barrier.Boundary
var w float64 = 0 barrierCollider := GenerateConvexPolygonCollider(boundaryUnaligned, spaceOffsetX, spaceOffsetY, "Barrier")
var h float64 = 0
for i, pi := range barrier.Boundary.Points {
for j, pj := range barrier.Boundary.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 _, p := range barrier.Boundary.Points {
barrierColliderShape.AddPoints(p.X, p.Y)
}
barrierCollider := resolv.NewObject(barrier.Boundary.Anchor.X+spaceOffsetX, barrier.Boundary.Anchor.Y+spaceOffsetY, w, h, "Barrier")
barrierCollider.SetShape(barrierColliderShape)
space.Add(barrierCollider) space.Add(barrierCollider)
} }
} }

View File

@ -85,7 +85,7 @@ type Game struct {
func NewGame() *Game { func NewGame() *Game {
stageName := "simple" // Use this for calibration stageName := "simple" // Use this for calibration
// stageName := "richsoil" // stageName := "richsoil"
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, playerPosMap, barrierMap, err := parseStage(stageName) stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, playerPosMap, barrierMap, err := parseStage(stageName)
if nil != err { if nil != err {

View File

@ -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"
@ -34,69 +33,46 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
spaceOffsetY := float64(spaceH) * 0.5 spaceOffsetY := float64(spaceH) * 0.5
playerColliderRadius := float64(32) playerColliderRadius := float64(32)
playerColliders := make([]*resolv.Object, len(playerList)) playerColliders := make([]*resolv.Object, len(playerList))
space := resolv.NewSpace(int(spaceW), int(spaceH), 16, 16) space := resolv.NewSpace(int(spaceW), int(spaceH), 16, 16)
for i, player := range playerList { for i, player := range playerList {
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" 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)) 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 playerColliders[i] = playerCollider
space.Add(playerCollider) space.Add(playerCollider)
} }
barrierLocalId := 0 barrierLocalId := 0
for _, barrierUnaligned := range barrierList { for _, barrierUnaligned := range barrierList {
barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, "Barrier") barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, "Barrier")
Logger.Info(fmt.Sprintf("Added barrier: shape=%v", barrierCollider.Shape)) Logger.Info(fmt.Sprintf("Added barrier: shape=%v", barrierCollider.Shape))
space.Add(barrierCollider) space.Add(barrierCollider)
barrierLocalId++ barrierLocalId++
} }
world.Space = space world.Space = space
moveToCollide := true moveToCollide := true
if moveToCollide { if moveToCollide {
toTestPlayerCollider := playerColliders[0] toTestPlayerCollider := playerColliders[0]
oldDx := 135.0 oldDx, oldDy := -2.98, -50.0
oldDy := 135.0 dx, dy := oldDx, oldDy
dx := oldDx if collision := toTestPlayerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil {
dy := oldDy playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon)
if collision := toTestPlayerCollider.Check(oldDx, oldDy, "Barrier"); collision != nil { barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon)
playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon) if overlapped, pushbackX, pushbackY := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped {
barrierShape := collision.Objects[0].Shape.(*resolv.ConvexPolygon) 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))
origX, origY := playerShape.Position() dx -= pushbackX
playerShape.SetPosition(origX+oldDx, origY+oldDy) dy -= pushbackY
if colliding := IsPolygonPairColliding(playerShape, barrierShape, nil); colliding { } else {
Logger.Info(fmt.Sprintf("Collided: playerShape=%v, oldDx=%v, oldDy=%v", playerShape, oldDx, oldDy)) 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)))
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 {
Logger.Info(fmt.Sprintf("Not Collided: playerShape=%v, oldDx=%v, oldDy=%v, toCheckBarrier=%v, e=%v", playerShape, oldDx, oldDy, barrierShape, e))
}
} 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.Y += dy
toTestPlayerCollider.X += dx toTestPlayerCollider.Update()
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 return world
} }

View File

@ -16,7 +16,7 @@ type Vec2D struct {
} }
func NormVec2D(dx, dy float64) Vec2D { func NormVec2D(dx, dy float64) Vec2D {
return Vec2D{dy, -dx} return Vec2D{dy, -dx}
} }
type Polygon2D struct { type Polygon2D struct {

View File

@ -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,16 +54,38 @@ func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceO
return collider return collider
} }
type SatResult struct { func CalcPushbacks(oldDx, oldDy float64, playerShape, barrierShape *resolv.ConvexPolygon) (bool, float64, float64) {
Overlap float64 origX, origY := playerShape.Position()
OverlapX float64 defer func() {
OverlapY float64 playerShape.SetPosition(origX, origY)
AContainedInB bool }()
BContainedInA bool playerShape.SetPosition(origX+oldDx, origY+oldDy)
Axis vector.Vector 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
}
} }
func IsPolygonPairColliding(a, b *resolv.ConvexPolygon, result *SatResult) bool { type SatResult struct {
Overlap float64
OverlapX float64
OverlapY float64
AContainedInB bool
BContainedInA bool
Axis vector.Vector
}
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
@ -113,7 +162,7 @@ func IsPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, re
} }
if nil != result { if nil != result {
result.Axis = e result.Axis = e
overlap := float64(0) overlap := float64(0)
if aStart < bStart { if aStart < bStart {

View File

@ -539,7 +539,7 @@
"array": [ "array": [
0, 0,
0, 0,
210.4441731196186, 342.9460598986377,
0, 0,
0, 0,
0, 0,

View File

@ -440,7 +440,7 @@
"array": [ "array": [
0, 0,
0, 0,
209.73151519075364, 344.6781107062066,
0, 0,
0, 0,
0, 0,

View File

@ -740,9 +740,13 @@ cc.Class({
newPlayerNode.setPosition(cc.v2(x, y)); newPlayerNode.setPosition(cc.v2(x, y));
newPlayerNode.getComponent("SelfPlayer").mapNode = self.node; newPlayerNode.getComponent("SelfPlayer").mapNode = self.node;
const currentSelfColliderCircle = newPlayerNode.getComponent(cc.CircleCollider); const currentSelfColliderCircle = newPlayerNode.getComponent(cc.CircleCollider);
const r = currentSelfColliderCircle.radius, d = 2*r;
// The collision box of an individual player is a polygon instead of a circle, because the backend collision engine doesn't handle circle alignment well.
const x0 = x-r, y0 = y-r;
let pts = [[0, 0], [d, 0], [d, d], [0, d]];
const newPlayerColliderLatest = self.latestCollisionSys.createCircle(x, y, currentSelfColliderCircle.radius); const newPlayerColliderLatest = self.latestCollisionSys.createPolygon(x0, y0, pts);
const newPlayerColliderChaser = self.chaserCollisionSys.createCircle(x, y, currentSelfColliderCircle.radius); const newPlayerColliderChaser = self.chaserCollisionSys.createPolygon(x0, y0, pts);
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
self.latestCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderLatest); self.latestCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderLatest);
self.chaserCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderChaser); self.chaserCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderChaser);
@ -952,10 +956,12 @@ cc.Class({
const joinIndex = playerRichInfo.joinIndex; const joinIndex = playerRichInfo.joinIndex;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex); const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const currentSelfColliderCircle = playerRichInfo.node.getComponent(cc.CircleCollider);
const r = currentSelfColliderCircle.radius;
rdf.players[playerRichInfo.id] = { rdf.players[playerRichInfo.id] = {
id: playerRichInfo.id, id: playerRichInfo.id,
x: playerCollider.x, x: playerCollider.x + r, // [WARNING] the (x, y) of "playerCollider" is offset to the anchor (i.e. first point of all points) of the polygon shape
y: playerCollider.y, y: playerCollider.y + r,
dir: self.ctrl.decodeDirection(null == inputFrameAppliedOnPrevRenderFrame ? 0 : inputFrameAppliedOnPrevRenderFrame.inputList[joinIndex - 1]), dir: self.ctrl.decodeDirection(null == inputFrameAppliedOnPrevRenderFrame ? 0 : inputFrameAppliedOnPrevRenderFrame.inputList[joinIndex - 1]),
speed: (null == speedRefRenderFrame ? playerRichInfo.speed : speedRefRenderFrame.players[playerRichInfo.id].speed), speed: (null == speedRefRenderFrame ? playerRichInfo.speed : speedRefRenderFrame.players[playerRichInfo.id].speed),
joinIndex: joinIndex joinIndex: joinIndex
@ -1025,8 +1031,11 @@ cc.Class({
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex); const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const player = latestRdf.players[playerId]; const player = latestRdf.players[playerId];
playerCollider.x = player.x;
playerCollider.y = player.y; const currentSelfColliderCircle = playerRichInfo.node.getComponent(cc.CircleCollider);
const r = currentSelfColliderCircle.radius;
playerCollider.x = player.x - r;
playerCollider.y = player.y - r;
}); });
/* /*

View File

@ -189,10 +189,11 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
const inputFrameIdConsecutive = (resp.inputFrameDownsyncBatch[0].inputFrameId == mapIns.lastAllConfirmedInputFrameId + 1); const inputFrameIdConsecutive = (resp.inputFrameDownsyncBatch[0].inputFrameId == mapIns.lastAllConfirmedInputFrameId + 1);
const renderFrameIdConsecutive = (resp.rdf.id <= mapIns.renderFrameId + mapIns.renderFrameIdLagTolerance); const renderFrameIdConsecutive = (resp.rdf.id <= mapIns.renderFrameId + mapIns.renderFrameIdLagTolerance);
if (inputFrameIdConsecutive && renderFrameIdConsecutive) { if (inputFrameIdConsecutive && renderFrameIdConsecutive) {
console.log("Got consecutive resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp)); // console.log("Got consecutive resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp));
mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch);
} else { } else {
console.warn("Got forced resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp, null, 2)); // console.warn("Got forced resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", the incoming resp=\n", JSON.stringify(resp, null, 2));
console.warn("Got forced resync@localRenderFrameId=", mapIns.renderFrameId, ", @lastAllConfirmedRenderFrameId=", mapIns.lastAllConfirmedRenderFrameId, "@lastAllConfirmedInputFrameId=", mapIns.lastAllConfirmedInputFrameId, ", @localRecentInputCache=", mapIns._stringifyRecentInputCache(false), ", inputFrameIdConsecutive=", inputFrameIdConsecutive, ", renderFrameIdConsecutive=", renderFrameIdConsecutive);
// The following order of execution is important // The following order of execution is important
mapIns.onRoomDownsyncFrame(resp.rdf); mapIns.onRoomDownsyncFrame(resp.rdf);
mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch); mapIns.onInputFrameDownsyncBatch(resp.inputFrameDownsyncBatch);

View 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);
}

View 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