//! Demonstrates Continuous Collision Detection (CCD) to prevent tunneling. //! //! Two forms of CCD are supported: //! //! 1. Speculative collision //! - Predicts approximate contacts before they happen. //! - Enabled by default for all rigid bodies. //! - Very efficient and relatively reliable. //! - Can sometimes cause ghost collisions. //! - Can sometimes miss collisions against objects spinning at very high speeds. //! //! 2. Swept CCD //! - Sweeps colliders from their previous positions to their current ones, //! and if a hit is found, moves the bodies to the time of impact. //! - Enabled for rigid bodies with the `SweptCcd` component. //! - Can prevent tunneling completely. More reliable than speculative collision. //! - More expensive than speculative collision. //! - Can cause "time loss" where bodies appear to stop for a moment //! because they are essentially brought back in time. //! - Two modes: //! 1. Linear: Only considers translational motion. //! 2. Non-linear: Considers both translation and rotation. More expensive. use avian2d::{math::*, prelude::*}; use bevy::{prelude::*, sprite::MaterialMesh2dBundle}; use examples_common_2d::ExampleCommonPlugin; fn main() { App::new() .add_plugins(( DefaultPlugins, ExampleCommonPlugin, PhysicsPlugins::default(), )) .insert_resource(ClearColor(Color::srgb(0.05, 0.05, 0.1))) .insert_resource(Gravity(Vector::NEG_Y * 9.81 * 100.0)) .add_systems(Startup, setup) .add_systems(Update, update_config) .add_systems(PhysicsSchedule, spawn_balls.in_set(PhysicsStepSet::First)) .run(); } #[derive(Component)] struct SpeculativeCollisionText; #[derive(Component)] struct SweptCcdText; fn setup(mut commands: Commands) { commands.spawn(Camera2dBundle::default()); // Add two kinematic bodies spinning at high speeds. commands.spawn(( SpriteBundle { sprite: Sprite { color: Color::srgb(0.7, 0.7, 0.8), custom_size: Some(Vec2::new(1.0, 400.0)), ..default() }, transform: Transform::from_xyz(-200.0, -200.0, 0.0), ..default() }, RigidBody::Kinematic, AngularVelocity(25.0), Collider::rectangle(1.0, 400.0), // Enable swept CCD for this body. Considers both translational and rotational motion by default. // This could also be on the ball projectiles. SweptCcd::default(), )); commands.spawn(( SpriteBundle { sprite: Sprite { color: Color::srgb(0.7, 0.7, 0.8), custom_size: Some(Vec2::new(1.0, 400.0)), ..default() }, transform: Transform::from_xyz(200.0, -200.0, 0.0), ..default() }, RigidBody::Kinematic, AngularVelocity(-25.0), Collider::rectangle(1.0, 400.0), // Enable swept CCD for this body. Considers both translational and rotational motion by default. // This could also be on the ball projectiles. SweptCcd::default(), )); // Setup help text. let text_style = TextStyle { font_size: 20.0, ..default() }; commands.spawn(( SpeculativeCollisionText, TextBundle::from_sections([ TextSection::new("(1) Speculative Collision: ", text_style.clone()), TextSection::new("On", text_style.clone()), ]) .with_style(Style { position_type: PositionType::Absolute, top: Val::Px(30.0), left: Val::Px(10.0), ..default() }), )); commands.spawn(( SweptCcdText, TextBundle::from_sections([ TextSection::new("(2) Swept CCD: ", text_style.clone()), TextSection::new( "Non-linear (considers both translation and rotation)", text_style, ), ]) .with_style(Style { position_type: PositionType::Absolute, top: Val::Px(50.0), left: Val::Px(10.0), ..default() }), )); } /// Spawns balls moving at the spinning objects at high speeds. fn spawn_balls( mut commands: Commands, mut materials: ResMut>, mut meshes: ResMut>, time: Res