mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 03:39:00 +00:00
Updated frontend animation trigger mechanism.
This commit is contained in:
parent
1593965950
commit
c58e690a47
@ -2,9 +2,10 @@
|
||||
|
||||
This project is a demo for a websocket-based rollback netcode inspired by [GGPO](https://github.com/pond3r/ggpo/blob/master/doc/README.md).
|
||||
|
||||
![gif_demo](./charts/along_wall_interaction_with_reconnection.gif)
|
||||
_(the following gif is sped up to 2x for file size reduction)_
|
||||
![gif_demo](./charts/melee_attack_2.gif)
|
||||
|
||||
Please also checkout [this demo video](https://pan.baidu.com/s/1YkfuHjNLzlFVnKiEj6wrDQ?pwd=tkr5) to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance.
|
||||
Please also checkout [this demo video](https://pan.baidu.com/s/1fy0CuFKnVP_Gn2cDfrj6yg?pwd=q5uc) to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance.
|
||||
|
||||
The video mainly shows the following features.
|
||||
- The backend receives inputs from frontend peers and broadcasts back for synchronization.
|
||||
|
@ -69,6 +69,10 @@ const (
|
||||
ATK_CHARACTER_STATE_ATKED1 = 3
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_PLAYER_RADIUS = float64(16)
|
||||
)
|
||||
|
||||
// These directions are chosen such that when speed is changed to "(speedX+delta, speedY+delta)" for any of them, the direction is unchanged.
|
||||
var DIRECTION_DECODER = [][]int32{
|
||||
{0, 0},
|
||||
@ -192,8 +196,8 @@ func (pR *Room) AddPlayerIfPossible(pPlayerFromDbInit *Player, session *websocke
|
||||
pPlayerFromDbInit.AckingInputFrameId = -1
|
||||
pPlayerFromDbInit.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED
|
||||
pPlayerFromDbInit.BattleState = PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK
|
||||
pPlayerFromDbInit.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||
pPlayerFromDbInit.ColliderRadius = float64(12) // Hardcoded
|
||||
pPlayerFromDbInit.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||
pPlayerFromDbInit.ColliderRadius = DEFAULT_PLAYER_RADIUS // Hardcoded
|
||||
|
||||
pR.Players[playerId] = pPlayerFromDbInit
|
||||
pR.PlayerDownsyncSessionDict[playerId] = session
|
||||
@ -218,15 +222,16 @@ func (pR *Room) ReAddPlayerIfPossible(pTmpPlayerInstance *Player, session *webso
|
||||
* -- YFLu
|
||||
*/
|
||||
defer pR.onPlayerReAdded(playerId)
|
||||
pR.PlayerDownsyncSessionDict[playerId] = session
|
||||
pR.PlayerSignalToCloseDict[playerId] = signalToCloseConnOfThisPlayer
|
||||
pEffectiveInRoomPlayerInstance := pR.Players[playerId]
|
||||
pEffectiveInRoomPlayerInstance.AckingFrameId = -1
|
||||
pEffectiveInRoomPlayerInstance.AckingInputFrameId = -1
|
||||
pEffectiveInRoomPlayerInstance.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED
|
||||
pEffectiveInRoomPlayerInstance.BattleState = PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK
|
||||
pEffectiveInRoomPlayerInstance.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||
pEffectiveInRoomPlayerInstance.ColliderRadius = float64(16) // Hardcoded
|
||||
pEffectiveInRoomPlayerInstance.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||
pEffectiveInRoomPlayerInstance.ColliderRadius = DEFAULT_PLAYER_RADIUS // Hardcoded
|
||||
|
||||
pR.PlayerDownsyncSessionDict[playerId] = session
|
||||
pR.PlayerSignalToCloseDict[playerId] = signalToCloseConnOfThisPlayer
|
||||
|
||||
Logger.Warn("ReAddPlayerIfPossible finished.", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("joinIndex", pEffectiveInRoomPlayerInstance.JoinIndex), zap.Any("playerBattleState", pEffectiveInRoomPlayerInstance.BattleState), zap.Any("roomState", pR.State), zap.Any("roomEffectivePlayerCount", pR.EffectivePlayerCount), zap.Any("AckingFrameId", pEffectiveInRoomPlayerInstance.AckingFrameId), zap.Any("AckingInputFrameId", pEffectiveInRoomPlayerInstance.AckingInputFrameId), zap.Any("LastSentInputFrameId", pEffectiveInRoomPlayerInstance.LastSentInputFrameId))
|
||||
return true
|
||||
@ -813,7 +818,7 @@ func (pR *Room) OnDismissed() {
|
||||
X: 0,
|
||||
Y: 0,
|
||||
},
|
||||
HitboxOffset: float64(12.0), // should be about the radius of the PlayerCollider
|
||||
HitboxOffset: float64(24.0), // should be about the radius of the PlayerCollider
|
||||
HitboxSize: &Vec2D{
|
||||
X: float64(45.0),
|
||||
Y: float64(32.0),
|
||||
@ -945,6 +950,14 @@ func (pR *Room) onPlayerAdded(playerId int32) {
|
||||
panic(fmt.Sprintf("onPlayerAdded error, nil == playerPos, roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount))
|
||||
}
|
||||
pR.Players[playerId].VirtualGridX, pR.Players[playerId].VirtualGridY = WorldToVirtualGridPos(playerPos.X, playerPos.Y, pR.WorldToVirtualGridRatio)
|
||||
// Hardcoded initial character orientation/facing
|
||||
if 0 == (pR.Players[playerId].JoinIndex % 2) {
|
||||
pR.Players[playerId].DirX = -2
|
||||
pR.Players[playerId].DirY = 0
|
||||
} else {
|
||||
pR.Players[playerId].DirX = +2
|
||||
pR.Players[playerId].DirY = 0
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
# Double playback speed of a video
|
||||
```
|
||||
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" output.mp4
|
||||
```
|
||||
|
||||
# GIF creation cmd reference
|
||||
```
|
||||
ffmpeg -ss 12 -t 13 -i input.mp4 -vf "fps=10,scale=480:-1" -loop 0 output.gif
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.7 MiB |
BIN
charts/melee_attack_2.gif
Normal file
BIN
charts/melee_attack_2.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 MiB |
@ -529,8 +529,8 @@
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0.5,
|
||||
0.5,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
|
@ -40,27 +40,26 @@ cc.Class({
|
||||
BaseCharacter.prototype.onLoad.call(this);
|
||||
},
|
||||
|
||||
updateCharacterAnim(newScheduledDirection, rdfPlayer, forceAnimSwitch) {
|
||||
if (0 == rdfPlayer.framesToRecover) {
|
||||
// Update directions
|
||||
if (forceAnimSwitch || null == this.activeDirection || (null != newScheduledDirection && (newScheduledDirection.dx != this.activeDirection.dx || newScheduledDirection.dy != this.activeDirection.dy))) {
|
||||
this.activeDirection = newScheduledDirection;
|
||||
if (this.animComp && this.animComp.node) {
|
||||
if (0 > newScheduledDirection.dx) {
|
||||
this.animComp.node.scaleX = (-1.0);
|
||||
} else if (0 < newScheduledDirection.dx) {
|
||||
this.animComp.node.scaleX = (1.0);
|
||||
}
|
||||
}
|
||||
updateCharacterAnim(rdfPlayer, prevRdfPlayer, forceAnimSwitch) {
|
||||
// Update directions
|
||||
if (this.animComp && this.animComp.node) {
|
||||
if (0 > rdfPlayer.dirX) {
|
||||
this.animComp.node.scaleX = (-1.0);
|
||||
} else if (0 < rdfPlayer.dirX) {
|
||||
this.animComp.node.scaleX = (1.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Update per character state
|
||||
let newCharacterState = rdfPlayer.characterState;
|
||||
const newAnimName = window.ATK_CHARACTER_STATE_ARR[newCharacterState][1];
|
||||
if (newAnimName != this.animComp.animationName) {
|
||||
this.animComp.playAnimation(newAnimName);
|
||||
// console.log(`JoinIndex=${this.joinIndex}, Resetting anim to ${newAnimName}, dir changed: (${oldDx}, ${oldDy}) -> (${newScheduledDirection.dx}, ${newScheduledDirection.dy})`);
|
||||
let prevCharacterState = (null == prevRdfPlayer ? window.ATK_CHARACTER_STATE.Idle1[0] : prevRdfPlayer.characterState);
|
||||
if (newCharacterState != prevCharacterState) {
|
||||
// Anim is edge-triggered
|
||||
const newAnimName = window.ATK_CHARACTER_STATE_ARR[newCharacterState][1];
|
||||
if (newAnimName != this.animComp.animationName) {
|
||||
this.animComp.playAnimation(newAnimName);
|
||||
console.log(`JoinIndex=${rdfPlayer.joinIndex}, Resetting anim to ${newAnimName}, state changed: (${prevCharacterState}, prevRdfPlayer is null? ${null == prevRdfPlayer}) -> (${newCharacterState})`);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -537,7 +537,7 @@ cc.Class({
|
||||
window.initPersistentSessionClient(self.initAfterWSConnected, boundRoomId);
|
||||
} else {
|
||||
self.showPopupInCanvas(self.gameRuleNode);
|
||||
// Deliberately left blank. -- YFLu
|
||||
// Deliberately left blank. -- YFLu
|
||||
}
|
||||
},
|
||||
|
||||
@ -630,7 +630,7 @@ cc.Class({
|
||||
}
|
||||
self.transitToState(ALL_MAP_STATES.VISUAL);
|
||||
self.battleState = ALL_BATTLE_STATES.IN_BATTLE;
|
||||
self.applyRoomDownsyncFrameDynamics(rdf);
|
||||
self.applyRoomDownsyncFrameDynamics(rdf, self.recentRenderCache.getByFrameId(rdf.id - 1));
|
||||
|
||||
return dumpRenderCacheRet;
|
||||
},
|
||||
@ -752,20 +752,14 @@ cc.Class({
|
||||
playerScriptIns.setSpecies("SoldierWaterGhost");
|
||||
} else if (2 == joinIndex) {
|
||||
playerScriptIns.setSpecies("SoldierFireGhost");
|
||||
if (0 == playerDownsyncInfo.dirX && 0 == playerDownsyncInfo.dirY) {
|
||||
playerScriptIns.animComp.node.scaleX = (-1.0);
|
||||
}
|
||||
}
|
||||
|
||||
const wpos = self.virtualGridToWorldPos(vx, vy);
|
||||
|
||||
newPlayerNode.setPosition(cc.v2(wpos[0], wpos[1]));
|
||||
const [wx, wy] = self.virtualGridToWorldPos(vx, vy);
|
||||
newPlayerNode.setPosition(wx, wy);
|
||||
playerScriptIns.mapNode = self.node;
|
||||
const cpos = self.virtualGridToPolygonColliderAnchorPos(vx, vy, playerDownsyncInfo.colliderRadius, playerDownsyncInfo.colliderRadius);
|
||||
const d = playerDownsyncInfo.colliderRadius * 2,
|
||||
x0 = cpos[0],
|
||||
y0 = cpos[1];
|
||||
let pts = [[0, 0], [d, 0], [d, d], [0, d]];
|
||||
[x0, y0] = self.virtualGridToPolygonColliderAnchorPos(vx, vy, playerDownsyncInfo.colliderRadius, playerDownsyncInfo.colliderRadius),
|
||||
pts = [[0, 0], [d, 0], [d, d], [0, d]];
|
||||
|
||||
const newPlayerCollider = self.collisionSys.createPolygon(x0, y0, pts);
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
@ -776,10 +770,7 @@ cc.Class({
|
||||
setLocalZOrder(newPlayerNode, 5);
|
||||
|
||||
newPlayerNode.active = true;
|
||||
playerScriptIns.updateCharacterAnim({
|
||||
dx: playerDownsyncInfo.dirX,
|
||||
dy: playerDownsyncInfo.dirY,
|
||||
}, playerDownsyncInfo, true);
|
||||
playerScriptIns.updateCharacterAnim(playerDownsyncInfo, null, true);
|
||||
|
||||
return [newPlayerNode, playerScriptIns];
|
||||
},
|
||||
@ -798,9 +789,7 @@ cc.Class({
|
||||
currSelfInput = null;
|
||||
const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here
|
||||
if (self.shouldGenerateInputFrameUpsync(self.renderFrameId)) {
|
||||
const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId);
|
||||
prevSelfInput = prevAndCurrInputs[0];
|
||||
currSelfInput = prevAndCurrInputs[1];
|
||||
[prevSelfInput, currSelfInput] = self._generateInputFrameUpsync(noDelayInputFrameId);
|
||||
}
|
||||
|
||||
let t0 = performance.now();
|
||||
@ -820,14 +809,15 @@ cc.Class({
|
||||
let t2 = performance.now();
|
||||
|
||||
// Inside the following "self.rollbackAndChase" actually ROLLS FORWARD w.r.t. the corresponding delayedInputFrame, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now.
|
||||
const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
||||
const [prevRdf, rdf] = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
||||
/*
|
||||
const nonTrivialChaseEnded = (prevChaserRenderFrameId < nextChaserRenderFrameId && nextChaserRenderFrameId == self.renderFrameId);
|
||||
if (nonTrivialChaseEnded) {
|
||||
console.debug("Non-trivial chase ended, prevChaserRenderFrameId=" + prevChaserRenderFrameId + ", nextChaserRenderFrameId=" + nextChaserRenderFrameId);
|
||||
}
|
||||
*/
|
||||
self.applyRoomDownsyncFrameDynamics(rdf);
|
||||
// [WARNING] Don't try to get "prevRdf(i.e. renderFrameId == latest-1)" by "self.recentRenderCache.getByFrameId(...)" here, as the cache might have been updated by asynchronous "onRoomDownsyncFrame(...)" calls!
|
||||
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
|
||||
let t3 = performance.now();
|
||||
} catch (err) {
|
||||
console.error("Error during Map.update", err);
|
||||
@ -962,26 +952,17 @@ cc.Class({
|
||||
}
|
||||
},
|
||||
|
||||
applyRoomDownsyncFrameDynamics(rdf) {
|
||||
applyRoomDownsyncFrameDynamics(rdf, prevRdf) {
|
||||
const self = this;
|
||||
const delayedInputFrameForPrevRenderFrame = self.getCachedInputFrameDownsyncWithPrediction(self._convertToInputFrameId(rdf.id - 1, self.inputDelayFrames));
|
||||
|
||||
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
|
||||
for (let [playerId, playerRichInfo] of self.playerRichInfoDict.entries()) {
|
||||
const immediatePlayerInfo = rdf.players[playerId];
|
||||
const wpos = self.virtualGridToWorldPos(immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY);
|
||||
const dx = (wpos[0] - playerRichInfo.node.x);
|
||||
const dy = (wpos[1] - playerRichInfo.node.y);
|
||||
//const justJiggling = (self.jigglingEps1D >= Math.abs(dx) && self.jigglingEps1D >= Math.abs(dy));
|
||||
playerRichInfo.node.setPosition(wpos[0], wpos[1]);
|
||||
// TODO: check "rdf.players[playerId].characterState" instead, might have to play Atk/Atked anim!
|
||||
if (null != delayedInputFrameForPrevRenderFrame) {
|
||||
const decodedInput = self.ctrl.decodeInput(delayedInputFrameForPrevRenderFrame.inputList[playerRichInfo.joinIndex - 1]);
|
||||
playerRichInfo.scriptIns.updateCharacterAnim(decodedInput, immediatePlayerInfo, false);
|
||||
} else {
|
||||
playerRichInfo.scriptIns.updateCharacterAnim(null, immediatePlayerInfo, false);
|
||||
}
|
||||
const prevRdfPlayer = (null == prevRdf ? null : prevRdf.players[playerId]);
|
||||
const [wx, wy] = self.virtualGridToWorldPos(immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY);
|
||||
//const justJiggling = (self.jigglingEps1D >= Math.abs(wx - playerRichInfo.node.x) && self.jigglingEps1D >= Math.abs(wy - playerRichInfo.node.y));
|
||||
playerRichInfo.node.setPosition(wx, wy);
|
||||
playerRichInfo.scriptIns.updateSpeed(immediatePlayerInfo.speed);
|
||||
});
|
||||
playerRichInfo.scriptIns.updateCharacterAnim(immediatePlayerInfo, prevRdfPlayer, false);
|
||||
}
|
||||
},
|
||||
|
||||
getCachedInputFrameDownsyncWithPrediction(inputFrameId) {
|
||||
@ -1044,9 +1025,7 @@ cc.Class({
|
||||
|
||||
const newVx = currPlayerDownsync.virtualGridX;
|
||||
const newVy = currPlayerDownsync.virtualGridY;
|
||||
const newCpos = self.virtualGridToPolygonColliderAnchorPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1].colliderRadius, self.playerRichInfoArr[joinIndex - 1].colliderRadius);
|
||||
playerCollider.x = newCpos[0];
|
||||
playerCollider.y = newCpos[1];
|
||||
[playerCollider.x, playerCollider.y] = self.virtualGridToPolygonColliderAnchorPos(newVx, newVy, self.playerRichInfoArr[joinIndex - 1].colliderRadius, self.playerRichInfoArr[joinIndex - 1].colliderRadius);
|
||||
}
|
||||
|
||||
// Check bullet-anything collisions first, because the pushbacks caused by bullets might later be reverted by player-barrier collision
|
||||
@ -1068,16 +1047,15 @@ cc.Class({
|
||||
if (0 > offender.dirX) {
|
||||
xfac = -1;
|
||||
}
|
||||
const offenderWpos = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY);
|
||||
const bulletWx = offenderWpos[0] + xfac * meleeBullet.hitboxOffset;
|
||||
const bulletWy = offenderWpos[1];
|
||||
const bulletCpos = self.worldToPolygonColliderAnchorPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5);
|
||||
const pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
|
||||
const newBulletCollider = collisionSys.createPolygon(bulletCpos[0], bulletCpos[1], pts);
|
||||
const [offenderWx, offenderWy] = self.virtualGridToWorldPos(offender.virtualGridX, offender.virtualGridY);
|
||||
const bulletWx = offenderWx + xfac * meleeBullet.hitboxOffset;
|
||||
const bulletWy = offenderWy;
|
||||
const [bulletCx, bulletCy] = self.worldToPolygonColliderAnchorPos(bulletWx, bulletWy, meleeBullet.hitboxSize.x * 0.5, meleeBullet.hitboxSize.y * 0.5), pts = [[0, 0], [meleeBullet.hitboxSize.x, 0], [meleeBullet.hitboxSize.x, meleeBullet.hitboxSize.y], [0, meleeBullet.hitboxSize.y]];
|
||||
const newBulletCollider = collisionSys.createPolygon(bulletCx, bulletCy, pts);
|
||||
newBulletCollider.data = meleeBullet;
|
||||
collisionSysMap.set(collisionBulletIndex, newBulletCollider);
|
||||
bulletColliders.set(collisionBulletIndex, newBulletCollider);
|
||||
// console.log(`A meleeBullet is added to collisionSys at currRenderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended: ${JSON.stringify(meleeBullet)}`);
|
||||
// console.log(`A meleeBullet is added to collisionSys at currRenderFrame.id=${currRenderFrame.id} as start-up frames ended and active frame is not yet ended: ${JSON.stringify(meleeBullet)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1180,9 +1158,9 @@ cc.Class({
|
||||
} else {
|
||||
thatPlayerInNextFrame.characterState = window.ATK_CHARACTER_STATE.Idle1[0];
|
||||
}
|
||||
const movement = self.virtualGridToWorldPos(decodedInput.dx + currPlayerDownsync.speed * decodedInput.dx, decodedInput.dy + currPlayerDownsync.speed * decodedInput.dy);
|
||||
playerCollider.x += movement[0];
|
||||
playerCollider.y += movement[1];
|
||||
const [movementX, movementY] = self.virtualGridToWorldPos(decodedInput.dx + currPlayerDownsync.speed * decodedInput.dx, decodedInput.dy + currPlayerDownsync.speed * decodedInput.dy);
|
||||
playerCollider.x += movementX;
|
||||
playerCollider.y += movementY;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1209,10 +1187,8 @@ cc.Class({
|
||||
const playerId = self.playerRichInfoArr[j].id;
|
||||
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
|
||||
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
|
||||
const newVpos = self.polygonColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j].colliderRadius, self.playerRichInfoArr[j].colliderRadius);
|
||||
const thatPlayerInNextFrame = nextRenderFramePlayers[playerId];
|
||||
thatPlayerInNextFrame.virtualGridX = newVpos[0];
|
||||
thatPlayerInNextFrame.virtualGridY = newVpos[1];
|
||||
[thatPlayerInNextFrame.virtualGridX, thatPlayerInNextFrame.virtualGridY] = self.polygonColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[joinIndex - 1][0], playerCollider.y - effPushbacks[joinIndex - 1][1], self.playerRichInfoArr[j].colliderRadius, self.playerRichInfoArr[j].colliderRadius);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1225,29 +1201,30 @@ cc.Class({
|
||||
This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd" if not interruptted.
|
||||
*/
|
||||
const self = this;
|
||||
let prevLatestRdf = null;
|
||||
let latestRdf = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame"
|
||||
if (null == latestRdf) {
|
||||
console.error(`Couldn't find renderFrameId=${renderFrameIdSt}, to rollback, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`);
|
||||
return latestRdf;
|
||||
return [prevLatestRdf, latestRdf];
|
||||
}
|
||||
|
||||
if (renderFrameIdSt >= renderFrameIdEd) {
|
||||
return latestRdf;
|
||||
return [prevLatestRdf, latestRdf];
|
||||
}
|
||||
|
||||
for (let i = renderFrameIdSt; i < renderFrameIdEd; ++i) {
|
||||
const currRenderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame"; [WARNING] When "true == isChasing", this function can be interruptted by "onRoomDownsyncFrame(rdf)" asynchronously anytime, making this line return "null"!
|
||||
if (null == currRenderFrame) {
|
||||
console.warn(`Couldn't find renderFrame for i=${i} to rollback, self.renderFrameId=${self.renderFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, might've been interruptted by onRoomDownsyncFrame`);
|
||||
return latestRdf;
|
||||
return [prevLatestRdf, latestRdf];
|
||||
}
|
||||
const j = self._convertToInputFrameId(i, self.inputDelayFrames);
|
||||
const delayedInputFrame = self.getCachedInputFrameDownsyncWithPrediction(j);
|
||||
if (null == delayedInputFrame) {
|
||||
console.warn(`Failed to get cached delayedInputFrame for i=${i}, j=${j}, self.renderFrameId=${self.renderFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}`);
|
||||
return latestRdf;
|
||||
return [prevLatestRdf, latestRdf];
|
||||
}
|
||||
|
||||
prevLatestRdf = latestRdf;
|
||||
latestRdf = self.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap);
|
||||
if (
|
||||
self._allConfirmed(delayedInputFrame.confirmedList)
|
||||
@ -1269,7 +1246,7 @@ cc.Class({
|
||||
self.dumpToRenderCache(latestRdf);
|
||||
}
|
||||
|
||||
return latestRdf;
|
||||
return [prevLatestRdf, latestRdf];
|
||||
},
|
||||
|
||||
_initPlayerRichInfoDict(players) {
|
||||
@ -1280,16 +1257,16 @@ cc.Class({
|
||||
const immediatePlayerInfo = players[playerId];
|
||||
self.playerRichInfoDict.set(playerId, immediatePlayerInfo);
|
||||
|
||||
const nodeAndScriptIns = self.spawnPlayerNode(immediatePlayerInfo.joinIndex, immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY, self.playerRichInfoDict.get(playerId));
|
||||
const [theNode, theScriptIns] = self.spawnPlayerNode(immediatePlayerInfo.joinIndex, immediatePlayerInfo.virtualGridX, immediatePlayerInfo.virtualGridY, immediatePlayerInfo);
|
||||
|
||||
Object.assign(self.playerRichInfoDict.get(playerId), {
|
||||
node: nodeAndScriptIns[0],
|
||||
scriptIns: nodeAndScriptIns[1]
|
||||
node: theNode,
|
||||
scriptIns: theScriptIns,
|
||||
});
|
||||
|
||||
if (self.selfPlayerInfo.id == playerId) {
|
||||
self.selfPlayerInfo = Object.assign(self.selfPlayerInfo, immediatePlayerInfo);
|
||||
nodeAndScriptIns[1].showArrowTipNode();
|
||||
theScriptIns.showArrowTipNode();
|
||||
}
|
||||
}
|
||||
self.playerRichInfoArr = new Array(self.playerRichInfoDict.size);
|
||||
@ -1351,13 +1328,13 @@ cc.Class({
|
||||
|
||||
polygonColliderAnchorToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH) {
|
||||
const self = this;
|
||||
const wpos = self.polygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH);
|
||||
return self.worldToVirtualGridPos(wpos[0], wpos[1])
|
||||
const [wx, wy] = self.polygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH);
|
||||
return self.worldToVirtualGridPos(wx, wy)
|
||||
},
|
||||
|
||||
virtualGridToPolygonColliderAnchorPos(vx, vy, halfBoundingW, halfBoundingH) {
|
||||
const self = this;
|
||||
const wpos = self.virtualGridToWorldPos(vx, vy);
|
||||
return self.worldToPolygonColliderAnchorPos(wpos[0], wpos[1], halfBoundingW, halfBoundingH)
|
||||
const [wx, wy] = self.virtualGridToWorldPos(vx, vy);
|
||||
return self.worldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH)
|
||||
},
|
||||
});
|
||||
|
@ -191,8 +191,8 @@ cc.Class({
|
||||
currSelfInput = prevAndCurrInputs[1];
|
||||
}
|
||||
|
||||
const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
||||
self.applyRoomDownsyncFrameDynamics(rdf);
|
||||
const [prevRdf, rdf] = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
|
||||
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
|
||||
let t3 = performance.now();
|
||||
} catch (err) {
|
||||
console.error("Error during Map.update", err);
|
||||
|
Loading…
Reference in New Issue
Block a user