2022-11-19 12:58:07 +00:00
const BaseCharacter = require ( "./BaseCharacter" ) ;
window . ATK _CHARACTER _STATE = {
Idle1 : [ 0 , "Idle1" ] ,
Walking : [ 1 , "Walking" ] ,
Atk1 : [ 2 , "Atk1" ] ,
2022-11-23 04:30:30 +00:00
Atked1 : [ 3 , "Atked1" ] ,
2022-12-31 07:47:45 +00:00
InAirIdle1NoJump : [ 4 , "InAirIdle1NoJump" ] ,
InAirIdle1ByJump : [ 5 , "InAirIdle1ByJump" ] , // The cycling part of it would be exactly "InAirIdle1NoJump"
InAirAtk1 : [ 6 , "InAirAtk1" ] ,
InAirAtked1 : [ 7 , "InAirAtked1" ] ,
BlownUp1 : [ 8 , "BlownUp1" ] ,
2023-01-02 08:36:17 +00:00
LayDown1 : [ 9 , "LayDown1" ] , // The last frame of "LayDown1" should have a simliar boundingbox with the first frame of "GetUp1", otherwise the animation would seem odd
2022-12-31 07:47:45 +00:00
GetUp1 : [ 10 , "GetUp1" ] ,
2023-01-01 12:18:35 +00:00
Atk2 : [ 11 , "Atk2" ] ,
Atk3 : [ 12 , "Atk3" ] ,
2022-11-19 12:58:07 +00:00
} ;
2022-11-20 13:07:45 +00:00
window . ATK _CHARACTER _STATE _ARR = [ ] ;
for ( let k in window . ATK _CHARACTER _STATE ) {
window . ATK _CHARACTER _STATE _ARR . push ( window . ATK _CHARACTER _STATE [ k ] ) ;
}
2022-11-27 11:38:26 +00:00
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET = new Set ( ) ;
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . Idle1 [ 0 ] ) ;
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . Walking [ 0 ] ) ;
2022-12-31 07:47:45 +00:00
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . InAirIdle1NoJump [ 0 ] ) ;
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . InAirIdle1ByJump [ 0 ] ) ;
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . BlownUp1 [ 0 ] ) ;
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . LayDown1 [ 0 ] ) ;
window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . add ( window . ATK _CHARACTER _STATE . GetUp1 [ 0 ] ) ;
2022-11-27 11:38:26 +00:00
2022-12-09 16:07:03 +00:00
window . ATK _CHARACTER _STATE _IN _AIR _SET = new Set ( ) ;
2022-12-31 07:47:45 +00:00
window . ATK _CHARACTER _STATE _IN _AIR _SET . add ( window . ATK _CHARACTER _STATE . InAirIdle1NoJump [ 0 ] ) ;
window . ATK _CHARACTER _STATE _IN _AIR _SET . add ( window . ATK _CHARACTER _STATE . InAirIdle1ByJump [ 0 ] ) ;
2022-12-09 16:07:03 +00:00
window . ATK _CHARACTER _STATE _IN _AIR _SET . add ( window . ATK _CHARACTER _STATE . InAirAtk1 [ 0 ] ) ;
window . ATK _CHARACTER _STATE _IN _AIR _SET . add ( window . ATK _CHARACTER _STATE . InAirAtked1 [ 0 ] ) ;
2022-12-31 07:47:45 +00:00
window . ATK _CHARACTER _STATE _IN _AIR _SET . add ( window . ATK _CHARACTER _STATE . BlownUp1 [ 0 ] ) ;
2022-12-09 16:07:03 +00:00
2022-11-26 16:00:39 +00:00
/ *
2022-11-27 11:38:26 +00:00
Kindly note that the use of dragonBones anim is an informed choice for the feasibility of "gotoAndPlayByFrame" , which is a required feature by "Map.rollbackAndChase" . You might find that "cc.Animation" -- the traditional frame anim -- can also suffice this requirement , yet if we want to develop 3 D frontend in the future , working with skeletal anim will make a smoother transition .
Moreover , frame anim doesn ' t support "compositie playing" and consumes more memory ( yet less CPU ) than a same skeletal anim , thus should only be used properly .
2022-11-26 16:00:39 +00:00
I 've also spent sometime in extending "ccc wrapped dragoneBones.ArmatureDisplay" for enabling "gotoAndPlayByFrame" in CACHE mode (in REALTIME mode it' s just the same as what ' s done here ) , but the debugging is an unexpected brainteaser -- not worth the time .
* /
2022-11-19 12:58:07 +00:00
cc . Class ( {
extends : BaseCharacter ,
properties : {
animNode : {
type : cc . Node ,
default : null
} ,
} ,
ctor ( ) {
2022-11-20 10:53:33 +00:00
this . speciesName = null ;
2022-11-22 09:12:51 +00:00
this . hp = 100 ;
this . maxHp = 100 ;
2022-12-09 09:22:04 +00:00
this . inAir = true ;
2022-11-20 10:53:33 +00:00
} ,
setSpecies ( speciesName ) {
this . speciesName = speciesName ;
2022-11-19 12:58:07 +00:00
} ,
onLoad ( ) {
BaseCharacter . prototype . onLoad . call ( this ) ;
2022-11-27 11:38:26 +00:00
this . effAnimNode = this . animNode . getChildByName ( this . speciesName ) ;
this . animComp = this . effAnimNode . getComponent ( dragonBones . ArmatureDisplay ) ;
if ( ! this . animComp ) {
this . animComp = this . effAnimNode . getComponent ( cc . Animation ) ;
2022-12-31 07:47:45 +00:00
}
2022-11-27 11:38:26 +00:00
this . effAnimNode . active = true ;
2022-11-19 12:58:07 +00:00
} ,
2022-12-31 07:47:45 +00:00
updateCharacterAnim ( rdfPlayer , prevRdfPlayer , forceAnimSwitch , chConfig ) {
2022-12-28 10:06:05 +00:00
// As this function might be called after many frames of a rollback, it's possible that the playing animation was predicted, different from "prevRdfPlayer.CharacterState" but same as "newCharacterState". More granular checks are needed to determine whether we should interrupt the playing animation.
2022-11-19 12:58:07 +00:00
2022-11-27 13:33:34 +00:00
// Update directions
if ( this . animComp && this . animComp . node ) {
2022-12-28 10:06:05 +00:00
if ( 0 > rdfPlayer . DirX ) {
2023-01-01 14:51:46 +00:00
this . animNode . scaleX = ( - 1.0 ) ;
2022-12-28 10:06:05 +00:00
} else if ( 0 < rdfPlayer . DirX ) {
2023-01-01 14:51:46 +00:00
this . animNode . scaleX = ( 1.0 ) ;
2022-11-25 03:20:05 +00:00
}
2022-11-27 11:38:26 +00:00
}
2022-12-28 10:06:05 +00:00
let newCharacterState = rdfPlayer . CharacterState ;
2022-12-17 09:33:14 +00:00
let newAnimName = window . ATK _CHARACTER _STATE _ARR [ newCharacterState ] [ 1 ] ;
2022-11-27 13:33:34 +00:00
let playingAnimName = null ;
let underlyingAnimationCtrl = null ;
if ( this . animComp instanceof dragonBones . ArmatureDisplay ) {
underlyingAnimationCtrl = this . animComp . _armature . animation ; // ALWAYS use the dragonBones api instead of ccc's wrapper!
playingAnimName = underlyingAnimationCtrl . lastAnimationName ;
} else {
underlyingAnimationCtrl = this . animComp . currentClip ;
playingAnimName = ( ! underlyingAnimationCtrl ? null : underlyingAnimationCtrl . name ) ;
}
2022-12-28 10:06:05 +00:00
// It turns out that "prevRdfPlayer.CharacterState" is not useful in this function :)
2022-11-27 13:33:34 +00:00
if ( newAnimName == playingAnimName && window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . has ( newCharacterState ) ) {
// No need to interrupt
2022-11-29 04:49:49 +00:00
// 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)}`);
2022-11-27 11:38:26 +00:00
return ;
}
2022-11-27 13:33:34 +00:00
if ( this . animComp instanceof dragonBones . ArmatureDisplay ) {
2022-12-31 07:47:45 +00:00
this . _interruptPlayingAnimAndPlayNewAnimDragonBones ( rdfPlayer , prevRdfPlayer , newCharacterState , newAnimName , underlyingAnimationCtrl , playingAnimName , chConfig ) ;
2022-11-27 13:33:34 +00:00
} else {
2022-12-31 07:47:45 +00:00
this . _interruptPlayingAnimAndPlayNewAnimFrameAnim ( rdfPlayer , prevRdfPlayer , newCharacterState , newAnimName , playingAnimName , chConfig ) ;
2022-11-25 13:53:30 +00:00
}
} ,
2022-12-31 07:47:45 +00:00
_interruptPlayingAnimAndPlayNewAnimDragonBones ( rdfPlayer , prevRdfPlayer , newCharacterState , newAnimName , underlyingAnimationCtrl , playingAnimName , chConfig ) {
2022-11-29 04:49:49 +00:00
if ( window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . has ( newCharacterState ) ) {
2022-11-25 13:53:30 +00:00
// No "framesToRecover"
2022-12-13 10:10:30 +00:00
// console.warn(`#DragonBones JoinIndex=${rdfPlayer.joinIndex}, ${playingAnimName} -> ${newAnimName}`);
2022-11-25 15:50:13 +00:00
underlyingAnimationCtrl . gotoAndPlayByFrame ( newAnimName , 0 , - 1 ) ;
2022-11-25 13:53:30 +00:00
} else {
2022-11-25 15:50:13 +00:00
const animationData = underlyingAnimationCtrl . _animations [ newAnimName ] ;
2022-12-31 07:47:45 +00:00
let frameIdxInAnim = rdfPlayer . FramesInChState ;
underlyingAnimationCtrl . gotoAndPlayByFrame ( newAnimName , frameIdxInAnim , 1 ) ;
2022-11-19 12:58:07 +00:00
}
} ,
2022-11-27 13:33:34 +00:00
2022-12-31 07:47:45 +00:00
_interruptPlayingAnimAndPlayNewAnimFrameAnim ( rdfPlayer , prevRdfPlayer , newCharacterState , newAnimName , playingAnimName , chConfig ) {
2022-11-27 13:33:34 +00:00
if ( window . ATK _CHARACTER _STATE _INTERRUPT _WAIVE _SET . has ( newCharacterState ) ) {
// No "framesToRecover"
2022-12-09 16:07:03 +00:00
//console.warn(`#FrameAnim JoinIndex=${rdfPlayer.joinIndex}, ${playingAnimName} -> ${newAnimName}`);
2022-11-27 13:33:34 +00:00
this . animComp . play ( newAnimName , 0 ) ;
return ;
}
// The "playTimes" counterpart is managed by each "cc.AnimationClip.wrapMode", already preset in the editor.
const targetClip = this . animComp . getClips ( ) [ newCharacterState ] ; // The clips follow the exact order in ATK_CHARACTER_STATE
2022-12-31 07:47:45 +00:00
let frameIdxInAnim = rdfPlayer . FramesInChState ;
if ( window . ATK _CHARACTER _STATE . InAirIdle1ByJump == newCharacterState && null != chConfig ) {
frameIdxInAnim = chConfig . InAirIdleFrameIdxTurningPoint + ( frameIdxInAnim - chConfig . InAirIdleFrameIdxTurningPoint ) % chConfig . InAirIdleFrameIdxTurnedCycle ; // TODO: Anyway to avoid using division here?
}
let fromTime = ( frameIdxInAnim / targetClip . sample ) ; // TODO: Anyway to avoid using division here?
2022-11-27 13:33:34 +00:00
this . animComp . play ( newAnimName , fromTime ) ;
} ,
2022-11-19 12:58:07 +00:00
} ) ;