import { _decorator, Component, Vec3, tween, Node, UIOpacity, isValid, Tween } from 'cc'; const { ccclass, property } = _decorator; @ccclass('FlyCurrency') export class FlyCurrency extends Component { // 动画参数配置 private readonly SPAWN_RADIUS: number = 120; // 初始扩散半径增大 private readonly PEAK_HEIGHT: number = 200; // 抛物线高度增加 private readonly FLY_DURATION: number = 0.9; // 基础飞行时间 // 初始化方法(使用世界坐标系) public init(worldStart: Vec3, worldEnd: Vec3) { // 确保使用世界坐标 const spawnPos = this.calcSpawnPosition(worldStart); this.setInitialState(spawnPos); // 动画参数计算 const delay = Math.random() * 0.4; const duration = this.FLY_DURATION + Math.random() * 0.3; const rotation = 720 * (Math.random() > 0.5 ? 1 : -1); // 入场动画序列 this.playEntranceAnimation(delay, duration, rotation, spawnPos, worldEnd); } // 计算初始散布位置 private calcSpawnPosition(center: Vec3): Vec3 { const angle = Math.random() * Math.PI * 2; const radius = Math.random() * this.SPAWN_RADIUS; return new Vec3( center.x + Math.cos(angle) * radius, center.y + Math.sin(angle) * radius, 0 ); } // 设置初始状态 private setInitialState(pos: Vec3) { const opacity = this.node.addComponent(UIOpacity); opacity.opacity = 0; // 初始完全透明 this.node.setWorldPosition(pos); this.node.setScale(Vec3.ZERO); // 初始缩放为0 this.node.angle = Math.random() * 360; } // 播放入场动画 private playEntranceAnimation( delay: number, duration: number, rotation: number, start: Vec3, end: Vec3 ) { tween(this.node) .delay(delay) // 第一阶段:弹性弹出 + 渐显 .parallel( tween() .to(0.3, { scale: new Vec3(1.2, 1.2, 1) }, { easing: 'backOut' }), tween(this.node.getComponent(UIOpacity)!) .to(0.2, { opacity: 255 }) ) // 第二阶段:飞行主动画 .parallel( this.createParabolaTween(start, end, duration), tween().by(duration, { angle: rotation }), tween().to(duration * 0.5, { scale: Vec3.ONE }) ) // 第三阶段:离场缩小 .to(0.2, { scale: Vec3.ZERO }) .call(() => { if (isValid(this.node)) { this.node.destroy(); } }) .start(); } protected onDestroy(): void { Tween.stopAllByTarget(this.node); } // 创建抛物线动画 private createParabolaTween(start: Vec3, end: Vec3, duration: number) { return tween().to(duration, {}, { onUpdate: (_, ratio: number) => { if (!isValid(this.node)) { return; } const currentX = start.x + (end.x - start.x) * ratio; const yProgress = Math.sin(ratio * Math.PI); // 正弦曲线 const currentY = start.y + (end.y - start.y) * ratio + yProgress * this.PEAK_HEIGHT; this.node.setWorldPosition(new Vec3(currentX, currentY, 0)); }, easing: 'sineOut' // 添加缓动函数 }); } }