我有 4 个完美弹性的墙,排列成一个盒子,还有一个零重力下的完美弹性球。球的初始速度为
(100., 100.)
,并且应该永远在盒子的四个边界之间弹跳。
然而,由于某种未知的原因,速度的 y 分量在每次弹跳后都趋向于零,直到最终,它只是从左右墙壁弹起,根本没有垂直速度。
这是我的代码的最小工作示例:
use rapier2d::prelude as rapier;
use macroquad::prelude as mq;
struct PhysicsWorld {
gravity: rapier::Vector<f32>,
integration_parameters: rapier::IntegrationParameters,
pipeline: rapier::PhysicsPipeline,
island_manager: rapier::IslandManager,
broad_phase: rapier::BroadPhase,
narrow_phase: rapier::NarrowPhase,
rigid_body_set: rapier::RigidBodySet,
collider_set: rapier::ColliderSet,
impulse_joint_set: rapier::ImpulseJointSet,
multibody_joint_set: rapier::MultibodyJointSet,
ccd_solver: rapier::CCDSolver,
query_pipeline: rapier::QueryPipeline,
physics_hooks: (),
event_handler: (),
}
impl Default for PhysicsWorld {
fn default() -> Self {
let gravity = rapier::Vector::zeros();
let integration_parameters = rapier::IntegrationParameters{
num_solver_iterations: std::num::NonZeroUsize::new(16).unwrap(),
prediction_distance: 0.001,
..Default::default()
};
let pipeline = rapier::PhysicsPipeline::new();
let island_manager = rapier::IslandManager::new();
let broad_phase = rapier::BroadPhase::new();
let narrow_phase = rapier::NarrowPhase::new();
let rigid_body_set = rapier::RigidBodySet::new();
let collider_set = rapier::ColliderSet::new();
let impulse_joint_set = rapier::ImpulseJointSet::new();
let multibody_joint_set = rapier::MultibodyJointSet::new();
let ccd_solver = rapier::CCDSolver::new();
let query_pipeline = rapier::QueryPipeline::new();
let physics_hooks = ();
let event_handler = ();
Self {
gravity,
integration_parameters,
pipeline,
island_manager,
broad_phase,
narrow_phase,
rigid_body_set,
collider_set,
impulse_joint_set,
multibody_joint_set,
ccd_solver,
query_pipeline,
physics_hooks,
event_handler,
}
}
}
impl PhysicsWorld {
fn update(&mut self) {
self.pipeline.step(
&self.gravity,
&self.integration_parameters,
&mut self.island_manager,
&mut self.broad_phase,
&mut self.narrow_phase,
&mut self.rigid_body_set,
&mut self.collider_set,
&mut self.impulse_joint_set,
&mut self.multibody_joint_set,
&mut self.ccd_solver,
Some(&mut self.query_pipeline),
&self.physics_hooks,
&self.event_handler,
);
}
pub fn add_ball_with_velocity(&mut self, radius: f32, x: f32, y: f32, vx: f32, vy: f32) -> (rapier::RigidBodyHandle, rapier::ColliderHandle) {
let rigid_body = rapier::RigidBodyBuilder::dynamic().translation(rapier::vector![x, y]).linvel(rapier::vector![vx, vy]).build();
let collider = rapier::ColliderBuilder::ball(radius).restitution(1.0).build();
let ball_r_handle = self.rigid_body_set.insert(rigid_body);
let ball_c_handle = self.collider_set.insert_with_parent(collider, ball_r_handle, &mut self.rigid_body_set);
return (ball_r_handle, ball_c_handle);
}
pub fn add_fixed_cuboid(&mut self, x: f32, y: f32, hx: f32, hy: f32) -> (rapier::RigidBodyHandle, rapier::ColliderHandle) {
let rigid_body = rapier::RigidBodyBuilder::fixed().translation(rapier::vector![x, y]).build();
let collider = rapier::ColliderBuilder::cuboid(hx, hy).restitution(1.0).build();
let cuboid_r_handle = self.rigid_body_set.insert(rigid_body);
let cuboid_c_handle = self.collider_set.insert_with_parent(collider, cuboid_r_handle, &mut self.rigid_body_set);
return (cuboid_r_handle, cuboid_c_handle);
}
pub fn add_window_borders(&mut self) {
let width = mq::screen_width();
let height = mq::screen_height();
let thickness = 10.0;
let h_center_x = width / 2.0;
let v_center_y = height / 2.0;
let h_top_y = height - thickness / 2.0; //(center of top border)
let h_bottom_y = thickness / 2.0; //(center of bottom border)
let v_left_x = thickness / 2.0; //(center of left border)
let v_right_x = width - thickness / 2.0; //(center of right border)
let _top = self.add_fixed_cuboid(h_center_x, h_top_y, width / 2.0, thickness / 2.0);
let _bottom = self.add_fixed_cuboid(h_center_x, h_bottom_y, width / 2.0, thickness / 2.0);
let _left = self.add_fixed_cuboid(v_left_x, v_center_y, thickness / 2.0, height / 2.0);
let _right = self.add_fixed_cuboid(v_right_x, v_center_y, thickness / 2.0, height / 2.0);
}
//draw the colliders (using the rigid body they are attached to)
pub fn draw_colliders(&self) {
for (_handle, collider) in self.collider_set.iter() {
let rigid_body_handle = collider.parent().expect("Collider has no parent");
let body = self.rigid_body_set.get(rigid_body_handle).unwrap();
let pos = body.position().translation.vector;
let shape: &dyn rapier::Shape = collider.shape();
match (shape.as_ball(), shape.as_cuboid()) {
(Some(ball), None) => {
mq::draw_circle(pos.x, pos.y, ball.radius, mq::WHITE);
}
(None, Some(cuboid)) => {
mq::draw_rectangle(pos.x - cuboid.half_extents.x, pos.y - cuboid.half_extents.y, cuboid.half_extents.x * 2.0, cuboid.half_extents.y * 2.0, mq::WHITE);
}
_ => {unreachable!()}
}
}
}
pub fn draw_ball_velocity_vector(&self, handle: rapier::RigidBodyHandle) {
let body = self.rigid_body_set.get(handle).unwrap();
let vel = body.linvel();
mq::draw_text(&format!("vx: {:.2}, vy: {:.2}", vel.x, vel.y), 10., 30., 20.0, mq::WHITE);
}
}
const ASPECT_RATIO: f32 = 16.0 / 9.0;
const WINDOW_WIDTH: f32 = 300.0;
const WINDOW_HEIGHT: f32 = WINDOW_WIDTH * ASPECT_RATIO;
fn main() {
macroquad::Window::from_config(
mq::Conf {
window_width: WINDOW_WIDTH as i32,
window_height: WINDOW_HEIGHT as i32,
..Default::default()
},
amain(),
);
}
async fn amain() {
let mut world = PhysicsWorld::default();
let (ball, _) = world.add_ball_with_velocity(10.0, 200.0, 200.0, 100.0, 100.0);
world.add_window_borders();
loop {
world.update();
mq::clear_background(mq::BLACK);
world.draw_ball_velocity_vector(ball);
world.draw_colliders();
mq::next_frame().await
}
}
为什么会出现这种情况?是什么导致球失去其速度的 y 分量?我该如何解决它?
我需要将对撞机和球的摩擦力设置为零。
pub fn add_ball_with_velocity(&mut self, radius: f32, x: f32, y: f32, vx: f32, vy: f32) -> (rapier::RigidBodyHandle, rapier::ColliderHandle) {
let rigid_body = rapier::RigidBodyBuilder::dynamic()
.translation(rapier::vector![x, y])
.linvel(rapier::vector![vx, vy])
.lock_rotations()
.build();
let collider = rapier::ColliderBuilder::ball(radius)
.restitution(1.0)
.friction(0.)
.build();
let ball_r_handle = self.rigid_body_set.insert(rigid_body);
let ball_c_handle = self.collider_set.insert_with_parent(collider, ball_r_handle, &mut self.rigid_body_set);
return (ball_r_handle, ball_c_handle);
}
pub fn add_fixed_cuboid(&mut self, x: f32, y: f32, hx: f32, hy: f32) -> (rapier::RigidBodyHandle, rapier::ColliderHandle) {
let rigid_body = rapier::RigidBodyBuilder::fixed()
.translation(rapier::vector![x, y])
.build();
let collider = rapier::ColliderBuilder::cuboid(hx, hy)
.restitution(1.0)
.friction(0.)
.build();
let cuboid_r_handle = self.rigid_body_set.insert(rigid_body);
let cuboid_c_handle = self.collider_set.insert_with_parent(collider, cuboid_r_handle, &mut self.rigid_body_set);
return (cuboid_r_handle, cuboid_c_handle);
}