Fixed part of frame chasing dynamics, yet collision handling is still broken.

This commit is contained in:
genxium 2022-09-25 20:48:09 +08:00
parent 1cc0eed39e
commit cccbeb1c29
4 changed files with 229 additions and 203 deletions

View File

@ -8,7 +8,8 @@
"__id__": 1 "__id__": 1
}, },
"optimizationPolicy": 0, "optimizationPolicy": 0,
"asyncLoadAssets": false "asyncLoadAssets": false,
"readonly": false
}, },
{ {
"__type__": "cc.Node", "__type__": "cc.Node",
@ -27,7 +28,6 @@
} }
], ],
"_active": true, "_active": true,
"_level": 1,
"_components": [ "_components": [
{ {
"__id__": 11 "__id__": 11
@ -63,17 +63,6 @@
"x": 0.5, "x": 0.5,
"y": 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": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -89,7 +78,19 @@
1, 1,
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", "__type__": "cc.Node",
@ -100,7 +101,6 @@
}, },
"_children": [], "_children": [],
"_active": false, "_active": false,
"_level": 0,
"_components": [ "_components": [
{ {
"__id__": 3 "__id__": 3
@ -127,17 +127,6 @@
"x": 0.5, "x": 0.5,
"y": 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": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -153,7 +142,19 @@
1, 1,
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", "__type__": "cc.Label",
@ -163,6 +164,7 @@
"__id__": 2 "__id__": 2
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_useOriginalSize": false, "_useOriginalSize": false,
"_string": "(0, 0)", "_string": "(0, 0)",
"_N$string": "(0, 0)", "_N$string": "(0, 0)",
@ -200,7 +202,6 @@
}, },
"_children": [], "_children": [],
"_active": false, "_active": false,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 6 "__id__": 6
@ -227,17 +228,6 @@
"x": 0.5, "x": 0.5,
"y": 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": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -253,7 +243,19 @@
1, 1,
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", "__type__": "cc.ParticleSystem",
@ -263,6 +265,9 @@
"__id__": 5 "__id__": 5
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_srcBlendFactor": 770,
"_dstBlendFactor": 1,
"_custom": true, "_custom": true,
"_file": { "_file": {
"__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5" "__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5"
@ -271,8 +276,6 @@
"__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3" "__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3"
}, },
"_texture": null, "_texture": null,
"_srcBlendFactor": 770,
"_dstBlendFactor": 1,
"_stopped": false, "_stopped": false,
"playOnLoad": true, "playOnLoad": true,
"autoRemoveOnFinish": false, "autoRemoveOnFinish": false,
@ -329,6 +332,7 @@
"x": 7, "x": 7,
"y": 7 "y": 7
}, },
"_positionType": 1,
"positionType": 1, "positionType": 1,
"emitterMode": 0, "emitterMode": 0,
"gravity": { "gravity": {
@ -372,7 +376,6 @@
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 9 "__id__": 9
@ -399,17 +402,6 @@
"x": 0.5, "x": 0.5,
"y": 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": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -425,7 +417,19 @@
1, 1,
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", "__type__": "cc.Sprite",
@ -435,6 +439,13 @@
"__id__": 8 "__id__": 8
}, },
"_enabled": true, "_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": { "_spriteFrame": {
"__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821" "__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821"
}, },
@ -449,12 +460,9 @@
"_fillStart": 0, "_fillStart": 0,
"_fillRange": 0, "_fillRange": 0,
"_isTrimmedMode": true, "_isTrimmedMode": true,
"_state": 0,
"_atlas": { "_atlas": {
"__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4"
}, },
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_id": "" "_id": ""
}, },
{ {
@ -476,6 +484,9 @@
"__id__": 1 "__id__": 1
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": null, "_spriteFrame": null,
"_type": 0, "_type": 0,
"_sizeMode": 0, "_sizeMode": 0,
@ -488,10 +499,7 @@
"_fillStart": 0, "_fillStart": 0,
"_fillRange": 0, "_fillRange": 0,
"_isTrimmedMode": true, "_isTrimmedMode": true,
"_state": 0,
"_atlas": null, "_atlas": null,
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_id": "" "_id": ""
}, },
{ {
@ -549,8 +557,8 @@
}, },
"_enabled": true, "_enabled": true,
"animComp": null, "animComp": null,
"baseSpeed": 300, "baseSpeed": 50,
"speed": 200, "speed": 50,
"lastMovedAt": 0, "lastMovedAt": 0,
"eps": 0.1, "eps": 0.1,
"magicLeanLowerBound": 0.414, "magicLeanLowerBound": 0.414,

View File

@ -8,7 +8,8 @@
"__id__": 1 "__id__": 1
}, },
"optimizationPolicy": 0, "optimizationPolicy": 0,
"asyncLoadAssets": false "asyncLoadAssets": false,
"readonly": false
}, },
{ {
"__type__": "cc.Node", "__type__": "cc.Node",
@ -27,7 +28,6 @@
} }
], ],
"_active": true, "_active": true,
"_level": 1,
"_components": [ "_components": [
{ {
"__id__": 11 "__id__": 11
@ -63,17 +63,6 @@
"x": 0.5, "x": 0.5,
"y": 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": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -89,7 +78,19 @@
1, 1,
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", "__type__": "cc.Node",
@ -100,7 +101,6 @@
}, },
"_children": [], "_children": [],
"_active": false, "_active": false,
"_level": 0,
"_components": [ "_components": [
{ {
"__id__": 3 "__id__": 3
@ -127,17 +127,6 @@
"x": 0.5, "x": 0.5,
"y": 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": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -153,7 +142,19 @@
1, 1,
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", "__type__": "cc.Label",
@ -163,6 +164,7 @@
"__id__": 2 "__id__": 2
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_useOriginalSize": false, "_useOriginalSize": false,
"_string": "(0, 0)", "_string": "(0, 0)",
"_N$string": "(0, 0)", "_N$string": "(0, 0)",
@ -200,7 +202,6 @@
}, },
"_children": [], "_children": [],
"_active": false, "_active": false,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 6 "__id__": 6
@ -227,17 +228,6 @@
"x": 0.5, "x": 0.5,
"y": 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": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -253,7 +243,19 @@
1, 1,
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", "__type__": "cc.ParticleSystem",
@ -263,6 +265,9 @@
"__id__": 5 "__id__": 5
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_srcBlendFactor": 770,
"_dstBlendFactor": 1,
"_custom": true, "_custom": true,
"_file": { "_file": {
"__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5" "__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5"
@ -271,8 +276,6 @@
"__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3" "__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3"
}, },
"_texture": null, "_texture": null,
"_srcBlendFactor": 770,
"_dstBlendFactor": 1,
"_stopped": false, "_stopped": false,
"playOnLoad": true, "playOnLoad": true,
"autoRemoveOnFinish": false, "autoRemoveOnFinish": false,
@ -329,6 +332,7 @@
"x": 7, "x": 7,
"y": 7 "y": 7
}, },
"_positionType": 1,
"positionType": 1, "positionType": 1,
"emitterMode": 0, "emitterMode": 0,
"gravity": { "gravity": {
@ -372,7 +376,6 @@
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 9 "__id__": 9
@ -399,17 +402,6 @@
"x": 0.5, "x": 0.5,
"y": 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": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -425,7 +417,19 @@
1, 1,
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", "__type__": "cc.Sprite",
@ -435,6 +439,13 @@
"__id__": 8 "__id__": 8
}, },
"_enabled": true, "_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": { "_spriteFrame": {
"__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821" "__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821"
}, },
@ -449,12 +460,9 @@
"_fillStart": 0, "_fillStart": 0,
"_fillRange": 0, "_fillRange": 0,
"_isTrimmedMode": true, "_isTrimmedMode": true,
"_state": 0,
"_atlas": { "_atlas": {
"__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4"
}, },
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_id": "" "_id": ""
}, },
{ {
@ -476,6 +484,9 @@
"__id__": 1 "__id__": 1
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": null, "_spriteFrame": null,
"_type": 0, "_type": 0,
"_sizeMode": 0, "_sizeMode": 0,
@ -488,10 +499,7 @@
"_fillStart": 0, "_fillStart": 0,
"_fillRange": 0, "_fillRange": 0,
"_isTrimmedMode": true, "_isTrimmedMode": true,
"_state": 0,
"_atlas": null, "_atlas": null,
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_id": "" "_id": ""
}, },
{ {
@ -549,8 +557,8 @@
}, },
"_enabled": true, "_enabled": true,
"animComp": null, "animComp": null,
"baseSpeed": 300, "baseSpeed": 50,
"speed": 200, "speed": 50,
"lastMovedAt": 0, "lastMovedAt": 0,
"eps": 0.1, "eps": 0.1,
"magicLeanLowerBound": 0.414, "magicLeanLowerBound": 0.414,

View File

@ -8,11 +8,11 @@ module.export = cc.Class({
}, },
baseSpeed: { baseSpeed: {
type: cc.Float, type: cc.Float,
default: 300, default: 50,
}, },
speed: { speed: {
type: cc.Float, type: cc.Float,
default: 300 default: 50
}, },
lastMovedAt: { lastMovedAt: {
type: cc.Float, type: cc.Float,

View File

@ -614,30 +614,31 @@ cc.Class({
} }
if (null != firstPredictedYetIncorrectInputFrameId) { if (null != firstPredictedYetIncorrectInputFrameId) {
const renderFrameId2 = self.renderFrameId;
const inputFrameId2 = self._convertToInputFrameId(renderFrameId2, self.inputDelayFrames);
const inputFrameId1 = firstPredictedYetIncorrectInputFrameId; const inputFrameId1 = firstPredictedYetIncorrectInputFrameId;
const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId" const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId"
if (renderFrameId1 < renderFrameId2) { if (renderFrameId1 < self.renderFrameId) {
/* /*
A typical case is as follows. A typical case is as follows.
-------------------------------------------------------- --------------------------------------------------------
[lastAllConfirmedRenderFrameId] : 22 [self.lastAllConfirmedRenderFrameId] : 22
<renderFrameId1> : 36 <renderFrameId1> : 36
[chaserRenderFrameId] : 62 <self.chaserRenderFrameId> : 62
<renderFrameId2> : 64 [self.renderFrameId] : 64
-------------------------------------------------------- --------------------------------------------------------
*/ */
console.warn("Mismatched input detected!: inputFrameId1:", inputFrameId1, ", renderFrameId1:", renderFrameId1); if (renderFrameId1 < self.chaserRenderFrameId) {
// The actual rollback-and-replay would later be executed in update(dt). // The actual rollback-and-replay would later be executed in update(dt).
self.chaserRenderFrameId = renderFrameId1; 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 { } 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. // 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.
console.log("Mismatched input yet no rollback needed: [inputFrameId1:", inputFrameId1, ", inputFrameId2:", inputFrameId2, "), [renderFrameId1:", renderFrameId1, ", renderFrameId2:", renderFrameId2, "). ");
} }
} }
}; };
@ -718,6 +719,9 @@ cc.Class({
self.applyRoomDownsyncFrameDynamics(rdf); self.applyRoomDownsyncFrameDynamics(rdf);
self._dumpToRenderCache(rdf); self._dumpToRenderCache(rdf);
self.battleState = ALL_BATTLE_STATES.IN_BATTLE; // Starts the increment of "self.renderFrameId" in "self.update(dt)" 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() { logBattleStats() {
@ -725,9 +729,10 @@ cc.Class({
let s = []; let s = [];
s.push("Battle stats: lastUpsyncInputFrameId=" + self.lastUpsyncInputFrameId + ", lastAllConfirmedInputFrameId=" + self.lastAllConfirmedInputFrameId); 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)); s.push(JSON.stringify(inputFrameDownsync));
}); }
console.log(s.join('\n')); console.log(s.join('\n'));
}, },
@ -776,65 +781,64 @@ cc.Class({
update(dt) { update(dt) {
const self = this; const self = this;
try { if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) {
if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) { try {
let prevSelfInput = null, currSelfInput = null; let prevSelfInput = null, currSelfInput = null;
const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here
if (self._shouldGenerateInputFrameUpsync(self.renderFrameId)) { if (self._shouldGenerateInputFrameUpsync(self.renderFrameId)) {
const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId); const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId);
prevSelfInput = prevAndCurrInputs[0]; prevSelfInput = prevAndCurrInputs[0];
currSelfInput = prevAndCurrInputs[1]; 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(); // Update countdown
if (self.shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, self.lastUpsyncInputFrameId, noDelayInputFrameId)) { if (null != self.countdownNanos) {
// TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously? self.countdownNanos -= self.rollbackEstimatedDt*1000000000;
self.sendInputFrameUpsyncBatch(noDelayInputFrameId); if (self.countdownNanos <= 0) {
} self.onBattleStopped(self.playerRichInfoDict);
return;
}
let t1 = performance.now(); const countdownSeconds = parseInt(self.countdownNanos / 1000000000);
if (self.chaserRenderFrameId <= self.renderFrameId) { if (isNaN(countdownSeconds)) {
let chaserUpperRenderFrameId = (self.chaserRenderFrameId + self.maxChasingRenderFramesPerUpdate); console.warn(`countdownSeconds is NaN for countdownNanos == ${self.countdownNanos}.`);
if (chaserUpperRenderFrameId > self.renderFrameId) chaserUpperRenderFrameId = self.renderFrameId; }
self.rollbackAndReplay(self.chaserRenderFrameId, chaserUpperRenderFrameId, self.chaserCollisionSys, self.chaserCollisionSysMap); self.countdownLabel.string = countdownSeconds;
self.chaserRenderFrameId = chaserUpperRenderFrameId; // 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();
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!! ++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) { getCachedInputFrameDownsyncWithPrediction(inputFrameId) {
const self = this; const self = this;
let inputFrameDownsync = self.recentInputCache.getByFrameId(inputFrameId); 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); const lastAllConfirmedInputFrame = self.recentInputCache.getByFrameId(self.lastAllConfirmedInputFrameId);
for (let i = 0; i < inputFrameDownsync.inputList.length; ++i) { for (let i = 0; i < inputFrameDownsync.inputList.length; ++i) {
if (i == self.selfPlayerInfo.joinIndex-1) continue; if (i == self.selfPlayerInfo.joinIndex-1) continue;
@ -1008,8 +1012,7 @@ cc.Class({
}, },
rollbackAndReplay(renderFrameIdSt, renderFrameIdEd, collisionSys, collisionSysMap) { rollbackAndReplay(renderFrameIdSt, renderFrameIdEd, collisionSys, collisionSysMap) {
if (renderFrameSt == renderFrameIdEd) { if (renderFrameSt >= renderFrameIdEd) {
console.warn("Unexpected input!");
return; return;
} }
@ -1031,18 +1034,25 @@ cc.Class({
/* /*
This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd". 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 renderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame"
const j = self._convertToInputFrameId(i, self.inputDelayFrames); 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) => { self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
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 player = renderFrame.players[playerId]; const player = renderFrame.players[playerId];
const decodedInput = self.ctrl.decodeDirection(inputs[joinIndex-1]); const encodedInput = inputList[joinIndex-1];
playerCollider.x += player.speed*decodedInput.dx*self.rollbackEstimatedDt; const decodedInput = self.ctrl.decodeDirection(encodedInput);
playerCollider.y += player.speed*decodedInput.dy*self.rollbackEstimatedDt; 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(); collisionSys.update();