import { _decorator, Component, Node, tween, Tween, Vec3 } from 'cc'; const { ccclass } = _decorator; /** 蹦跳峰值相对起点世界 Y 偏移(与乾坤袋回收飘桃一致) */ const BOUNCE_WORLD_OFFSET_Y = 50; /** 直飞基础时长(秒) */ const FLY_DIRECT_BASE_SEC = 0.65; /** 直飞随机附加时长(秒) */ const FLY_DIRECT_RANDOM_SEC = 0.15; @ccclass('CoinFly') export class CoinFly extends Component { protected onLoad(): void { } /** * 蹦一下再飘向目标(全程世界坐标,避免挂 ui_fly 后本地 y 偏移变成「往上冒」)。 * @param endPos 终点世界坐标;null 则只原地弹跳后回池(AI 击杀掉落) * @param callfunc 到达回调(传入本节点,便于拆掉临时子节点) * @param startDelay 起飞前延迟(秒) * @param flySec 飞向终点的时长(秒);≤0 用默认短飞行 */ flyAni(endPos: Vec3 | null, callfunc: ((host?: Node) => void) | null = null, startDelay = 0, flySec = 0): void { const node = this.node; if (!node?.isValid) return; Tween.stopAllByTarget(node); const start = node.worldPosition.clone(); const peak = new Vec3(start.x, start.y + BOUNCE_WORLD_OFFSET_Y, start.z); const chain = tween(node) .delay(Math.max(0, startDelay)) .to(0.2, { worldPosition: peak }, { easing: 'backOut' }) .to(0.2, { worldPosition: start }, { easing: 'backOut' }) .delay(0.3); if (endPos) { const sec = flySec > 0 ? flySec : 0.1 + Math.random() * 0.1; chain.to(sec, { worldPosition: endPos.clone() }); } chain .call(() => { callfunc?.(node); gg.res.putNode(node); }) .start(); } /** 弹跳结束回调(只蹦不飞) */ private _afterPop: (() => void) | null = null; /** 直飞结束回调 */ private _afterFly: ((host?: Node) => void) | null = null; /** 弹跳结束 */ private readonly _finishPopStay = (): void => { const done = this._afterPop; this._afterPop = null; done?.(); }; /** 直飞结束:回调后回池 */ private readonly _finishFlyTo = (): void => { const node = this.node; const done = this._afterFly; this._afterFly = null; done?.(node); if (node?.isValid) gg.res.putNode(node); }; /** * 只蹦一下并停在落点,不飞向终点、不回池。 * @param onDone 弹跳结束 */ popStay(onDone: (() => void) | null = null): void { const node = this.node; this._afterPop = onDone; if (!node?.isValid) { this._finishPopStay(); return; } Tween.stopAllByTarget(node); const start = node.worldPosition.clone(); const peak = new Vec3(start.x, start.y + BOUNCE_WORLD_OFFSET_Y, start.z); tween(node) .to(0.2, { worldPosition: peak }, { easing: 'backOut' }) .to(0.2, { worldPosition: start }, { easing: 'backOut' }) .delay(0.3) .call(this._finishPopStay) .start(); } /** * 从当前位置飞向终点后回池。时长与击杀掉落的短飞行一致。 * @param endPos 终点世界坐标 * @param callfunc 到达回调(传入本节点) * @param flySec 飞行时长(秒);≤0 用击杀掉落的短飞行 */ flyTo(endPos: Vec3, callfunc: ((host?: Node) => void) | null = null, flySec = 0): void { const node = this.node; this._afterFly = callfunc; if (!node?.isValid) { this._finishFlyTo(); return; } Tween.stopAllByTarget(node); const sec = flySec > 0 ? flySec : 0.1 + Math.random() * 0.1; tween(node) .to(sec, { worldPosition: endPos.clone() }) .call(this._finishFlyTo) .start(); } /** * 从当前位置直接飘向目标(无蹦跳、无额外等待)。 * @param endPos 终点世界坐标 * @param callfunc 到达回调(传入本节点) * @param startDelay 起飞前延迟(秒) */ flyDirectAni(endPos: Vec3, callfunc: ((host?: Node) => void) | null = null, startDelay = 0): void { const node = this.node; if (!node?.isValid) return; Tween.stopAllByTarget(node); const end = endPos.clone(); tween(node) .delay(Math.max(0, startDelay)) .to(FLY_DIRECT_BASE_SEC + Math.random() * FLY_DIRECT_RANDOM_SEC, { worldPosition: end }, { easing: 'sineOut' }) .call(() => { callfunc?.(node); gg.res.putNode(node); }) .start(); } }