Files
pixelheros/assets/script/game/skill/TooltipCom.ts
panFD d559fdbaa4 feat(config,skill): 新增通用文字飘字提示功能
1. 新增TooltipTypes.tip枚举类型,支持调用方自定义文字和颜色
2. 扩展Tooltip.load和hp_tip方法,增加tipColor参数传递自定义颜色
3. 新增setupTipLabel工具方法,处理通用文字飘字的UI设置
4. 优化现有tip类型的注释和默认颜色逻辑,修复对象池复用颜色残留问题
5. 修复heroSet.ts导入多余空格问题
2026-09-07 21:08:26 +08:00

246 lines
11 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, Collider2D, Contact2DType, v3, IPhysics2DContact, Vec3, tween, Label, resources, SpriteFrame, Sprite, UIOpacity, Color, math, Tween, Node } 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;
/** DoT 飘字需激活的 debuff 图标节点名(loss_life/hp/debuff 下的子节点,如 FireDamage);空串=普通伤害 */
debuffIcon: string = "";
/** 通用文字飘字(TooltipTypes.tip)的自定义颜色,null 表示使用预制体默认色 */
tipColor: Color | null = null;
// 动画参数配置
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, debuffIcon: string = "", tipColor: Color | null = null) {
this.stype = type;
this.value = value;
this.s_uuid = uuid;
this.fac = fac;
this.debuffIcon = debuffIcon;
this.tipColor = tipColor;
// 初始化或获取 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坐标,方便统一处理。增加60的Y轴基础偏移,防止伤害飘字等遮挡血条
let currentY = this.node.position.y + 60;
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);
this.setupDebuffIcon(); // DoT 伤害时点亮 loss_life/hp/debuff 下的对应图标
scaleMax = 1.5;
break;
case TooltipTypes.health: // 治疗
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
this.node.setSiblingIndex(topSiblingIndex);
this.setupLabel("add_life", "hp", this.value);
isHeal = true;
break;
case TooltipTypes.addmp: // 回蓝
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
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.value);
// 技能喊话基于 load 传入位置再抬高 50,避免遮挡角色
this.node.setPosition(v3(this.node.position.x, this.node.position.y + 50));
this.node.setSiblingIndex(topSiblingIndex);
break;
case TooltipTypes.uskill:
this.setupLabel("uskill", "name", this.value);
this.node.setPosition(v3(this.node.position.x, currentY + 30));
this.node.setSiblingIndex(topSiblingIndex);
break;
case TooltipTypes.lvup:
this.node.getChildByName("lvup").active = true;
this.node.setPosition(v3(this.node.position.x, currentY - 30));
this.node.setSiblingIndex(topSiblingIndex);
break;
case TooltipTypes.apup:
this.setupLabel("apup", "num", "+" + this.value);
this.node.setPosition(v3(this.node.position.x, currentY - 60));
this.node.setSiblingIndex(topSiblingIndex);
break;
case TooltipTypes.hpup:
this.setupLabel("hpup", "num", "+" + this.value);
this.node.setPosition(v3(this.node.position.x, currentY - 60));
this.node.setSiblingIndex(topSiblingIndex);
break;
case TooltipTypes.shield: // 护盾吸收伤害:显示“吸收”+ 数值
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
this.node.setSiblingIndex(topSiblingIndex);
this.setupTipLabel(`吸收 ${this.value}`, this.tipColor ?? new Color(79, 195, 247, 255)); // 默认蓝
isHeal = true; // 垂直上飘,区别于伤害随机横飘
break;
case TooltipTypes.miss: // 闪避
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
this.node.setSiblingIndex(topSiblingIndex);
this.setupTipLabel(this.value || "闪避", this.tipColor ?? new Color(255, 255, 255, 255)); // 默认白
isHeal = true;
break;
case TooltipTypes.tip: // 通用文字飘字
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
this.node.setSiblingIndex(topSiblingIndex);
this.setupTipLabel(this.value, this.tipColor);
isHeal = true;
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;
}
}
/**
* 通用文字飘字:激活 words 节点并设置文字与颜色
* 预制体需包含 words/name 结构(Label);与 skill 节点区分,走漂移上飘动画而非原地淡出
* 对象池复用安全:未传色时恢复默认白,避免上次自定义色残留
*/
private setupTipLabel(text: string, color: Color | null = null) {
const words = this.node.getChildByName("words");
if (!words) return;
words.active = true;
const label = words.getChildByName("name")?.getComponent(Label);
if (label) {
label.string = text;
label.color = color ?? Color.WHITE.clone();
}
}
/**
* DoT 伤害飘字:激活 loss_life/hp/debuff 下指定的 debuff 图标子节点。
* 仅怪物普通伤害(loss_life)路径调用;debuffIcon 为空或未找到节点时静默跳过。
*/
private setupDebuffIcon() {
if (!this.debuffIcon) return;
const debuffNode = this.node.getChildByName("loss_life")?.getChildByName("hp")?.getChildByName("debuff");
if (!debuffNode) return;
// 先全部隐藏(对象池复用防残留),再点亮目标图标
for (const child of debuffNode.children) {
child.active = child.name === this.debuffIcon;
}
}
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' });
const isSkill = this.stype === TooltipTypes.skill;
if (isSkill) {
// 技能类型的提示:回弹后稍作停留,然后原地快速淡出
t.to(this.popDuration, { scale: v3(sx * 1.2, 1.2, 1) });
t.delay(0.2);
t.parallel(
tween(this._uiOpacity).to(this.fadeDuration, { opacity: 0 })
);
} else {
// 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);
}
}