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
},
"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,

View File

@ -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,

View File

@ -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,

View File

@ -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
<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).
console.warn("Mismatched input detected, resetting chaserRenderFrameId: inputFrameId1:", inputFrameId1, ", renderFrameId1:", renderFrameId1, ", chaserRenderFrameId before reset: ", self.chaserRenderFrameId);
self.chaserRenderFrameId = renderFrameId1;
} 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, "). ");
// Deliberately left blank, chasing is ongoing.
}
} else {
// 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,8 +781,8 @@ cc.Class({
update(dt) {
const self = this;
try {
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)) {
@ -793,12 +798,11 @@ cc.Class({
}
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 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.
@ -806,17 +810,20 @@ cc.Class({
self.applyRoomDownsyncFrameDynamics(rdf);
let t3 = performance.now();
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!!
/*
if (prevChaserRenderFrameId < nextChaserRenderFrameId) {
console.log("Took ", t1-t0, " milliseconds to send upsync cmds, ", t2-t1, " milliseconds to chase renderFrameIds=[", prevChaserRenderFrameId, ", ", nextChaserRenderFrameId, "], @renderFrameId=", self.renderFrameId);
}
const mapNode = self.node;
const canvasNode = mapNode.parent;
const canvasParentNode = canvasNode.parent;
if (null != window.boundRoomId) {
self.boundRoomIdLabel.string = window.boundRoomId;
*/
} 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);
}
// update countdown
// Update countdown
if (null != self.countdownNanos) {
self.countdownNanos -= self.rollbackEstimatedDt*1000000000;
if (self.countdownNanos <= 0) {
@ -830,11 +837,8 @@ cc.Class({
}
self.countdownLabel.string = countdownSeconds;
}
} catch (err) {
console.error("Error during Map.update", err);
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!!
}
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();