From cccbeb1c2982a4077b3b31bdad90417e102061c8 Mon Sep 17 00:00:00 2001 From: genxium Date: Sun, 25 Sep 2022 20:48:09 +0800 Subject: [PATCH] Fixed part of frame chasing dynamics, yet collision handling is still broken. --- .../assets/resources/prefabs/Pacman1.prefab | 134 ++++++++------- .../assets/resources/prefabs/Pacman2.prefab | 134 ++++++++------- frontend/assets/scripts/BasePlayer.js | 4 +- frontend/assets/scripts/Map.js | 160 ++++++++++-------- 4 files changed, 229 insertions(+), 203 deletions(-) diff --git a/frontend/assets/resources/prefabs/Pacman1.prefab b/frontend/assets/resources/prefabs/Pacman1.prefab index bfc71fe..7d5723c 100644 --- a/frontend/assets/resources/prefabs/Pacman1.prefab +++ b/frontend/assets/resources/prefabs/Pacman1.prefab @@ -8,7 +8,8 @@ "__id__": 1 }, "optimizationPolicy": 0, - "asyncLoadAssets": false + "asyncLoadAssets": false, + "readonly": false }, { "__type__": "cc.Node", @@ -27,7 +28,6 @@ } ], "_active": true, - "_level": 1, "_components": [ { "__id__": 11 @@ -63,17 +63,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 2, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -89,7 +78,19 @@ 1, 1 ] - } + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 2, + "groupIndex": 2, + "_id": "" }, { "__type__": "cc.Node", @@ -100,7 +101,6 @@ }, "_children": [], "_active": false, - "_level": 0, "_components": [ { "__id__": 3 @@ -127,17 +127,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -153,7 +142,19 @@ 1, 1 ] - } + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" }, { "__type__": "cc.Label", @@ -163,6 +164,7 @@ "__id__": 2 }, "_enabled": true, + "_materials": [], "_useOriginalSize": false, "_string": "(0, 0)", "_N$string": "(0, 0)", @@ -200,7 +202,6 @@ }, "_children": [], "_active": false, - "_level": 2, "_components": [ { "__id__": 6 @@ -227,17 +228,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -253,7 +243,19 @@ 1, 1 ] - } + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" }, { "__type__": "cc.ParticleSystem", @@ -263,6 +265,9 @@ "__id__": 5 }, "_enabled": true, + "_materials": [], + "_srcBlendFactor": 770, + "_dstBlendFactor": 1, "_custom": true, "_file": { "__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5" @@ -271,8 +276,6 @@ "__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3" }, "_texture": null, - "_srcBlendFactor": 770, - "_dstBlendFactor": 1, "_stopped": false, "playOnLoad": true, "autoRemoveOnFinish": false, @@ -329,6 +332,7 @@ "x": 7, "y": 7 }, + "_positionType": 1, "positionType": 1, "emitterMode": 0, "gravity": { @@ -372,7 +376,6 @@ }, "_children": [], "_active": true, - "_level": 2, "_components": [ { "__id__": 9 @@ -399,17 +402,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -425,7 +417,19 @@ 1, 1 ] - } + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" }, { "__type__": "cc.Sprite", @@ -435,6 +439,13 @@ "__id__": 8 }, "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, "_spriteFrame": { "__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821" }, @@ -449,12 +460,9 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_state": 0, "_atlas": { "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" }, - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, "_id": "" }, { @@ -476,6 +484,9 @@ "__id__": 1 }, "_enabled": true, + "_materials": [], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, "_spriteFrame": null, "_type": 0, "_sizeMode": 0, @@ -488,10 +499,7 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_state": 0, "_atlas": null, - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, "_id": "" }, { @@ -549,8 +557,8 @@ }, "_enabled": true, "animComp": null, - "baseSpeed": 300, - "speed": 200, + "baseSpeed": 50, + "speed": 50, "lastMovedAt": 0, "eps": 0.1, "magicLeanLowerBound": 0.414, diff --git a/frontend/assets/resources/prefabs/Pacman2.prefab b/frontend/assets/resources/prefabs/Pacman2.prefab index 84058c2..cc5b6c8 100644 --- a/frontend/assets/resources/prefabs/Pacman2.prefab +++ b/frontend/assets/resources/prefabs/Pacman2.prefab @@ -8,7 +8,8 @@ "__id__": 1 }, "optimizationPolicy": 0, - "asyncLoadAssets": false + "asyncLoadAssets": false, + "readonly": false }, { "__type__": "cc.Node", @@ -27,7 +28,6 @@ } ], "_active": true, - "_level": 1, "_components": [ { "__id__": 11 @@ -63,17 +63,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 2, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -89,7 +78,19 @@ 1, 1 ] - } + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 2, + "groupIndex": 2, + "_id": "" }, { "__type__": "cc.Node", @@ -100,7 +101,6 @@ }, "_children": [], "_active": false, - "_level": 0, "_components": [ { "__id__": 3 @@ -127,17 +127,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -153,7 +142,19 @@ 1, 1 ] - } + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" }, { "__type__": "cc.Label", @@ -163,6 +164,7 @@ "__id__": 2 }, "_enabled": true, + "_materials": [], "_useOriginalSize": false, "_string": "(0, 0)", "_N$string": "(0, 0)", @@ -200,7 +202,6 @@ }, "_children": [], "_active": false, - "_level": 2, "_components": [ { "__id__": 6 @@ -227,17 +228,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -253,7 +243,19 @@ 1, 1 ] - } + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" }, { "__type__": "cc.ParticleSystem", @@ -263,6 +265,9 @@ "__id__": 5 }, "_enabled": true, + "_materials": [], + "_srcBlendFactor": 770, + "_dstBlendFactor": 1, "_custom": true, "_file": { "__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5" @@ -271,8 +276,6 @@ "__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3" }, "_texture": null, - "_srcBlendFactor": 770, - "_dstBlendFactor": 1, "_stopped": false, "playOnLoad": true, "autoRemoveOnFinish": false, @@ -329,6 +332,7 @@ "x": 7, "y": 7 }, + "_positionType": 1, "positionType": 1, "emitterMode": 0, "gravity": { @@ -372,7 +376,6 @@ }, "_children": [], "_active": true, - "_level": 2, "_components": [ { "__id__": 9 @@ -399,17 +402,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -425,7 +417,19 @@ 1, 1 ] - } + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" }, { "__type__": "cc.Sprite", @@ -435,6 +439,13 @@ "__id__": 8 }, "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, "_spriteFrame": { "__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821" }, @@ -449,12 +460,9 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_state": 0, "_atlas": { "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" }, - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, "_id": "" }, { @@ -476,6 +484,9 @@ "__id__": 1 }, "_enabled": true, + "_materials": [], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, "_spriteFrame": null, "_type": 0, "_sizeMode": 0, @@ -488,10 +499,7 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_state": 0, "_atlas": null, - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, "_id": "" }, { @@ -549,8 +557,8 @@ }, "_enabled": true, "animComp": null, - "baseSpeed": 300, - "speed": 200, + "baseSpeed": 50, + "speed": 50, "lastMovedAt": 0, "eps": 0.1, "magicLeanLowerBound": 0.414, diff --git a/frontend/assets/scripts/BasePlayer.js b/frontend/assets/scripts/BasePlayer.js index 2d1840b..72d2b53 100644 --- a/frontend/assets/scripts/BasePlayer.js +++ b/frontend/assets/scripts/BasePlayer.js @@ -8,11 +8,11 @@ module.export = cc.Class({ }, baseSpeed: { type: cc.Float, - default: 300, + default: 50, }, speed: { type: cc.Float, - default: 300 + default: 50 }, lastMovedAt: { type: cc.Float, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 482c8ef..507a562 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -614,30 +614,31 @@ cc.Class({ } if (null != firstPredictedYetIncorrectInputFrameId) { - const renderFrameId2 = self.renderFrameId; - const inputFrameId2 = self._convertToInputFrameId(renderFrameId2, self.inputDelayFrames); const inputFrameId1 = firstPredictedYetIncorrectInputFrameId; const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId" - if (renderFrameId1 < renderFrameId2) { + if (renderFrameId1 < self.renderFrameId) { /* A typical case is as follows. -------------------------------------------------------- - [lastAllConfirmedRenderFrameId] : 22 + [self.lastAllConfirmedRenderFrameId] : 22 - : 36 + : 36 - [chaserRenderFrameId] : 62 + : 62 - : 64 + [self.renderFrameId] : 64 -------------------------------------------------------- */ - console.warn("Mismatched input detected!: inputFrameId1:", inputFrameId1, ", renderFrameId1:", renderFrameId1); - // The actual rollback-and-replay would later be executed in update(dt). - self.chaserRenderFrameId = renderFrameId1; + if (renderFrameId1 < self.chaserRenderFrameId) { + // The actual rollback-and-replay would later be executed in update(dt). + console.warn("Mismatched input detected, resetting chaserRenderFrameId: inputFrameId1:", inputFrameId1, ", renderFrameId1:", renderFrameId1, ", chaserRenderFrameId before reset: ", self.chaserRenderFrameId); + self.chaserRenderFrameId = renderFrameId1; + } else { + // Deliberately left blank, chasing is ongoing. + } } else { - // No need to rollback when "renderFrameId1 == renderFrameId2", because the "delayedInputFrame for renderFrameId2" is not yet executed by now, it just went through "++self.renderFrameId" in "update(dt)" and javascript-runtime is mostly single-threaded in our programmable range. - console.log("Mismatched input yet no rollback needed: [inputFrameId1:", inputFrameId1, ", inputFrameId2:", inputFrameId2, "), [renderFrameId1:", renderFrameId1, ", renderFrameId2:", renderFrameId2, "). "); + // No need to rollback when "renderFrameId1 == self.renderFrameId", because the "corresponding delayedInputFrame for renderFrameId2" is NOT YET EXECUTED BY NOW, it just went through "++self.renderFrameId" in "update(dt)" and javascript-runtime is mostly single-threaded in our programmable range. } } }; @@ -718,6 +719,9 @@ cc.Class({ self.applyRoomDownsyncFrameDynamics(rdf); self._dumpToRenderCache(rdf); self.battleState = ALL_BATTLE_STATES.IN_BATTLE; // Starts the increment of "self.renderFrameId" in "self.update(dt)" + if (null != window.boundRoomId) { + self.boundRoomIdLabel.string = window.boundRoomId; + } }, logBattleStats() { @@ -725,9 +729,10 @@ cc.Class({ let s = []; s.push("Battle stats: lastUpsyncInputFrameId=" + self.lastUpsyncInputFrameId + ", lastAllConfirmedInputFrameId=" + self.lastAllConfirmedInputFrameId); - self.recentInputCache.forEach((inputFrameDownsync, inputFrameId) => { + for (let i = self.recentInputCache.stFrameId; i < self.recentInputCache.edFrameId; ++i) { + const inputFrameDownsync = self.recentInputCache.getByFrameId(i); s.push(JSON.stringify(inputFrameDownsync)); - }); + } console.log(s.join('\n')); }, @@ -776,65 +781,64 @@ cc.Class({ update(dt) { const self = this; - try { - if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) { - let prevSelfInput = null, 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]; + if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) { + try { + let prevSelfInput = null, 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]; + } + + let t0 = performance.now(); + if (self.shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, self.lastUpsyncInputFrameId, noDelayInputFrameId)) { + // TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously? + self.sendInputFrameUpsyncBatch(noDelayInputFrameId); + } + + let t1 = performance.now(); + const prevChaserRenderFrameId = self.chaserRenderFrameId; + let nextChaserRenderFrameId = (prevChaserRenderFrameId + self.maxChasingRenderFramesPerUpdate); + if (nextChaserRenderFrameId > self.renderFrameId) nextChaserRenderFrameId = self.renderFrameId; + self.rollbackAndReplay(prevChaserRenderFrameId, nextChaserRenderFrameId, self.chaserCollisionSys, self.chaserCollisionSysMap); + self.chaserRenderFrameId = nextChaserRenderFrameId; // Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic! + let t2 = performance.now(); + + // Inside "self.rollbackAndReplay", the "self.latestCollisionSys" is ALWAYS ROLLED BACK to "self.recentRenderCache.get(self.renderFrameId)" before being applied dynamics from corresponding inputFrameDownsync, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now. + const rdf = self.rollbackAndReplay(self.renderFrameId, self.renderFrameId+1, self.latestCollisionSys, self.latestCollisionSysMap); + + self.applyRoomDownsyncFrameDynamics(rdf); + let t3 = performance.now(); + /* + if (prevChaserRenderFrameId < nextChaserRenderFrameId) { + console.log("Took ", t1-t0, " milliseconds to send upsync cmds, ", t2-t1, " milliseconds to chase renderFrameIds=[", prevChaserRenderFrameId, ", ", nextChaserRenderFrameId, "], @renderFrameId=", self.renderFrameId); + } + */ + } catch (err) { + console.error("Error during Map.update", err); + } finally { + // Update camera to track selfPlayer. + if (null != self.ctrl) { + self.ctrl.justifyMapNodePosAndScale(self.ctrl.linearSpeedBase, self.ctrl.zoomingSpeedBase); } - let t0 = performance.now(); - if (self.shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, self.lastUpsyncInputFrameId, noDelayInputFrameId)) { - // TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously? - self.sendInputFrameUpsyncBatch(noDelayInputFrameId); - } + // Update countdown + if (null != self.countdownNanos) { + self.countdownNanos -= self.rollbackEstimatedDt*1000000000; + if (self.countdownNanos <= 0) { + self.onBattleStopped(self.playerRichInfoDict); + return; + } - let t1 = performance.now(); - if (self.chaserRenderFrameId <= self.renderFrameId) { - let chaserUpperRenderFrameId = (self.chaserRenderFrameId + self.maxChasingRenderFramesPerUpdate); - if (chaserUpperRenderFrameId > self.renderFrameId) chaserUpperRenderFrameId = self.renderFrameId; - self.rollbackAndReplay(self.chaserRenderFrameId, chaserUpperRenderFrameId, self.chaserCollisionSys, self.chaserCollisionSysMap); - self.chaserRenderFrameId = chaserUpperRenderFrameId; // Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic! + const countdownSeconds = parseInt(self.countdownNanos / 1000000000); + if (isNaN(countdownSeconds)) { + console.warn(`countdownSeconds is NaN for countdownNanos == ${self.countdownNanos}.`); + } + self.countdownLabel.string = countdownSeconds; } - let t2 = performance.now(); - - // Inside "self.rollbackAndReplay", the "self.latestCollisionSys" is ALWAYS ROLLED BACK to "self.recentRenderCache.get(self.renderFrameId)" before being applied dynamics from corresponding inputFrameDownsync, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now. - const rdf = self.rollbackAndReplay(self.renderFrameId, self.renderFrameId+1, self.latestCollisionSys, self.latestCollisionSysMap); - - self.applyRoomDownsyncFrameDynamics(rdf); - let t3 = performance.now(); - ++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!! } - const mapNode = self.node; - const canvasNode = mapNode.parent; - const canvasParentNode = canvasNode.parent; - if (null != window.boundRoomId) { - self.boundRoomIdLabel.string = window.boundRoomId; - } - - // update countdown - if (null != self.countdownNanos) { - self.countdownNanos -= self.rollbackEstimatedDt*1000000000; - if (self.countdownNanos <= 0) { - self.onBattleStopped(self.playerRichInfoDict); - return; - } - - const countdownSeconds = parseInt(self.countdownNanos / 1000000000); - if (isNaN(countdownSeconds)) { - console.warn(`countdownSeconds is NaN for countdownNanos == ${self.countdownNanos}.`); - } - self.countdownLabel.string = countdownSeconds; - } - } catch (err) { - console.error("Error during Map.update", err); - } - if (null != self.ctrl) { - self.ctrl.justifyMapNodePosAndScale(self.ctrl.linearSpeedBase, self.ctrl.zoomingSpeedBase); } }, @@ -996,7 +1000,7 @@ cc.Class({ getCachedInputFrameDownsyncWithPrediction(inputFrameId) { const self = this; let inputFrameDownsync = self.recentInputCache.getByFrameId(inputFrameId); - if (-1 != self.lastAllConfirmedInputFrameId && inputFrameId > self.lastAllConfirmedInputFrameId) { + if (null != inputFrameDownsync && -1 != self.lastAllConfirmedInputFrameId && inputFrameId > self.lastAllConfirmedInputFrameId) { const lastAllConfirmedInputFrame = self.recentInputCache.getByFrameId(self.lastAllConfirmedInputFrameId); for (let i = 0; i < inputFrameDownsync.inputList.length; ++i) { if (i == self.selfPlayerInfo.joinIndex-1) continue; @@ -1008,8 +1012,7 @@ cc.Class({ }, rollbackAndReplay(renderFrameIdSt, renderFrameIdEd, collisionSys, collisionSysMap) { - if (renderFrameSt == renderFrameIdEd) { - console.warn("Unexpected input!"); + if (renderFrameSt >= renderFrameIdEd) { return; } @@ -1031,18 +1034,25 @@ cc.Class({ /* This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd". */ - for (let i = renderFrameSt; i < renderFrameIdEd; ++i) { + for (let i = renderFrameIdSt; i < renderFrameIdEd; ++i) { const renderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame" const j = self._convertToInputFrameId(i, self.inputDelayFrames); - const inputs = self.recentInputCache.getByFrameId(j).inputList; + const inputList = self.getCachedInputFrameDownsyncWithPrediction(j).inputList; self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { const joinIndex = playerRichInfo.joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const playerCollider = collisionSysMap.get(collisionPlayerIndex); const player = renderFrame.players[playerId]; - const decodedInput = self.ctrl.decodeDirection(inputs[joinIndex-1]); - playerCollider.x += player.speed*decodedInput.dx*self.rollbackEstimatedDt; - playerCollider.y += player.speed*decodedInput.dy*self.rollbackEstimatedDt; + const encodedInput = inputList[joinIndex-1]; + const decodedInput = self.ctrl.decodeDirection(encodedInput); + const baseChange = player.speed*self.rollbackEstimatedDt; + playerCollider.x += baseChange*decodedInput.dx; + playerCollider.y += baseChange*decodedInput.dy; + /* + if (0 < encodedInput) { + console.log("playerId=", playerId, "@renderFrameId=", i, ", delayedInputFrameId=", j, ", baseChange=", baseChange, ": x=", playerCollider.x, ", y=", playerCollider.y); + } + */ }); collisionSys.update();