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 { 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; // 动画参数配置 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) { this.stype = type; this.value = value; this.s_uuid = uuid; // 初始化或获取 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("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); } }