移除测试用的英雄成长模拟数据,修复动画状态与实际表现不一致的问题 1. 重构HeroSpine的idle/move方法,新增返回值标记动画是否被拦截 2. 重构HeroViewComp的status_change逻辑,仅在动画切换成功时更新状态 3. 重构HeroAnmComp的动画播放逻辑,修复动画结束后默认播放的异常 4. 移除SingletonModuleComp中的测试用英雄成长数据 5. 简化MoveSystem中状态切换的冗余判断逻辑
110 lines
4.2 KiB
TypeScript
110 lines
4.2 KiB
TypeScript
import { _decorator, Animation, AnimationState, CCClass, Component, } from "cc";
|
||
import { HeroViewComp } from "./HeroViewComp";
|
||
import { FacSet } from "../common/config/GameSet";
|
||
import { FlashSprite } from "./hit-flash-white/scripts/FlashSprite";
|
||
const { ccclass, property } = _decorator;
|
||
|
||
@ccclass('HeroAnmComp')
|
||
export default class HeroAnmComp extends Component {
|
||
fsSprite: FlashSprite = null!;
|
||
private anmcon: any = null
|
||
private _hasStop = true;
|
||
private _atkIndex = 0;
|
||
anms: any[] = ["idle", "move", "atk0", "max0", "max1"]
|
||
onLoad() {
|
||
this.anmcon = this.node.getComponent(Animation)
|
||
this.fsSprite = this.node.getComponent(FlashSprite);
|
||
this.anmcon.on(Animation.EventType.FINISHED, this.onAnimationFinished, this);
|
||
}
|
||
|
||
onDestroy() {
|
||
if (this.anmcon) {
|
||
this.anmcon.off(Animation.EventType.FINISHED, this.onAnimationFinished, this);
|
||
}
|
||
}
|
||
|
||
/** 大招是否播放中(最高优先级,期间禁止一切动画切换) */
|
||
private get maxPlaying(): boolean {
|
||
return !!(this.anmcon.getState("max0")?.isPlaying || this.anmcon.getState("max1")?.isPlaying);
|
||
}
|
||
/** 普攻是否播放中(次优先级,仅可被大招打断) */
|
||
private get atkPlaying(): boolean {
|
||
return !!this.anmcon.getState("atk0")?.isPlaying;
|
||
}
|
||
/** buff 是否播放中(可被大招/普攻打断,但拦截 idle/move) */
|
||
private get buffPlaying(): boolean {
|
||
return !!this.anmcon.getState("buff")?.isPlaying;
|
||
}
|
||
|
||
stop() {
|
||
this._hasStop = true;
|
||
}
|
||
onAnimationFinished(type: Animation.EventType, state: AnimationState) {
|
||
// HeroAnmComp 与 HeroViewComp 挂在同一节点上,直接 getComponent 即可,
|
||
// 不需要走父节点查找(且 Component 没有 getComponentInParent,必须在 Node 上调用)
|
||
const viewComp = this.node.getComponent(HeroViewComp);
|
||
if (state.name === "max0" || state.name === "max1") {
|
||
// 大招播放结束,清除 HeroViewComp 的置顶标记,恢复 MoveSystem 统一排序
|
||
if (viewComp) {
|
||
viewComp.isCastingMax = false;
|
||
}
|
||
}
|
||
if (state.name != "idle" && state.name != "move") {
|
||
// 攻击/buff/大招结束:按视图层记录的真实状态回放,而非过期的缓存动画,
|
||
// 避免"位置已停但还在播走路"的原地走步(view.status 由 status_change 成功切换时写入)
|
||
const target = viewComp?.status === "move" ? "move" : "idle";
|
||
if (!this.maxPlaying && !this.atkPlaying && !this.buffPlaying) {
|
||
if (!this.anmcon.getState(target)?.isPlaying) {
|
||
this.anmcon.play(target);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
atked() {
|
||
this.fsSprite.clickFlash();
|
||
}
|
||
/**
|
||
* 播放移动动画。
|
||
* Why 返回 boolean:普攻/buff/大招播放期间会被拦截(return false),
|
||
* 调用方(HeroSpine/HeroViewComp)据此不记录状态,保证记录状态与实际动画一致,
|
||
* 下一帧重试时去重条件不成立可再次尝试,避免"位置在动但动画卡死"。
|
||
* @returns true = 动画已处于目标状态(新播或原本在播);false = 被高优先级动画拦截
|
||
*/
|
||
move(): boolean {
|
||
if (this.maxPlaying || this.atkPlaying || this.buffPlaying) return false
|
||
if (this.anmcon.getState("move").isPlaying) return true
|
||
this.anmcon.play("move")
|
||
return true
|
||
}
|
||
atk() {
|
||
if (this.maxPlaying) return
|
||
this.anmcon.play(`atk0`)
|
||
}
|
||
max() {
|
||
if (this.maxPlaying) return
|
||
this.anmcon.play("max0")
|
||
}
|
||
play(anm: string = "") {
|
||
if (anm === "") return
|
||
if (this.maxPlaying) return
|
||
this.anmcon.play(anm)
|
||
}
|
||
|
||
/**
|
||
* 播放待机动画(返回值语义与 move() 一致:被高优先级动画拦截时返回 false)
|
||
*/
|
||
idle(): boolean {
|
||
if (this.maxPlaying || this.atkPlaying || this.buffPlaying) return false
|
||
if (this.anmcon.getState("idle").isPlaying) return true
|
||
this.anmcon.play("idle")
|
||
return true
|
||
}
|
||
buff() {
|
||
if (this.maxPlaying) return
|
||
this.anmcon.play("buff")
|
||
}
|
||
dead() {
|
||
|
||
}
|
||
}
|