Reputation: 2044
I have 4 perfectly elastic walls arranged as a box and a perfectly elastic ball in zero gravity. The ball is given an initial velocity of (100., 100.)
and should bounce between the four borders of the box forever.
However, for some unknown reason, the y component of the velocity tends toward zero after each bounce, until eventually, it is just bouncing off the left and right walls with no vertical velocity at all.
Here is a minimal working example of my code:
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
}
}
Why does this happen? What is causing the ball to lose the y component of its velocity? How can I fix it?
Upvotes: 0
Views: 80
Reputation: 2044
I needed to set the friction of both the collider and the ball to zero.
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);
}
Upvotes: 1