Files
pixelheros/assets/script/game/map/HeroMoveComp.ts
panFD 9c2c0a1621 feat(map): add hero move animation component
新增了英雄移动组件,实现多个英雄的周期性往复移动和动画速度同步匹配
同时更新了预制件配置以适配新的组件和节点命名调整
2026-06-13 22:12:27 +08:00

91 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { _decorator, Component, Node, Animation, Vec3 } from "cc";
const { ccclass, property } = _decorator;
@ccclass('HeroMoveComp')
export class HeroMoveComp extends Component {
@property({ type: Node })
hero1: Node = null!;
@property({ type: Node })
hero2: Node = null!;
@property({ type: Node })
hero3: Node = null!;
@property({ type: Node })
hero4: Node = null!;
@property({ type: Node })
hero5: Node = null!;
// 控制移动速度的基础参数
private baseSpeed: number = 20; // 每秒向右移动的基础像素速度
private timer: number = 0;
// 为每个英雄保存其对应的 Animation 和随机参数
private heroDatas: { node: Node, anim: Animation | null, startX: number, phase: number }[] = [];
onLoad() {
const heroes = [this.hero1, this.hero2, this.hero3, this.hero4, this.hero5];
heroes.forEach(heroNode => {
if (heroNode) {
// 查找名为 "h" 的子节点
const hNode = heroNode.getChildByName("h");
let anim: Animation | null = null;
if (hNode) {
anim = hNode.getComponent(Animation);
// 确保有动画组件才尝试播放
if (anim && anim.clips && anim.clips.length > 0) {
anim.play("move");
}
}
this.heroDatas.push({
node: heroNode,
anim: anim,
startX: heroNode.position.x, // 记录初始X位置
phase: Math.random() * Math.PI * 2 // 随机初始相位
});
}
});
}
update(dt: number) {
this.timer += dt;
this.heroDatas.forEach(data => {
if (!data.node) return;
// 设置周期为 4 秒 (例如1秒加速向右1秒减速回原点1秒减速向左1秒加速回原点)
// 使用正弦波控制速度,余弦波控制位移(因为速度是位移的导数)
const cycleDuration = 4;
const frequency = (Math.PI * 2) / cycleDuration;
// 速度倍率,范围在 0.9 ~ 1.1 之间波动
// sin 波形在 4秒周期内正负各占 2秒
const speedMultiplier = 1.0 + Math.sin(this.timer * frequency + data.phase) * 0.1;
// 调整动画的播放速度
if (data.anim) {
// 检查状态是否存在,以避免没有播放动画时的错误
const state = data.anim.getState("move");
if (state && state.isPlaying) {
state.speed = speedMultiplier;
} else if (data.anim.clips && data.anim.clips.length > 0) {
// 如果停止了,尝试重新播放
data.anim.play("move");
}
}
// 根据时间计算位置偏移量
// 使用 -cos 函数使得当速度sin为最大时位置在原点
// 积分关系:速度 v = A * sin(wt),位移 s = - (A/w) * cos(wt)
// 这样能保证在一个周期内它始终围绕原点往复运动,最终回到原始点
const maxOffset = 30; // 左右偏离原点的最大像素距离
const currentOffset = -Math.cos(this.timer * frequency + data.phase) * maxOffset;
const pos = data.node.position;
// 相对于初始点进行位置设置,确保周期性回到原始点
data.node.setPosition(new Vec3(data.startX + currentOffset, pos.y, pos.z));
});
}
}