You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
4.7 KiB
137 lines
4.7 KiB
|
|
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();
|
|
}
|
|
}
|
|
|