mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-25 11:18:55 +00:00
Finally fixed inconsistent pushbacks between frontend and backend.
This commit is contained in:
parent
bb6055f48c
commit
335e11e925
@ -424,9 +424,9 @@ func (pR *Room) StartBattle() {
|
|||||||
Logger.Error("battleMainLoop, recovery spot#1, recovered from: ", zap.Any("roomId", pR.Id), zap.Any("panic", r))
|
Logger.Error("battleMainLoop, recovery spot#1, recovered from: ", zap.Any("roomId", pR.Id), zap.Any("panic", r))
|
||||||
}
|
}
|
||||||
pR.StopBattleForSettlement()
|
pR.StopBattleForSettlement()
|
||||||
rdfIdToActuallyUsedInputDump := pR.rdfIdToActuallyUsedInputString()
|
|
||||||
Logger.Info(fmt.Sprintf("The `battleMainLoop` for roomId=%v is stopped@renderFrameId=%v, with battleDurationFrames=%v:\n%v", pR.Id, pR.RenderFrameId, pR.BattleDurationFrames, pR.InputsBufferString(false))) // This takes sometime to print
|
Logger.Info(fmt.Sprintf("The `battleMainLoop` for roomId=%v is stopped@renderFrameId=%v, with battleDurationFrames=%v:\n%v", pR.Id, pR.RenderFrameId, pR.BattleDurationFrames, pR.InputsBufferString(false))) // This takes sometime to print
|
||||||
os.WriteFile(fmt.Sprintf("room_%d.txt", pR.Id), []byte(rdfIdToActuallyUsedInputDump), 0644) // DEBUG ONLY
|
//rdfIdToActuallyUsedInputDump := pR.rdfIdToActuallyUsedInputString()
|
||||||
|
//os.WriteFile(fmt.Sprintf("room_%d.txt", pR.Id), []byte(rdfIdToActuallyUsedInputDump), 0644) // DEBUG ONLY
|
||||||
pR.onBattleStoppedForSettlement()
|
pR.onBattleStoppedForSettlement()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -773,7 +773,7 @@ func (pR *Room) OnDismissed() {
|
|||||||
pR.RollbackEstimatedDtNanos = 16666666 // A little smaller than the actual per frame time, just for logging FAST FRAME
|
pR.RollbackEstimatedDtNanos = 16666666 // A little smaller than the actual per frame time, just for logging FAST FRAME
|
||||||
dilutedServerFps := float64(58.0) // Don't set this value too small, otherwise we might miss force confirmation needs for slow tickers!
|
dilutedServerFps := float64(58.0) // Don't set this value too small, otherwise we might miss force confirmation needs for slow tickers!
|
||||||
pR.dilutedRollbackEstimatedDtNanos = int64(float64(pR.RollbackEstimatedDtNanos) * float64(pR.ServerFps) / dilutedServerFps)
|
pR.dilutedRollbackEstimatedDtNanos = int64(float64(pR.RollbackEstimatedDtNanos) * float64(pR.ServerFps) / dilutedServerFps)
|
||||||
pR.BattleDurationFrames = 15 * pR.ServerFps
|
pR.BattleDurationFrames = 60 * pR.ServerFps
|
||||||
pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1)
|
pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1)
|
||||||
pR.InputFrameUpsyncDelayTolerance = (pR.NstDelayFrames >> pR.InputScaleFrames) - 1 // this value should be strictly smaller than (NstDelayFrames >> InputScaleFrames), otherwise "type#1 forceConfirmation" might become a lag avalanche
|
pR.InputFrameUpsyncDelayTolerance = (pR.NstDelayFrames >> pR.InputScaleFrames) - 1 // this value should be strictly smaller than (NstDelayFrames >> InputScaleFrames), otherwise "type#1 forceConfirmation" might become a lag avalanche
|
||||||
pR.MaxChasingRenderFramesPerUpdate = 12 // Don't set this value too high to avoid exhausting frontend CPU within a single frame
|
pR.MaxChasingRenderFramesPerUpdate = 12 // Don't set this value too high to avoid exhausting frontend CPU within a single frame
|
||||||
|
@ -64,7 +64,7 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
|||||||
if moveToCollide {
|
if moveToCollide {
|
||||||
effPushback := Vec2D{X: float64(0), Y: float64(0)}
|
effPushback := Vec2D{X: float64(0), Y: float64(0)}
|
||||||
colliderWidth, colliderHeight := playerColliderRadius*2, playerColliderRadius*4
|
colliderWidth, colliderHeight := playerColliderRadius*2, playerColliderRadius*4
|
||||||
playerColliders[0].X, playerColliders[0].Y = VirtualGridToPolygonColliderBLPos(int32(-139000-2000), int32(-474500+2000), colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
|
playerColliders[0].X, playerColliders[0].Y = VirtualGridToPolygonColliderBLPos(int32(-139000), int32(-474500), colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
|
||||||
playerColliders[0].Update()
|
playerColliders[0].Update()
|
||||||
|
|
||||||
playerColliders[1].X, playerColliders[1].Y = VirtualGridToPolygonColliderBLPos(int32(-163000), int32(-520000), colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
|
playerColliders[1].X, playerColliders[1].Y = VirtualGridToPolygonColliderBLPos(int32(-163000), int32(-520000), colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
|
||||||
@ -84,9 +84,9 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
|||||||
Logger.Warn(fmt.Sprintf("Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(bShape), overlapResult))
|
Logger.Warn(fmt.Sprintf("Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(bShape), overlapResult))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toTestPlayerCollider.X -= effPushback.X
|
//toTestPlayerCollider.X -= effPushback.X
|
||||||
toTestPlayerCollider.Y -= effPushback.Y
|
//toTestPlayerCollider.Y -= effPushback.Y
|
||||||
toTestPlayerCollider.Update()
|
//toTestPlayerCollider.Update()
|
||||||
Logger.Info(fmt.Sprintf("effPushback={%v, %v}", effPushback.X, effPushback.Y))
|
Logger.Info(fmt.Sprintf("effPushback={%v, %v}", effPushback.X, effPushback.Y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -12,7 +12,7 @@
|
|||||||
<object id="135" x="901" y="1579">
|
<object id="135" x="901" y="1579">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
<object id="137" x="865" y="1561">
|
<object id="137" x="861" y="1540">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
|
@ -461,7 +461,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
217.37237634989413,
|
216.79265527990535,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -920,8 +920,7 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu
|
|||||||
if (self.lastAllConfirmedInputFrameId >= delayedInputFrameId && !self.equalRoomDownsyncFrames(othersForcedDownsyncRenderFrame, rdf)) {
|
if (self.lastAllConfirmedInputFrameId >= delayedInputFrameId && !self.equalRoomDownsyncFrames(othersForcedDownsyncRenderFrame, rdf)) {
|
||||||
console.warn(`Mismatched render frame@rdf.id=${rdf.id} w/ inputFrameId=${delayedInputFrameId}:
|
console.warn(`Mismatched render frame@rdf.id=${rdf.id} w/ inputFrameId=${delayedInputFrameId}:
|
||||||
rdf=${JSON.stringify(rdf)}
|
rdf=${JSON.stringify(rdf)}
|
||||||
othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame)}
|
othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame)}`);
|
||||||
${self._stringifyRdfIdToActuallyUsedInput()}`);
|
|
||||||
// closeWSConnection(constants.RET_CODE.CLIENT_MISMATCHED_RENDER_FRAME, "");
|
// closeWSConnection(constants.RET_CODE.CLIENT_MISMATCHED_RENDER_FRAME, "");
|
||||||
// self.onManualRejoinRequired("[DEBUG] CLIENT_MISMATCHED_RENDER_FRAME");
|
// self.onManualRejoinRequired("[DEBUG] CLIENT_MISMATCHED_RENDER_FRAME");
|
||||||
rdf = othersForcedDownsyncRenderFrame;
|
rdf = othersForcedDownsyncRenderFrame;
|
||||||
|
@ -197,7 +197,7 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
|||||||
break;
|
break;
|
||||||
case constants.RET_CODE.BATTLE_STOPPED:
|
case constants.RET_CODE.BATTLE_STOPPED:
|
||||||
// deliberately do nothing
|
// deliberately do nothing
|
||||||
console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`);
|
//console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`);
|
||||||
break;
|
break;
|
||||||
case constants.RET_CODE.PLAYER_NOT_ADDABLE_TO_ROOM:
|
case constants.RET_CODE.PLAYER_NOT_ADDABLE_TO_ROOM:
|
||||||
case constants.RET_CODE.PLAYER_NOT_READDABLE_TO_ROOM:
|
case constants.RET_CODE.PLAYER_NOT_READDABLE_TO_ROOM:
|
||||||
@ -212,7 +212,7 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
|||||||
case constants.RET_CODE.PLAYER_NOT_FOUND:
|
case constants.RET_CODE.PLAYER_NOT_FOUND:
|
||||||
case constants.RET_CODE.PLAYER_CHEATING:
|
case constants.RET_CODE.PLAYER_CHEATING:
|
||||||
case 1006: // Peer(i.e. the backend) gone unexpectedly
|
case 1006: // Peer(i.e. the backend) gone unexpectedly
|
||||||
console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`);
|
//console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`);
|
||||||
window.clearLocalStorageAndBackToLoginScene(true);
|
window.clearLocalStorageAndBackToLoginScene(true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -224,8 +224,8 @@ func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e resolv.Vector, re
|
|||||||
func WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio float64) (int32, int32) {
|
func WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio float64) (int32, int32) {
|
||||||
// [WARNING] Introduces loss of precision!
|
// [WARNING] Introduces loss of precision!
|
||||||
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
|
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
|
||||||
var virtualGridX int32 = int32(math.Round(wx * worldToVirtualGridRatio))
|
var virtualGridX int32 = int32(math.Floor(wx * worldToVirtualGridRatio))
|
||||||
var virtualGridY int32 = int32(math.Round(wy * worldToVirtualGridRatio))
|
var virtualGridY int32 = int32(math.Floor(wy * worldToVirtualGridRatio))
|
||||||
return virtualGridX, virtualGridY
|
return virtualGridX, virtualGridY
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,30 +254,42 @@ func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBounding
|
|||||||
return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
||||||
}
|
}
|
||||||
|
|
||||||
func calcHardPushbacksNorms(playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D) []Vec2D {
|
func calcHardPushbacksNorms(joinIndex int32, playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D) *[]Vec2D {
|
||||||
ret := make([]Vec2D, 0, 10) // no one would simultaneously have more than 5 hardPushbacks
|
ret := make([]Vec2D, 0, 10) // no one would simultaneously have more than 5 hardPushbacks
|
||||||
collision := playerCollider.Check(0, 0)
|
collision := playerCollider.Check(0, 0)
|
||||||
if nil == collision {
|
if nil == collision {
|
||||||
return ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//playerColliderCenterX, playerColliderCenterY := playerCollider.Center()
|
||||||
|
//fmt.Printf("joinIndex=%d calcHardPushbacksNorms has non-empty collision;playerColliderPos=(%.2f,%.2f)\n", joinIndex, playerColliderCenterX, playerColliderCenterY)
|
||||||
for _, obj := range collision.Objects {
|
for _, obj := range collision.Objects {
|
||||||
|
isBarrier := false
|
||||||
switch obj.Data.(type) {
|
switch obj.Data.(type) {
|
||||||
case *Barrier:
|
case *PlayerDownsync:
|
||||||
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
|
case *MeleeBullet:
|
||||||
overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, barrierShape)
|
|
||||||
if !overlapped {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// ALWAY snap into hardPushbacks!
|
|
||||||
// [OverlapX, OverlapY] is the unit vector that points into the platform
|
|
||||||
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapY
|
|
||||||
ret = append(ret, Vec2D{X: overlapResult.OverlapX, Y: overlapResult.OverlapY})
|
|
||||||
pEffPushback.X += pushbackX
|
|
||||||
pEffPushback.Y += pushbackY
|
|
||||||
default:
|
default:
|
||||||
|
// By default it's a regular barrier, even if data is nil, note that Golang syntax of switch-case is kind of confusing, this "default" condition is met only if "!*PlayerDownsync && !*MeleeBullet".
|
||||||
|
isBarrier = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isBarrier {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||||
|
overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, barrierShape)
|
||||||
|
if !overlapped {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// ALWAY snap into hardPushbacks!
|
||||||
|
// [OverlapX, OverlapY] is the unit vector that points into the platform
|
||||||
|
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapY
|
||||||
|
ret = append(ret, Vec2D{X: overlapResult.OverlapX, Y: overlapResult.OverlapY})
|
||||||
|
pEffPushback.X += pushbackX
|
||||||
|
pEffPushback.Y += pushbackY
|
||||||
|
//fmt.Printf("joinIndex=%d calcHardPushbacksNorms found one hardpushback; immediatePushback=(%.2f,%.2f)\n", joinIndex, pushbackX, pushbackY)
|
||||||
}
|
}
|
||||||
return ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// [WARNING] The params of this method is carefully tuned such that only "battle.RoomDownsyncFrame" is a necessary custom struct.
|
// [WARNING] The params of this method is carefully tuned such that only "battle.RoomDownsyncFrame" is a necessary custom struct.
|
||||||
@ -312,7 +324,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputList, delaye
|
|||||||
}
|
}
|
||||||
|
|
||||||
effPushbacks := make([]Vec2D, roomCapacity)
|
effPushbacks := make([]Vec2D, roomCapacity)
|
||||||
hardPushbackNorms := make([][]Vec2D, roomCapacity)
|
hardPushbackNorms := make([]*[]Vec2D, roomCapacity)
|
||||||
|
|
||||||
// 1. Process player inputs
|
// 1. Process player inputs
|
||||||
if nil != delayedInputList {
|
if nil != delayedInputList {
|
||||||
@ -385,13 +397,12 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputList, delaye
|
|||||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||||
playerCollider := collisionSysMap[collisionPlayerIndex]
|
playerCollider := collisionSysMap[collisionPlayerIndex]
|
||||||
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
|
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
hardPushbackNorms[joinIndex-1] = calcHardPushbacksNorms(playerCollider, playerShape, snapIntoPlatformOverlap, &(effPushbacks[joinIndex-1]))
|
hardPushbackNorms[joinIndex-1] = calcHardPushbacksNorms(joinIndex, playerCollider, playerShape, snapIntoPlatformOverlap, &(effPushbacks[joinIndex-1]))
|
||||||
thatPlayerInNextFrame := nextRenderFramePlayers[i]
|
thatPlayerInNextFrame := nextRenderFramePlayers[i]
|
||||||
fallStopping := false
|
landedOnGravityPushback := false
|
||||||
if collision := playerCollider.Check(0, 0); nil != collision {
|
if collision := playerCollider.Check(0, 0); nil != collision {
|
||||||
for _, obj := range collision.Objects {
|
for _, obj := range collision.Objects {
|
||||||
isBarrier, isAnotherPlayer, isBullet := false, false, false
|
isBarrier, isAnotherPlayer, isBullet := false, false, false
|
||||||
// TODO: Make this part work in JavaScript without having to expose all types Barrier/PlayerDownsync/MeleeBullet by js.MakeWrapper.
|
|
||||||
switch obj.Data.(type) {
|
switch obj.Data.(type) {
|
||||||
case *PlayerDownsync:
|
case *PlayerDownsync:
|
||||||
isAnotherPlayer = true
|
isAnotherPlayer = true
|
||||||
@ -411,17 +422,11 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputList, delaye
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
normAlignmentWithGravity := (overlapResult.OverlapX*float64(0) + overlapResult.OverlapY*float64(-1.0))
|
normAlignmentWithGravity := (overlapResult.OverlapX*float64(0) + overlapResult.OverlapY*float64(-1.0))
|
||||||
landedOnGravityPushback := (snapIntoPlatformThreshold < normAlignmentWithGravity) // prevents false snapping on the lateral sides
|
|
||||||
if landedOnGravityPushback {
|
|
||||||
// kindly note that one player might land on top of another player, and snapping is also required in such case
|
|
||||||
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapY
|
|
||||||
thatPlayerInNextFrame.InAir = false
|
|
||||||
}
|
|
||||||
if isAnotherPlayer {
|
if isAnotherPlayer {
|
||||||
// [WARNING] The "zero overlap collision" might be randomly detected/missed on either frontend or backend, to have deterministic result we added paddings to all sides of a playerCollider. As each velocity component of (velX, velY) being a multiple of 0.5 at any renderFrame, each position component of (x, y) can only be a multiple of 0.5 too, thus whenever a 1-dimensional collision happens between players from [player#1: i*0.5, player#2: j*0.5, not collided yet] to [player#1: (i+k)*0.5, player#2: j*0.5, collided], the overlap becomes (i+k-j)*0.5+2*s, and after snapping subtraction the effPushback magnitude for each player is (i+k-j)*0.5, resulting in 0.5-multiples-position for the next renderFrame.
|
// [WARNING] The "zero overlap collision" might be randomly detected/missed on either frontend or backend, to have deterministic result we added paddings to all sides of a playerCollider. As each velocity component of (velX, velY) being a multiple of 0.5 at any renderFrame, each position component of (x, y) can only be a multiple of 0.5 too, thus whenever a 1-dimensional collision happens between players from [player#1: i*0.5, player#2: j*0.5, not collided yet] to [player#1: (i+k)*0.5, player#2: j*0.5, collided], the overlap becomes (i+k-j)*0.5+2*s, and after snapping subtraction the effPushback magnitude for each player is (i+k-j)*0.5, resulting in 0.5-multiples-position for the next renderFrame.
|
||||||
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap*2)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap*2)*overlapResult.OverlapY
|
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap*2)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap*2)*overlapResult.OverlapY
|
||||||
}
|
}
|
||||||
for _, hardPushbackNorm := range hardPushbackNorms[joinIndex-1] {
|
for _, hardPushbackNorm := range *hardPushbackNorms[joinIndex-1] {
|
||||||
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
|
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
|
||||||
if isBarrier || (isAnotherPlayer && 0 > projectedMagnitude) {
|
if isBarrier || (isAnotherPlayer && 0 > projectedMagnitude) {
|
||||||
pushbackX -= projectedMagnitude * hardPushbackNorm.X
|
pushbackX -= projectedMagnitude * hardPushbackNorm.X
|
||||||
@ -430,16 +435,23 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputList, delaye
|
|||||||
}
|
}
|
||||||
effPushbacks[joinIndex-1].X += pushbackX
|
effPushbacks[joinIndex-1].X += pushbackX
|
||||||
effPushbacks[joinIndex-1].Y += pushbackY
|
effPushbacks[joinIndex-1].Y += pushbackY
|
||||||
if currPlayerDownsync.InAir && landedOnGravityPushback {
|
|
||||||
fallStopping = true
|
if snapIntoPlatformThreshold < normAlignmentWithGravity {
|
||||||
|
landedOnGravityPushback = true
|
||||||
|
//playerColliderCenterX, playerColliderCenterY := playerCollider.Center()
|
||||||
|
//fmt.Printf("joinIndex=%d landedOnGravityPushback\n{renderFrame.id: %d, isBarrier: %v, isAnotherPlayer: %v}\nhardPushbackNormsOfThisPlayer=%v, playerColliderPos=(%.2f,%.2f), immediatePushback={%.3f, %.3f}, effPushback={%.3f, %.3f}, overlapMag=%.4f\n", joinIndex, currRenderFrame.Id, isBarrier, isAnotherPlayer, *hardPushbackNorms[joinIndex-1], playerColliderCenterX, playerColliderCenterY, pushbackX, pushbackY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, overlapResult.Overlap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fallStopping {
|
if landedOnGravityPushback {
|
||||||
thatPlayerInNextFrame.VelX = 0
|
thatPlayerInNextFrame.InAir = false
|
||||||
thatPlayerInNextFrame.VelY = 0
|
if currPlayerDownsync.InAir {
|
||||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
|
// fallStopping
|
||||||
thatPlayerInNextFrame.FramesToRecover = 0
|
thatPlayerInNextFrame.VelX = 0
|
||||||
|
thatPlayerInNextFrame.VelY = 0
|
||||||
|
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
|
||||||
|
thatPlayerInNextFrame.FramesToRecover = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if currPlayerDownsync.InAir {
|
if currPlayerDownsync.InAir {
|
||||||
oldNextCharacterState := thatPlayerInNextFrame.CharacterState
|
oldNextCharacterState := thatPlayerInNextFrame.CharacterState
|
||||||
|
Loading…
Reference in New Issue
Block a user