我在用Rust和 Macroquad,但我的球的位置却跑到了错误的一边?
我最近学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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。