mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 11:48:56 +00:00
Simplified frontend anim handling.
This commit is contained in:
parent
fa491b357d
commit
52be2a6a79
@ -2,10 +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).
|
This project is a demo for a websocket-based rollback netcode inspired by [GGPO](https://github.com/pond3r/ggpo/blob/master/doc/README.md).
|
||||||
|
|
||||||
_(the following gif is sped up to 2x for file size reduction)_
|
_(the following gif is sped up to ~1.5x for file size reduction, kindly note that around ~11s countdown, the attack animation is resumed from a partial progress)_
|
||||||
![gif_demo](./charts/melee_attack_2.gif)
|
![gif_demo](./charts/melee_attack_fractional_anim_resume_spedup.gif)
|
||||||
|
|
||||||
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.
|
Please also checkout [this demo video](https://pan.baidu.com/s/1U1wb7KWyHorZElNWcS5HHA?pwd=30wh) 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 video mainly shows the following features.
|
||||||
- The backend receives inputs from frontend peers and broadcasts back for synchronization.
|
- The backend receives inputs from frontend peers and broadcasts back for synchronization.
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 5.3 MiB |
BIN
charts/melee_attack_fractional_anim_resume_spedup.gif
Normal file
BIN
charts/melee_attack_fractional_anim_resume_spedup.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 MiB |
@ -440,7 +440,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
210.4441731196186,
|
216.50635094610968,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -454,7 +454,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
210.4441731196186,
|
216.50635094610968,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -32,7 +32,7 @@ cc.Class({
|
|||||||
this.speciesName = speciesName;
|
this.speciesName = speciesName;
|
||||||
this.effAnimNode = this.animNode.getChildByName(this.speciesName);
|
this.effAnimNode = this.animNode.getChildByName(this.speciesName);
|
||||||
this.animComp = this.effAnimNode.getComponent(dragonBones.ArmatureDisplay);
|
this.animComp = this.effAnimNode.getComponent(dragonBones.ArmatureDisplay);
|
||||||
this.animComp.playAnimation(ATK_CHARACTER_STATE.Idle1[1]);
|
this.animComp.playAnimation(ATK_CHARACTER_STATE.Idle1[1]); // [WARNING] This is the only exception ccc's wrapper is used!
|
||||||
this.effAnimNode.active = true;
|
this.effAnimNode.active = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -41,6 +41,7 @@ cc.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateCharacterAnim(rdfPlayer, prevRdfPlayer, forceAnimSwitch) {
|
updateCharacterAnim(rdfPlayer, prevRdfPlayer, forceAnimSwitch) {
|
||||||
|
const underlyingAnimationCtrl = this.animComp._armature.animation; // ALWAYS use the dragonBones api instead of ccc's wrapper!
|
||||||
// Update directions
|
// Update directions
|
||||||
if (this.animComp && this.animComp.node) {
|
if (this.animComp && this.animComp.node) {
|
||||||
if (0 > rdfPlayer.dirX) {
|
if (0 > rdfPlayer.dirX) {
|
||||||
@ -54,46 +55,47 @@ cc.Class({
|
|||||||
let newCharacterState = rdfPlayer.characterState;
|
let newCharacterState = rdfPlayer.characterState;
|
||||||
let prevCharacterState = (null == prevRdfPlayer ? window.ATK_CHARACTER_STATE.Idle1[0] : prevRdfPlayer.characterState);
|
let prevCharacterState = (null == prevRdfPlayer ? window.ATK_CHARACTER_STATE.Idle1[0] : prevRdfPlayer.characterState);
|
||||||
const newAnimName = window.ATK_CHARACTER_STATE_ARR[newCharacterState][1];
|
const newAnimName = window.ATK_CHARACTER_STATE_ARR[newCharacterState][1];
|
||||||
|
const playingAnimName = underlyingAnimationCtrl.lastAnimationName;
|
||||||
|
const isPlaying = underlyingAnimationCtrl.isPlaying;
|
||||||
|
|
||||||
// As this function might be called after many frames of a rollback, it's possible that the playing animation was predicted, different from "prevCharacterState" but same as "newCharacterState". More granular checks are needed to determine whether we should interrupt the playing animation.
|
// As this function might be called after many frames of a rollback, it's possible that the playing animation was predicted, different from "prevCharacterState" but same as "newCharacterState". More granular checks are needed to determine whether we should interrupt the playing animation.
|
||||||
if (newCharacterState != prevCharacterState) {
|
if (newCharacterState != prevCharacterState) {
|
||||||
if (newAnimName == this.animComp.animationName) {
|
if (newAnimName == playingAnimName) {
|
||||||
if (ATK_CHARACTER_STATE.Idle1[0] == newCharacterState || ATK_CHARACTER_STATE.Walking[0] == newCharacterState) {
|
if (ATK_CHARACTER_STATE.Idle1[0] == newCharacterState || ATK_CHARACTER_STATE.Walking[0] == newCharacterState) {
|
||||||
if (false == this.animComp._playing) {
|
|
||||||
this.animComp.playAnimation(newAnimName);
|
|
||||||
}
|
|
||||||
// No need to interrupt
|
// No need to interrupt
|
||||||
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, not interrupting ${newAnimName} while the playing anim is also ${this.animComp.animationName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, , to: ${JSON.stringify(rdfPlayer)}`);
|
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, not interrupting ${newAnimName} while the playing anim is also ${playingAnimName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, , to: ${JSON.stringify(rdfPlayer)}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._interruptPlayingAnimAndPlayNewAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName);
|
this._interruptPlayingAnimAndPlayNewAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, underlyingAnimationCtrl);
|
||||||
} else {
|
} else {
|
||||||
// newCharacterState == prevCharacterState
|
// newCharacterState == prevCharacterState
|
||||||
if (newAnimName != this.animComp.animationName) {
|
if (newAnimName != playingAnimName) {
|
||||||
// the playing animation was falsely predicted
|
// the playing animation was falsely predicted
|
||||||
this._interruptPlayingAnimAndPlayNewAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName);
|
this._interruptPlayingAnimAndPlayNewAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, underlyingAnimationCtrl);
|
||||||
|
} else {
|
||||||
|
if (!(ATK_CHARACTER_STATE.Idle1[0] == newCharacterState || ATK_CHARACTER_STATE.Walking[0] == newCharacterState)) {
|
||||||
|
// yet there's still a chance that the playing anim is not put at the current frame
|
||||||
|
this._interruptPlayingAnimAndPlayNewAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, underlyingAnimationCtrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO: What if (newAnimName == this.animComp.animationName) but (false == this.animComp._playing) by now? Do we just force it to play from beginning or use "this._interruptPlayingAnimAndPlayNewAnim"?
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_interruptPlayingAnimAndPlayNewAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName) {
|
_interruptPlayingAnimAndPlayNewAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, underlyingAnimationCtrl) {
|
||||||
if (ATK_CHARACTER_STATE.Idle1[0] == newCharacterState || ATK_CHARACTER_STATE.Walking[0] == newCharacterState) {
|
if (ATK_CHARACTER_STATE.Idle1[0] == newCharacterState || ATK_CHARACTER_STATE.Walking[0] == newCharacterState) {
|
||||||
// No "framesToRecover"
|
// No "framesToRecover"
|
||||||
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, playing new ${newAnimName} from the beginning: while the playing anim is ${this.animComp.animationName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, , to: ${JSON.stringify(rdfPlayer)}`);
|
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, playing new ${newAnimName} from the beginning: while the playing anim is ${playAnimation}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, , to: ${JSON.stringify(rdfPlayer)}`);
|
||||||
this.animComp.playAnimation(newAnimName);
|
underlyingAnimationCtrl.gotoAndPlayByFrame(newAnimName, 0, -1);
|
||||||
} else {
|
} else {
|
||||||
const animationData = this.animComp._armature.animation._animations[newAnimName];
|
const animationData = underlyingAnimationCtrl._animations[newAnimName];
|
||||||
let fromAnimFrame = (animationData.frameCount - rdfPlayer.framesToRecover);
|
let fromAnimFrame = (animationData.frameCount - rdfPlayer.framesToRecover);
|
||||||
if (fromAnimFrame > 0) {
|
if (fromAnimFrame > 0) {
|
||||||
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, playing ${newAnimName} from the middle: rdfPlayer.framesToRecover=${rdfPlayer.framesToRecover} while the playing anim is ${this.animComp.animationName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}`);
|
|
||||||
} else if (fromAnimFrame < 0) {
|
} else if (fromAnimFrame < 0) {
|
||||||
// For Atk1 or Atk2, it's possible that the "meleeBullet.recoveryFrames" is configured to be slightly larger than corresponding animation duration frames
|
// For Atk1 or Atk2, it's possible that the "meleeBullet.recoveryFrames" is configured to be slightly larger than corresponding animation duration frames
|
||||||
fromAnimFrame = 0;
|
fromAnimFrame = 0;
|
||||||
}
|
}
|
||||||
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, playing ${newAnimName} from the middle: fromAnimFrame=${fromAnimFrame}, animFrameCount=${animationData.frameCount}, rdfPlayer.framesToRecover=${rdfPlayer.framesToRecover} while the playing anim is ${this.animComp.animationName}`);
|
underlyingAnimationCtrl.gotoAndPlayByFrame(newAnimName, fromAnimFrame, 1);
|
||||||
this.animComp._armature.animation.gotoAndPlayByFrame(newAnimName, fromAnimFrame);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user