import { assetManager, instantiate, Label, Node, Prefab, tween, Tween, UIOpacity, UITransform, Vec3 } from 'cc'; import { GBundle, GPath } from '../../game/ConfigRes'; import { UILayer } from '../../../mx/module/ui/UIManager'; const POWER_PREFAB_NAME = 'power'; const FLOAT_UP_PX = 80; const FLOAT_DURATION_SEC = 1.2; /** * Battlebuff 体力增加飘字(comm/prefab/power,addNum 显示 +N) */ export class BattleBuffVigourFloatView { /** 播放体力增加飘字 */ static play(amount: number, parent?: Node | null, worldPos?: Vec3 | null): void { const delta = Math.max(0, Math.floor(Number(amount) || 0)); if (delta <= 0) return; const mountParent = parent?.isValid ? parent : null; if (!mountParent && !gg.ui) { console.log('[BattleBuffVigourFloatView] UI 未就绪'); return; } const attach = (prefabNode: Node): void => { BattleBuffVigourFloatView._setupNode(prefabNode, mountParent, worldPos, delta); }; const cachedPrefab = gg.res.getRes(POWER_PREFAB_NAME, GPath.Prefab, GBundle.Comm, Prefab); if (cachedPrefab) { attach(instantiate(cachedPrefab)); return; } const bundle = assetManager.getBundle(GBundle.Comm); if (!bundle) { console.log('[BattleBuffVigourFloatView] comm bundle 未加载'); return; } const full = `${GPath.Prefab}${POWER_PREFAB_NAME}`; const loadedPrefab = bundle.get(full, Prefab); if (loadedPrefab) { attach(instantiate(loadedPrefab)); return; } bundle.load(full, Prefab, (err, loaded) => { if (err || !loaded) { if (err) console.log('[BattleBuffVigourFloatView] power 预制体加载失败', String(err)); return; } attach(instantiate(loaded)); }); } private static _setupNode(node: Node, parent: Node | null, worldPos: Vec3 | null | undefined, delta: number): void { if (parent?.isValid) { node.parent = parent; } else { gg.ui.showNode(node, UILayer.Anim); } node.active = true; const layerParent = node.parent; if (!layerParent?.isValid) { node.destroy(); return; } const addNum = node.getChildByName('addNum')?.getComponent(Label); if (!addNum) { console.log('[BattleBuffVigourFloatView] addNum 节点缺失'); node.destroy(); return; } addNum.string = `+${delta}`; const local = BattleBuffVigourFloatView._resolveLocalPos(layerParent, worldPos); node.setPosition(local.x, local.y, local.z); const opacity = node.getComponent(UIOpacity) ?? node.addComponent(UIOpacity); opacity.opacity = 255; Tween.stopAllByTarget(node); Tween.stopAllByTarget(opacity); const endY = local.y + FLOAT_UP_PX; tween(node) .to(FLOAT_DURATION_SEC, { position: new Vec3(local.x, endY, local.z) }, { easing: 'cubicOut' }) .call(() => { if (node.isValid) node.destroy(); }) .start(); tween(opacity) .delay(FLOAT_DURATION_SEC * 0.35) .to(FLOAT_DURATION_SEC * 0.65, { opacity: 0 }) .start(); } private static _resolveLocalPos(parent: Node, worldPos?: Vec3 | null): Vec3 { const ut = parent.getComponent(UITransform); if (worldPos && ut) { return ut.convertToNodeSpaceAR(worldPos); } return new Vec3(0, 0, 0); } }