我在用Rust和 Macroquad,但我的球的位置却跑到了错误的一边?

人工智能 2026-07-08

我最近学Rust几天了,正在尝试做一个球弹跳的模拟。它本来工作,但在我加入delta之后开始出问题,球在底部生成后就立刻往上弹。我尝试了很多工具,甚至AI,但都没解决。它在加入delta之前还能工作,从那以后就再也没行过。我用cargo run运行,编辑器是Sublime Text。

use macroquad::prelude::*;
use std::io;

fn window_conf() -> Conf {
    Conf {
        window_width: 800,
        window_height: 600,
        ..Default::default()
    }
}

#[macroquad::main("Physics Simulation")]
async fn main() {
    let mut radius_input = String::new();
    let mut drop_distance_input = String::new();
    let mut total_distance_input = String::new();
    let mut gravity_input = String::new();
    let mut coefficient_of_restitution_input = String::new();

    let x: f32 = 400.0;
    let mut y: f32;
    let playback_rate: f32 = 1.0;

    let mut velocity: f32 = 0.0;

    println!("Total Height (metre): ");
    io::stdin()
        .read_line(&mut total_distance_input)
        .unwrap();

    let total_height: f32 = total_distance_input.trim().parse().unwrap();

    let pixel_to_height_ratio: f32 = 600.0 / total_height;

    println!("Drop Distance (metre): ");
    io::stdin()
        .read_line(&mut drop_distance_input)
        .unwrap();

    let drop_distance: f32 = drop_distance_input.trim().parse().unwrap();

    println!("Radius (metre): ");
    io::stdin()
        .read_line(&mut radius_input)
        .unwrap();

    let mut radius: f32 = radius_input.trim().parse().unwrap();
    radius *= pixel_to_height_ratio;

    // Convert height above ground into screen coordinates
    let drop_distance_pixels = drop_distance * pixel_to_height_ratio;
    y = 600.0 - drop_distance_pixels - radius;

    println!("Gravity (9.78 m/s on earth): ");
    io::stdin()
        .read_line(&mut gravity_input)
        .unwrap();

    let mut gravity: f32 = gravity_input.trim().parse().unwrap();
    gravity *= pixel_to_height_ratio;

    println!("Coefficient of Restitution (0 to 1): ");
    io::stdin()
        .read_line(&mut coefficient_of_restitution_input)
        .unwrap();

    let coefficient_of_restitution: f32 =
        coefficient_of_restitution_input.trim().parse().unwrap();

    loop {
        clear_background(BLACK);

        draw_circle(x, y, radius, RED);

        let dt = get_frame_time();

        velocity += gravity * dt * playback_rate;
        y += velocity * dt * playback_rate;

        if y + radius >= 600.0 {
            y = 600.0 - radius;
            velocity *= -coefficient_of_restitution;
        }

        next_frame().await;
    }
}

解决方案

问题其实很简单: 你正在从标准输入读取仿真数值,这使仿真实际上处于“半启动”状态。

因此,你得到的第一個(其实是第二个,因为 macroquad计算帧时间的复杂性dt 值来自于 get_frame_time,这是因为它包含了把数值输入到标准输入所花的时间(如果你添加适当的日志,就能很容易看出来)。

这会导致球在初始帧中因为计算出的高速度而向上冲出。下面的输入可以用来验证这一点:

Total Height (metre): 
100
Drop Distance (metre): 
10
Radius (metre): 
1
Gravity (9.78 m/s on earth): 
9.78
Coefficient of Restitution (0 to 1): 
0.5

如果你把它们输入得相当快,然后再等大约30秒,你会看到球再次向下落下并按预期弹跳。

最简单的修复方法就是对 dt 值进行夹紧。若你的仿真以60 FPS运行,像下面这样似乎就能正常工作:

let dt = get_frame_time().min(1.0 / 30.0);

不过,我个人建议直接使用另一种提供这些设置的方式(也许从文件读取之类的)。

另一种做法是只在绘制完前两个帧后再开始仿真(第二帧的时间因为stdin的延迟而变得非常巨大),但我不确定这是否比单纯夹紧更优,因为前者需要额外的逻辑。

let mut frames = 0;
loop {
    clear_background(BLACK);

    draw_circle(x, y, radius, RED);

    let dt = get_frame_time();

    if frames > 2 {
        velocity += gravity * dt * playback_rate;
        y += velocity * dt * playback_rate;

        if y + radius >= 600.0 {
            y = 600.0 - radius;
            velocity *= -coefficient_of_restitution;
        }
    }
    frames += 1;

    next_frame().await;
}

附注:如果在读取最后一个输入后就能重置引擎计时器,那就太好了,但遗憾的是,在macroquad上似乎并不能这样做。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章