import { _decorator, Component, Node, ProgressBar, tween } from 'cc'; const { ccclass, property } = _decorator; /** * 血条动画组件 * 使用两个重叠的血条实现黄色血皮滑动动画效果 * 红色血条显示当前血量,黄色血条用于滑动动画 * 血条默认隐藏,只在受伤时显示并播放动画 */ @ccclass('HPBarAnimation') export class HPBarAnimation extends Component { @property({ type: Node, tooltip: '红色血条节点' }) public redBarNode: Node = null; @property({ type: Node, tooltip: '黄色血条节点' }) public yellowBarNode: Node = null; @property({ type: Node, tooltip: 'HPBar根节点' }) public hpBarRootNode: Node = null; private redProgressBar: ProgressBar = null; private yellowProgressBar: ProgressBar = null; private currentProgress: number = 1.0; private targetProgress: number = 1.0; private currentTween: any = null; // 当前正在运行的动画 private hideTimer: any = null; // 隐藏血条的定时器 start() { this.initializeComponents(); } /** * 初始化组件 */ private initializeComponents() { // 获取组件所属的敌人节点信息 const enemyNode = this.node; const enemyName = enemyNode ? enemyNode.name : 'Unknown'; // 如果没有设置HPBar根节点,尝试自动查找 if (!this.hpBarRootNode) { this.hpBarRootNode = this.node.getChildByName('HPBar'); } // 获取红色血条组件 if (this.redBarNode) { this.redProgressBar = this.redBarNode.getComponent(ProgressBar); if (this.redProgressBar) { this.currentProgress = this.redProgressBar.progress; this.targetProgress = this.currentProgress; } else { console.error(`[HPBarAnimation] [${enemyName}] 红色血条节点上未找到ProgressBar组件!`); } } else { console.error(`[HPBarAnimation] [${enemyName}] 红色血条节点未设置!`); } // 获取黄色血条组件 if (this.yellowBarNode) { this.yellowProgressBar = this.yellowBarNode.getComponent(ProgressBar); if (this.yellowProgressBar) { // 黄色血条初始进度与红色血条同步 this.yellowProgressBar.progress = this.currentProgress; } else { console.error(`[HPBarAnimation] [${enemyName}] 黄色血条节点上未找到ProgressBar组件!`); } } else { console.error(`[HPBarAnimation] [${enemyName}] 黄色血条节点未设置!`); } // 初始化时隐藏血条 this.hideHealthBar(); } /** * 获取节点路径 */ private getNodePath(node: Node): string { if (!node) return 'null'; let path = node.name; let current = node; while (current.parent) { current = current.parent; path = current.name + '/' + path; } return path; } /** * 更新血条显示 */ private updateBarDisplay() { if (!this.redProgressBar || !this.yellowProgressBar) { return; } // 红色血条显示当前血量 if (this.redProgressBar && this.redProgressBar.isValid) { this.redProgressBar.progress = this.currentProgress; } } /** * 显示血条 */ public showHealthBar() { if (this.hpBarRootNode && this.hpBarRootNode.isValid) { this.hpBarRootNode.active = true; } // 清除隐藏定时器 if (this.hideTimer) { clearTimeout(this.hideTimer); this.hideTimer = null; } } /** * 隐藏血条 */ public hideHealthBar() { if (this.hpBarRootNode && this.hpBarRootNode.isValid) { this.hpBarRootNode.active = false; } } /** * 延迟隐藏血条 * @param delay 延迟时间(秒) */ private scheduleHideHealthBar(delay: number = 3.0) { // 清除之前的定时器 if (this.hideTimer) { clearTimeout(this.hideTimer); } // 设置新的定时器 this.hideTimer = setTimeout(() => { this.hideHealthBar(); this.hideTimer = null; }, delay * 1000); } /** * 更新血条进度并播放动画 * @param newProgress 新的血量百分比 (0-1) * @param showBar 是否显示血条(默认为true) */ public updateProgress(newProgress: number, showBar: boolean = true) { const enemyName = this.node ? this.node.name : 'Unknown'; if (!this.redProgressBar || !this.yellowProgressBar) { console.warn(`[HPBarAnimation] [${enemyName}] 红色或黄色血条组件未初始化`); return; } // 限制进度范围 newProgress = Math.max(0, Math.min(1, newProgress)); // 如果需要显示血条 if (showBar) { this.showHealthBar(); } // 如果血量增加或没有变化,直接更新 if (newProgress >= this.currentProgress) { this.currentProgress = newProgress; this.targetProgress = newProgress; // 同步更新两个血条 if (this.redProgressBar && this.redProgressBar.isValid) { this.redProgressBar.progress = newProgress; } if (this.yellowProgressBar && this.yellowProgressBar.isValid) { this.yellowProgressBar.progress = newProgress; } this.updateBarDisplay(); // 如果血量满了,立即隐藏血条 if (newProgress >= 1.0) { this.scheduleHideHealthBar(1.0); // 1秒后隐藏 } else if (showBar) { this.scheduleHideHealthBar(3.0); // 3秒后隐藏 } return; } // 血量减少时播放黄色血皮滑动动画 this.playDamageAnimation(newProgress); } /** * 播放伤害动画 * @param newProgress 新的血量百分比 */ private playDamageAnimation(newProgress: number) { console.log(`[HPBarAnimation] 开始播放伤害动画:${this.currentProgress} -> ${newProgress}`); // 停止当前正在运行的动画,避免动画冲突 if (this.currentTween) { this.currentTween.stop(); this.currentTween = null; } this.targetProgress = newProgress; // 保存原始血量作为黄色血条起始位置 const originalProgress = this.currentProgress; // 1. 立即更新红色血条到新的血量值 this.currentProgress = newProgress; if (this.redProgressBar && this.redProgressBar.isValid) { this.redProgressBar.progress = newProgress; } // 2. 黄色血条从原血量滑动到新血量位置 // 黄色血条保持在原始位置 if (this.yellowProgressBar && this.yellowProgressBar.isValid) { this.yellowProgressBar.progress = originalProgress; } this.updateBarDisplay(); // 创建滑动动画,先暂停0.4秒再滑动 this.currentTween = tween({ progress: originalProgress }) .delay(0.4) // 暂停0.4秒 .to(0.3, { progress: newProgress }, { onUpdate: (target) => { // 动画过程中更新黄色血条进度 if (this.yellowProgressBar && this.yellowProgressBar.isValid) { this.yellowProgressBar.progress = target.progress; this.updateBarDisplay(); } }, onComplete: () => { // 动画完成后黄色血条进度等于当前血量 if (this.yellowProgressBar && this.yellowProgressBar.isValid) { this.yellowProgressBar.progress = newProgress; this.updateBarDisplay(); } // 清除动画引用 this.currentTween = null; // 动画完成后延迟隐藏血条 if (newProgress > 0) { this.scheduleHideHealthBar(3.0); // 3秒后隐藏血条 } } }) .start(); } /** * 获取当前血量百分比 */ public getCurrentProgress(): number { return this.currentProgress; } /** * 重置血条到满血状态 */ public resetToFull() { this.updateProgress(1.0, false); // 重置时不显示血条 } /** * 重置血条到满血状态并隐藏 */ public resetToFullAndHide() { this.currentProgress = 1.0; this.targetProgress = 1.0; // 同步更新两个血条到满血状态 if (this.redProgressBar && this.redProgressBar.isValid) { this.redProgressBar.progress = 1.0; } if (this.yellowProgressBar && this.yellowProgressBar.isValid) { this.yellowProgressBar.progress = 1.0; } this.updateBarDisplay(); // 立即隐藏血条 this.hideHealthBar(); } onDestroy() { // 清理正在运行的动画,防止内存泄漏 if (this.currentTween) { this.currentTween.stop(); this.currentTween = null; } // 清理隐藏血条的定时器,防止内存泄漏 if (this.hideTimer) { clearTimeout(this.hideTimer); this.hideTimer = null; } } }