Files
pixelheros/assets/script/game/skill/TooltipCom.ts
pan 42d0959b05 refactor(hero&shield): 优化护盾系统逻辑与英雄配置
1. 修复护盾吸收数值计算错误,正确返回实际吸收伤害量
2. 调整护盾计数逻辑,改为按次数统计而非伤害量
3. 更新护盾相关提示文本与UI表现
4. 重构小铁卫与小刺客的英雄成长配置,补充详细设计文档
5. 删除冗余的旧版英雄配置文件
2026-07-24 09:44:43 +08:00

243 lines
10 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 { SkillTriggerType } from "../common/config/heroSet";
import { Tooltip } from "./Tooltip";
const { ccclass, property } = _decorator;
/** 视图层对象 */
@ccclass('TooltipCom')
@ecs.register('TooltipView', false)
export class TooltipCom extends CCComp {
@property(Node)
atking_bg: Node = null;
@property(Node)
atked_bg: Node = null;
@property(Node)
fstart_bg: Node = null;
@property(Node)
fend_bg: Node = null;
@property(Node)
dead_bg: Node = null;
@property(Node)
revive_bg: Node = null;
stype: number = 1; // 1:减少生命值2增加生命值3技能图标
value: string = "";
s_uuid: number = 1001;
fac: number = FacSet.MON;
/** 当前技能喊话对应的触发类型(空字符串表示普通主动技能) */
triggerType: string = "";
// 动画参数配置
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, triggerType: string = "") {
this.stype = type;
this.value = value;
this.s_uuid = uuid;
this.fac = fac;
this.triggerType = triggerType;
// 初始化或获取 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 + 30;
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.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);
// this.node.setPosition(v3(this.node.position.x, currentY));
this.node.setSiblingIndex(topSiblingIndex);
// 根据触发类型激活对应的背景标识(追击/反击/起手/生息/亡语/复活)
this.setupTriggerBg(this.triggerType);
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.setupLabel("add_life", "hp", "格挡");
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;
}
}
/**
* 根据技能触发类型激活对应的背景标识节点
* 仅 TooltipTypes.skill 走此分支,其他飘字类型不受影响
* 对象池复用场景下先统一关闭所有 _bg避免上一次的状态残留
*/
private setupTriggerBg(triggerType: string) {
// 先关闭所有触发类型背景,防止节点池复用时残留
this.atking_bg && (this.atking_bg.active = false);
this.atked_bg && (this.atked_bg.active = false);
this.fstart_bg && (this.fstart_bg.active = false);
this.fend_bg && (this.fend_bg.active = false);
this.dead_bg && (this.dead_bg.active = false);
this.revive_bg && (this.revive_bg.active = false);
if (!triggerType) return;
const bgMap: Record<string, Node | null> = {
[SkillTriggerType.Atking]: this.atking_bg,
[SkillTriggerType.Atked]: this.atked_bg,
[SkillTriggerType.FStart]: this.fstart_bg,
[SkillTriggerType.FEnd]: this.fend_bg,
[SkillTriggerType.Dead]: this.dead_bg,
[SkillTriggerType.Revive]: this.revive_bg,
};
const bg = bgMap[triggerType];
if (bg) bg.active = true;
}
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);
}
}