You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 08:42:35