Bevy引擎中物体按指定时间减速至目标点的实现问题
问题分析与解决方案
核心错误点
- 运动学参数冲突:你试图同时指定初始速度、位移、运动时长三个参数,要求物体做匀变速运动并在时长结束时到达终点且速度为0,但这三个参数在匀变速运动下存在严格约束——初始速度必须由位移和时长计算得出,不能自由指定。你的例子中,位移大小是75,时长10秒,正确的初始速度大小应该是15,但你传入了100,直接导致物体提前到达终点。
- 加速度公式错误:
find_accel_to_stop_at_destination里的推导公式不符合匀变速运动的约束条件,计算出的加速度无法满足“末速度为0”和“指定时间到达”的双重要求。 - 提前停止逻辑干扰:
do_move中距离终点小于1就强制停止的逻辑,打断了完整的运动过程,无法让物体运行到指定时长。
修正方案
方案1:基于匀变速运动的标准实现(推荐)
既然需求是“指定时间到达+末速度为0”,初始速度必须由位移和时长推导,移除自由指定的初始速度参数:
调整结构体与构建函数
pub struct MoveToBuilder { pub start: Vec2, pub destination: Vec2, pub time: f32, // 仅保留起点、终点、时长三个必要参数 } pub struct MoveTo { pub destination: Vec2, pub velocity: Vec2, pub acceleration: Vec2, pub total_time: f32, pub elapsed_time: f32, // 记录已运动时长,避免提前停止 } pub fn build_move_to(builder: MoveToBuilder) -> MoveTo { let displacement = builder.destination - builder.start; let time = builder.time; // 用匀变速运动约束公式计算初速度和加速度 let velocity = 2.0 * displacement / time; let acceleration = -2.0 * displacement / (time * time); MoveTo { destination: builder.destination, velocity, acceleration, total_time: time, elapsed_time: 0.0, } }
修正运动更新逻辑
impl MovementPattern for MoveTo { fn do_move(&mut self, transform: &mut Transform, time: &Res<Time>) { let delta_time = time.delta_secs(); // 仅在运动时长达标时停止,避免提前终止 if self.elapsed_time >= self.total_time { self.velocity = Vec2::ZERO; // 强制设置到终点,消除浮点误差 transform.translation = self.destination.extend(transform.translation.z); return; } // 更新速度与位置 self.velocity += self.acceleration * delta_time; let translation_delta = self.velocity * delta_time; transform.translation += translation_delta.extend(0.0); // 更新已流逝时间 self.elapsed_time += delta_time; } }
方案2:保留初始速度的变加速实现
如果必须指定初始速度,匀变速运动无法满足需求,可使用三次贝塞尔曲线实现平滑的变加速运动,确保初速度、末速度、时长、位置全部符合要求:
// 示例:基于三次贝塞尔曲线的运动构建 pub struct MoveTo { pub start: Vec2, pub destination: Vec2, pub p1: Vec2, // 贝塞尔控制点1 pub p2: Vec2, // 贝塞尔控制点2 pub total_time: f32, pub elapsed_time: f32, } pub fn build_move_to_with_initial_speed(start: Vec2, dest: Vec2, initial_speed: f32, time: f32) -> MoveTo { let direction = (dest - start).normalize(); let initial_velocity = direction * initial_speed; let final_velocity = Vec2::ZERO; // 末速度为0 // 计算贝塞尔控制点,匹配初末速度 let p1 = start + initial_velocity * time / 3.0; let p2 = dest - final_velocity * time / 3.0; MoveTo { start, destination: dest, p1, p2, total_time: time, elapsed_time: 0.0, } } // 运动更新逻辑 impl MovementPattern for MoveTo { fn do_move(&mut self, transform: &mut Transform, time: &Res<Time>) { let delta_time = time.delta_secs(); if self.elapsed_time >= self.total_time { transform.translation = self.destination.extend(transform.translation.z); return; } let t_ratio = self.elapsed_time / self.total_time; // 三次贝塞尔曲线位置计算 let t = t_ratio; let one_minus_t = 1.0 - t; let position = one_minus_t.powf(3.0) * self.start + 3.0 * one_minus_t.powf(2.0) * t * self.p1 + 3.0 * one_minus_t * t.powf(2.0) * self.p2 + t.powf(3.0) * self.destination; transform.translation = position.extend(transform.translation.z); self.elapsed_time += delta_time; } }
示例验证
用方案1处理你的测试案例:
- 位移:
Vec2(0, -75)(从241到166) - 时长:10秒
- 计算得到初始速度:
Vec2(0, -15),加速度:Vec2(0, 1.5)
物体将在10秒时恰好到达终点,速度从-15均匀减至0,完全符合需求。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

