Drafted backend collision with pushback calculations.

This commit is contained in:
yflu 2022-10-21 22:39:08 +08:00
parent bc8989a0e6
commit 150e30db2a
4 changed files with 48 additions and 50 deletions

View File

@ -5,9 +5,9 @@ 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"
"math/rand" "math/rand"
. "server/common" . "server/common"
"server/common/utils" "server/common/utils"
@ -1151,19 +1151,35 @@ 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! origX, origY := playerShape.Position()
// dx = changeWithCollision.X() playerShape.SetPosition(origX+oldDx, origY+oldDy)
// dy = changeWithCollision.Y() if colliding := IsPolygonPairColliding(playerShape, barrierShape, nil); colliding {
dx = 0 Logger.Info(fmt.Sprintf("Collided: playerShape=%v, oldDx=%v, oldDy=%v", playerShape, oldDx, oldDy))
dy = 0 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
}
}
playerShape.SetPosition(origX, origY)
} }
playerCollider.X += dx playerCollider.X += dx
playerCollider.Y += dy playerCollider.Y += dy
@ -1203,9 +1219,7 @@ func (pR *Room) refreshColliders() {
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 +1229,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

@ -56,10 +56,8 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
moveToCollide := true moveToCollide := true
if moveToCollide { if moveToCollide {
toTestPlayerCollider := playerColliders[0] toTestPlayerCollider := playerColliders[0]
oldDx := 135.0 oldDx, oldDy := 135.0, 135.0
oldDy := 135.0 dx, dy := oldDx, oldDy
dx := oldDx
dy := 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)

View File

@ -440,7 +440,7 @@
"array": [ "array": [
0, 0,
0, 0,
209.73151519075364, 342.9460598986377,
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;
}); });
/* /*