Files
pixelheros/assets/script/game/hero/HeroTopBarComp.ts
panFD 4fee02d82e refactor(hero): 移除英雄等级显示相关逻辑
1. 从HeroTopBarComp中移除等级Label组件、lvShow方法以及等级显示调用
2. 从HeroViewComp的update逻辑中移除等级脏数据监听
3. 调整英雄顶栏预制体的布局参数并隐藏等级节点
2026-08-13 23:16:44 +08:00

174 lines
6.4 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, ProgressBar, Sprite, UIOpacity, Vec3, v3, tween, Tween, Color, clamp } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { FacSet, FightSet } from "../common/config/GameSet";
const { ccclass, property } = _decorator;
/**
* 英雄头顶状态栏组件
*
* 挂载在英雄节点下的 "top" 子节点上,独立管理:
* - 血条进度ProgressBar
* - 护盾数值条ProgressBartop/shield 节点,显示 shield / SHIELD_MAX
* - 透明度激活 / 闲置
* - 受击抖动动画
*
* 数据流HeroViewComp 将 HeroAttrsComp 引用传入,本组件通过 model 读取属性。
* 注意:英雄根节点下的 "shielded" 是护盾特效动画节点,由 HeroViewComp 管理,不在本组件职责内。
*/
@ccclass('HeroTopBarComp')
@ecs.register('HeroTopBar', false)
export class HeroTopBarComp extends CCComp {
// ==================== @property 绑定(编辑器拖拽) ====================
@property({ type: ProgressBar, tooltip: "血条进度条" })
private hpProgressBar: ProgressBar = null!;
@property({ type: ProgressBar, tooltip: "护盾数值条top 下的 shield 节点)" })
private shieldProgressBar: ProgressBar = null!;
@property({ type: Sprite, tooltip: "血条填充 Sprite用于设置阵营颜色" })
private hpBarSprite: Sprite = null!;
// ==================== 内部引用 ====================
private topOpacity: UIOpacity = null!;
private basePos: Vec3 = v3();
// ==================== 配置 ====================
private hp_height: number = 90;
private boss_hp_height: number = 160;
// ==================== 透明度 ====================
private readonly barIdleOpacity: number = 153;
private readonly barActiveOpacity: number = 255;
private readonly idleOpacityDelay: number = 0.25;
/** 外部赋值:英雄数据模型 */
private _model: HeroAttrsComp = null!;
public get model(): HeroAttrsComp { return this._model; }
public set model(value: HeroAttrsComp) { this._model = value; }
private readonly restoreBarIdleOpacity = () => {
this.setTopBarOpacity(false);
};
/**
* 初始化 / 对象池复用时重置
* @param model 英雄属性组件
* @param direction 显示方向1 或 -1
*/
public init(model: HeroAttrsComp, direction: number = 1): void {
this._model = model;
// UIOpacity 无 @property 绑定,运行时获取或添加
this.topOpacity = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
// 方向取 abs避免对象池复用时 scale 累乘导致血条反转
this.node.setScale(direction * Math.abs(this.node.scale.x), 1 * this.node.scale.y);
// 位置
const height = model.is_boss ? this.boss_hp_height : this.hp_height;
this.node.setPosition(0, height, 0);
this.basePos = this.node.position.clone();
// 血条颜色(英雄阵营为绿色)
if (model.fac === FacSet.HERO && this.hpBarSprite) {
this.hpBarSprite.color = new Color("#2ECC71");
}
// 确保血条等 UI 始终在最上层
this.node.setSiblingIndex(999);
// 重置显示状态
this.node.getChildByName("hp")!.active = true;
this.node.getChildByName("cd")!.active = false;
this.node.getChildByName("lv")!.active = false;
this.node.active = true;
this.setTopBarOpacity(false);
this.hpShow();
this.shieldShow(model.shield);
}
/** 更新血量进度条 */
public hpShow(): void {
if (!this._model || !this.hpProgressBar) return;
const hp = this._model.hp;
const hpMax = this._model.hp_max;
let targetProgress = hpMax > 0 ? hp / hpMax : 0;
targetProgress = clamp(targetProgress, 0, 1);
const prevProgress = this.hpProgressBar.progress;
if (targetProgress < prevProgress) {
this.activateTopBar();
this.playHpBarShake();
}
this.hpProgressBar.progress = targetProgress;
if (targetProgress > prevProgress) {
this.setTopBarOpacity(false);
}
}
/** 更新护盾数值条(百分比 = shield / SHIELD_MAX */
public shieldShow(shield: number = 0): void {
if (!this.shieldProgressBar) return;
const progress = FightSet.SHIELD_MAX > 0 ? shield / FightSet.SHIELD_MAX : 0;
this.shieldProgressBar.progress = clamp(progress, 0, 1);
// 护盾节点显隐
this.shieldProgressBar.node.active = shield > 0;
}
/** 激活顶栏(高亮 + 延迟恢复闲置透明度) */
public activateTopBar(): void {
this.setTopBarOpacity(true);
this.unschedule(this.restoreBarIdleOpacity);
this.scheduleOnce(this.restoreBarIdleOpacity, this.idleOpacityDelay);
}
/** 设置顶栏透明度 */
public setTopBarOpacity(isActive: boolean): void {
if (!this.node || !this.node.isValid) return;
if (this.topOpacity) {
this.topOpacity.opacity = isActive ? this.barActiveOpacity : this.barIdleOpacity;
}
this.node.active = true;
}
/** 显示顶栏 */
public show(): void {
if (this.node && this.node.isValid) this.node.active = true;
}
/** 隐藏顶栏 */
public hide(): void {
if (this.node && this.node.isValid) this.node.active = false;
}
/** 血条受击抖动动画 */
private playHpBarShake(): void {
if (!this.node || !this.node.isValid) return;
Tween.stopAllByTarget(this.node);
this.node.setPosition(this.basePos);
tween(this.node)
.by(0.04, { position: v3(-3, 0, 0) })
.by(0.04, { position: v3(6, 0, 0) })
.by(0.04, { position: v3(-5, 0, 0) })
.by(0.04, { position: v3(2, 0, 0) })
.call(() => {
this.node.setPosition(this.basePos);
})
.start();
}
/** 对象池回收 / 组件重置时调用 */
public reset(): void {
this.unscheduleAllCallbacks();
if (this.node && this.node.isValid) {
Tween.stopAllByTarget(this.node);
this.node.setPosition(this.basePos);
}
}
}