Files
pixelheros/assets/script/game/hero/HeroAnmComp.ts
panFD 8c8810bb7e fix(hero): 修复英雄大招动画播放异常问题
1. 重构大招播放方法,支持传入自定义动画clip名,适配不同职业的大招动画
2. 修复技能匹配逻辑,通过前缀匹配处理max0/max1类型的大招技能
3. 更新技能配置表,将辅助职业的技能act改为max1适配新逻辑
2026-09-14 21:38:52 +08:00

116 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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() {
}
}