扩展 Tooltip 系统以支持根据伤害来源的阵营(英雄或怪物)显示不同的文本标签。修改 HeroViewComp 调用时传递阵营信息,TooltipCom 根据阵营选择对应的本地化键名。同时调整了工具提示预制件的默认激活状态和文本颜色。
181 lines
7.4 KiB
TypeScript
181 lines
7.4 KiB
TypeScript
import { _decorator, Collider2D, Contact2DType, v3, IPhysics2DContact, Vec3, tween, Label, resources, SpriteFrame, Sprite, UIOpacity, Color, math, Tween } 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 { SkillSet } from "../common/config/SkillSet";
|
||
import { FacSet, TooltipTypes } from "../common/config/GameSet";
|
||
import { Tooltip } from "./Tooltip";
|
||
|
||
const { ccclass, property } = _decorator;
|
||
|
||
/** 视图层对象 */
|
||
@ccclass('TooltipCom')
|
||
@ecs.register('TooltipView', false)
|
||
export class TooltipCom extends CCComp {
|
||
stype: number = 1; // 1:减少生命值,2:增加生命值,3:技能图标
|
||
value: string = "";
|
||
s_uuid: number = 1001;
|
||
fac: number = FacSet.MON;
|
||
|
||
// 动画参数配置
|
||
private readonly popDuration = 0.15;
|
||
private readonly driftDuration = 0.5;
|
||
private readonly fadeDuration = 0.2;
|
||
|
||
private _uiOpacity: UIOpacity | null = null;
|
||
|
||
start() {
|
||
// 首次加载时如果未手动调用 init,则自动初始化(防止直接挂载场景的情况)
|
||
// 但在当前架构下,Tooltip 都是通过 Tooltip.load 创建的,所以 start 可以留空或仅做基本检查
|
||
}
|
||
|
||
/** 初始化并播放动画 */
|
||
init(type: number, value: string, uuid: number, fac: number = FacSet.MON) {
|
||
this.stype = type;
|
||
this.value = value;
|
||
this.s_uuid = uuid;
|
||
this.fac = fac;
|
||
|
||
// 初始化或获取 UIOpacity 组件
|
||
this._uiOpacity = this.node.getComponent(UIOpacity);
|
||
if (!this._uiOpacity) {
|
||
this._uiOpacity = this.node.addComponent(UIOpacity);
|
||
}
|
||
this._uiOpacity.opacity = 255;
|
||
|
||
// 检测父节点翻转
|
||
let sx = 1;
|
||
if (this.node.parent && this.node.parent.scale.x < 0) {
|
||
sx = -1;
|
||
}
|
||
|
||
// 🔥 关键修复:停止当前节点和 UIOpacity 上的所有缓动
|
||
Tween.stopAllByTarget(this.node);
|
||
if (this._uiOpacity) {
|
||
Tween.stopAllByTarget(this._uiOpacity);
|
||
this._uiOpacity.opacity = 255;
|
||
}
|
||
|
||
this.node.setScale(v3(0, 0, 1)); // 初始缩放为0
|
||
this.node.active = true; // 确保节点激活
|
||
|
||
// 重置所有子节点状态
|
||
this.node.children.forEach(child => child.active = false);
|
||
|
||
let scaleMax = 1.5;
|
||
let isCrit = false;
|
||
let isHeal = false;
|
||
|
||
// 初始随机偏移,防止完全重叠
|
||
const offsetX = (Math.random() - 0.5) * 40;
|
||
|
||
// 设置极高的渲染层级,防止被怪物遮挡
|
||
const topSiblingIndex = 9999;
|
||
|
||
// 临时变量,存储当前Y坐标,方便统一处理
|
||
let currentY = this.node.position.y;
|
||
|
||
switch (this.stype) {
|
||
case TooltipTypes.life: // 普通伤害
|
||
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
|
||
this.node.setSiblingIndex(topSiblingIndex);
|
||
this.setupLabel(this.fac === FacSet.HERO ? "hloss" : "loss_life", "hp", this.value);
|
||
scaleMax = 1.5;
|
||
break;
|
||
case TooltipTypes.health: // 治疗
|
||
this.node.setSiblingIndex(topSiblingIndex);
|
||
this.setupLabel("add_life", "hp", this.value);
|
||
isHeal = true;
|
||
break;
|
||
case TooltipTypes.addmp: // 回蓝
|
||
this.node.setSiblingIndex(topSiblingIndex);
|
||
this.setupLabel("add_mp", "mp", this.value);
|
||
isHeal = true;
|
||
break;
|
||
case TooltipTypes.crit: // 暴击
|
||
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
|
||
this.node.setSiblingIndex(topSiblingIndex + 100); // 暴击层级更高
|
||
this.setupLabel("bloss", "hp", this.value);
|
||
scaleMax = 3.0; // 暴击放大
|
||
isCrit = true;
|
||
break;
|
||
case TooltipTypes.skill:
|
||
const skillConfig = SkillSet[this.s_uuid];
|
||
const skillName = skillConfig ? "<" + skillConfig.name + ">" : "";
|
||
this.setupLabel("skill", "name", skillName);
|
||
this.node.setPosition(v3(this.node.position.x, currentY));
|
||
this.node.setSiblingIndex(topSiblingIndex);
|
||
break;
|
||
case TooltipTypes.uskill:
|
||
this.setupLabel("uskill", "name", this.value);
|
||
this.node.setPosition(v3(this.node.position.x, this.node.position.y + 30));
|
||
this.node.setSiblingIndex(topSiblingIndex);
|
||
break;
|
||
case TooltipTypes.lvup:
|
||
this.node.getChildByName("lvup").active = true;
|
||
this.node.setPosition(v3(this.node.position.x, this.node.position.y - 30));
|
||
this.node.setSiblingIndex(topSiblingIndex);
|
||
break;
|
||
case TooltipTypes.apup:
|
||
this.setupLabel("apup", "num", "+" + this.value);
|
||
this.node.setPosition(v3(this.node.position.x, this.node.position.y - 60));
|
||
this.node.setSiblingIndex(topSiblingIndex);
|
||
break;
|
||
case TooltipTypes.hpup:
|
||
this.setupLabel("hpup", "num", "+" + this.value);
|
||
this.node.setPosition(v3(this.node.position.x, this.node.position.y - 60));
|
||
this.node.setSiblingIndex(topSiblingIndex);
|
||
break;
|
||
case TooltipTypes.shield:
|
||
this.node.setSiblingIndex(topSiblingIndex);
|
||
this.setupLabel("add_life", "hp", this.value);
|
||
break;
|
||
}
|
||
|
||
this.playAnimation(scaleMax, isCrit, isHeal, sx);
|
||
}
|
||
|
||
private setupLabel(nodeName: string, labelNodeName: string, text: string) {
|
||
const node = this.node.getChildByName(nodeName);
|
||
if (node) {
|
||
node.active = true;
|
||
const label = node.getChildByName(labelNodeName)?.getComponent(Label);
|
||
if (label) label.string = text;
|
||
}
|
||
}
|
||
|
||
playAnimation(scaleMax: number, isCrit: boolean, isHeal: boolean, sx: number = 1) {
|
||
// 随机 X 轴偏移 (防止重叠)
|
||
const randomX = (Math.random() - 0.5) * 60;
|
||
const moveY = 80;
|
||
|
||
const t = tween(this.node);
|
||
|
||
// 1. 爆发阶段 (Pop)
|
||
t.to(this.popDuration, { scale: v3(sx * scaleMax, scaleMax, 1) }, { easing: 'backOut' });
|
||
|
||
// 2. 漂移阶段 (Drift) & 3. 淡出 (Fade)
|
||
// 并行执行:恢复缩放 + 位移 + 淡出
|
||
t.parallel(
|
||
tween(this.node).to(this.popDuration, { scale: v3(sx * 1.2, 1.2, 1) }), // 回弹到正常大小 (稍微放大)
|
||
tween(this.node).by(this.driftDuration, { position: v3(isHeal ? 0 : randomX, moveY, 0) }, { easing: 'sineOut' }), // 治疗垂直飘,其他随机飘
|
||
tween(this._uiOpacity).delay(this.driftDuration - this.fadeDuration).to(this.fadeDuration, { opacity: 0 }) // 最后阶段淡出
|
||
);
|
||
|
||
// 结束销毁
|
||
t.call(() => {
|
||
// this.ent.destroy(); // 不要销毁实体,而是直接回收节点
|
||
Tooltip.put(this.node);
|
||
}).start();
|
||
}
|
||
|
||
update(deltaTime: number) {
|
||
|
||
}
|
||
|
||
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
|
||
reset() {
|
||
// 使用对象池回收
|
||
Tooltip.put(this.node);
|
||
}
|
||
}
|