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`) } /** * 播放大招动画。 * Why 带 name:技能表 act 直接配置 clip 名(战士技 "max0"、法系技 "max1"), * 固定播 max0 会导致法系大招动画永远播不出。 * @param name 大招 clip 名("max0"/"max1"),maxPlaying guard 已同时覆盖两者 */ max(name: string = "max0") { if (this.maxPlaying) return this.anmcon.play(name) } 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() { } }