消消方块阵换皮表情
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.

101 lines
3.5 KiB

1 week ago
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' // 添加缓动函数
});
}
}