diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 44d1e604..55347dff 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -19,6 +19,17 @@ jobs:
- name: Run cargo check
run: cargo check
+ docs:
+ name: Check Documentation
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ - name: Run cargo doc
+ run: cargo doc --workspace --no-deps --document-private-items --keep-going
+ env:
+ RUSTDOCFLAGS: "-D warnings"
+
test:
name: Test Suite
strategy:
@@ -31,7 +42,7 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Run cargo test
- run: cargo test --no-default-features --features enhanced-determinism,collider-from-mesh,serialize,debug-plugin,avian2d/2d,avian3d/3d,avian2d/f64,avian3d/f64,default-collider,parry-f64,bevy_scene
+ run: cargo test --no-default-features --features enhanced-determinism,collider-from-mesh,serialize,debug-plugin,avian2d/2d,avian3d/3d,avian2d/f64,avian3d/f64,default-collider,parry-f64,bevy_scene,bevy_picking
lints:
name: Lints
diff --git a/README.md b/README.md
index dc540640..68e41e9a 100644
--- a/README.md
+++ b/README.md
@@ -121,7 +121,7 @@ fn setup(
Collider::cuboid(1.0, 1.0, 1.0),
AngularVelocity(Vec3::new(2.5, 3.5, 1.5)),
PbrBundle {
- mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)),
+ mesh: meshes.add(Cuboid::from_length(1.0)),
material: materials.add(Color::srgb_u8(124, 144, 255)),
transform: Transform::from_xyz(0.0, 4.0, 0.0),
..default()
@@ -146,7 +146,7 @@ fn setup(
}
```
-
+
## More examples
@@ -165,9 +165,10 @@ cargo run --example cubes --no-default-features --features "3d f64 parry-f64"
## Supported Bevy versions
-| Bevy | Avian |
-| ---- | ----- |
-| 0.14 | 0.1 |
+| Bevy | Avian |
+| ------- | ----- |
+| 0.15 RC | main |
+| 0.14 | 0.1 |
Bevy XPBD versions (the predecessor of Avian)
@@ -179,6 +180,7 @@ cargo run --example cubes --no-default-features --features "3d f64 parry-f64"
| 0.12 | 0.3 |
| 0.11 | 0.2 |
| 0.10 | 0.1 |
+
## Future features
@@ -199,7 +201,7 @@ For larger changes and additions, it's better to open an issue or ask me for inp
before making a pull request.
You can also ask for help or ask questions on the [Bevy Discord](https://discord.com/invite/gMUk5Ph)
-server's `Avian Physics` thread in `#crate-help`. My username on the Discord is `Jondolf` (`@jondolfdev`).
+server's Avian Physics topic in `#ecosystem-crates`. My username on the Discord is `Jondolf` (`@jondolfdev`).
## Acknowledgements
diff --git a/crates/avian2d/Cargo.toml b/crates/avian2d/Cargo.toml
index e6e97a1a..58d51bf0 100644
--- a/crates/avian2d/Cargo.toml
+++ b/crates/avian2d/Cargo.toml
@@ -12,7 +12,15 @@ keywords = ["gamedev", "physics", "simulation", "bevy"]
categories = ["game-development", "science", "simulation"]
[features]
-default = ["2d", "f32", "parry-f32", "debug-plugin", "parallel", "bevy_scene"]
+default = [
+ "2d",
+ "f32",
+ "parry-f32",
+ "debug-plugin",
+ "parallel",
+ "bevy_scene",
+ "bevy_picking",
+]
2d = []
f32 = []
f64 = []
@@ -25,6 +33,7 @@ enhanced-determinism = [
"parry2d?/enhanced-determinism",
"parry2d-f64?/enhanced-determinism",
"bevy_math/libm",
+ "bevy_heavy/libm",
]
default-collider = ["dep:nalgebra"]
@@ -34,11 +43,14 @@ parry-f32 = ["f32", "dep:parry2d", "default-collider"]
parry-f64 = ["f64", "dep:parry2d-f64", "default-collider"]
bevy_scene = ["bevy/bevy_scene"]
+bevy_picking = ["bevy/bevy_picking"]
serialize = [
"dep:serde",
"bevy/serialize",
+ "bevy_transform_interpolation/serialize",
"parry2d?/serde-serialize",
"parry2d-f64?/serde-serialize",
+ "bitflags/serde",
]
[lib]
@@ -49,16 +61,16 @@ bench = false
[dependencies]
avian_derive = { path = "../avian_derive", version = "0.1" }
-bevy = { version = "0.14", default-features = false }
-bevy_math = { version = "0.14" }
+bevy = { version = "0.15", default-features = false }
+bevy_math = { version = "0.15" }
+bevy_heavy = { version = "0.1" }
+bevy_transform_interpolation = { version = "0.1" }
libm = { version = "0.2", optional = true }
-parry2d = { version = "0.15", optional = true }
-parry2d-f64 = { version = "0.15", optional = true }
-nalgebra = { version = "0.32.6", features = [
- "convert-glam027",
-], optional = true }
+parry2d = { version = "0.17", optional = true }
+parry2d-f64 = { version = "0.17", optional = true }
+nalgebra = { version = "0.33", features = ["convert-glam029"], optional = true }
serde = { version = "1", features = ["derive"], optional = true }
-derive_more = "0.99"
+derive_more = "1"
indexmap = "2.0.0"
fxhash = "0.2.1"
itertools = "0.13"
@@ -67,10 +79,13 @@ bitflags = "2.5.0"
[dev-dependencies]
examples_common_2d = { path = "../examples_common_2d" }
benches_common_2d = { path = "../benches_common_2d" }
-bevy_math = { version = "0.14", features = ["approx"] }
+bevy_math = { version = "0.15", features = ["approx"] }
+bevy_heavy = { version = "0.1", features = ["approx"] }
+glam = { version = "0.29", features = ["bytemuck"] }
approx = "0.5"
+bytemuck = "1.19"
criterion = { version = "0.5", features = ["html_reports"] }
-insta = "1.0"
+bevy_mod_debugdump = { git = "https://github.com/jakobhellermann/bevy_mod_debugdump" }
[[example]]
name = "dynamic_character_2d"
@@ -96,10 +111,18 @@ required-features = ["2d", "default-collider"]
name = "custom_collider"
required-features = ["2d"]
+[[example]]
+name = "determinism_2d"
+required-features = ["2d", "default-collider", "enhanced-determinism"]
+
[[example]]
name = "fixed_joint_2d"
required-features = ["2d", "default-collider"]
+[[example]]
+name = "interpolation"
+required-features = ["2d"]
+
[[example]]
name = "move_marbles"
required-features = ["2d", "default-collider"]
@@ -116,7 +139,6 @@ required-features = ["2d", "default-collider"]
name = "revolute_joint_2d"
required-features = ["2d", "default-collider"]
-[[bench]]
-name = "pyramid"
-required-features = ["2d", "default-collider"]
-harness = false
+[[example]]
+name = "debugdump_2d"
+required-features = ["2d"]
diff --git a/crates/avian2d/examples/ccd.rs b/crates/avian2d/examples/ccd.rs
index 42f7686c..e48aad66 100644
--- a/crates/avian2d/examples/ccd.rs
+++ b/crates/avian2d/examples/ccd.rs
@@ -22,7 +22,7 @@
//! 2. Non-linear: Considers both translation and rotation. More expensive.
use avian2d::{math::*, prelude::*};
-use bevy::{prelude::*, sprite::MaterialMesh2dBundle};
+use bevy::prelude::*;
use examples_common_2d::ExampleCommonPlugin;
fn main() {
@@ -41,25 +41,22 @@ fn main() {
}
#[derive(Component)]
-struct SpeculativeCollisionText;
+struct SpeculativeCollisionEnabledText;
#[derive(Component)]
-struct SweptCcdText;
+struct SweptCcdModeText;
fn setup(mut commands: Commands) {
- commands.spawn(Camera2dBundle::default());
+ commands.spawn(Camera2d);
// 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),
+ Sprite {
+ color: Color::srgb(0.7, 0.7, 0.8),
+ custom_size: Some(Vec2::new(1.0, 400.0)),
..default()
},
+ Transform::from_xyz(-200.0, -200.0, 0.0),
RigidBody::Kinematic,
AngularVelocity(25.0),
Collider::rectangle(1.0, 400.0),
@@ -68,15 +65,12 @@ fn setup(mut commands: Commands) {
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),
+ Sprite {
+ color: Color::srgb(0.7, 0.7, 0.8),
+ custom_size: Some(Vec2::new(1.0, 400.0)),
..default()
},
+ Transform::from_xyz(200.0, -200.0, 0.0),
RigidBody::Kinematic,
AngularVelocity(-25.0),
Collider::rectangle(1.0, 400.0),
@@ -86,39 +80,46 @@ fn setup(mut commands: Commands) {
));
// Setup help text.
- let text_style = TextStyle {
+ let font = TextFont {
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()
- }),
- ));
+ commands
+ .spawn((
+ Text::default(),
+ Node {
+ position_type: PositionType::Absolute,
+ top: Val::Px(30.0),
+ left: Val::Px(10.0),
+ ..default()
+ },
+ ))
+ .with_children(|children| {
+ children.spawn((TextSpan::new("(1) Speculative Collision: "), font.clone()));
+ children.spawn((
+ TextSpan::new("On"),
+ font.clone(),
+ SpeculativeCollisionEnabledText,
+ ));
+ });
+ commands
+ .spawn((
+ Text::default(),
+ Node {
+ position_type: PositionType::Absolute,
+ top: Val::Px(50.0),
+ left: Val::Px(10.0),
+ ..default()
+ },
+ ))
+ .with_children(|children| {
+ children.spawn((TextSpan::new("(2) Swept CCD: "), font.clone()));
+ children.spawn((
+ TextSpan::new("Non-linear (considers both translation and rotation)"),
+ font,
+ SweptCcdModeText,
+ ));
+ });
}
/// Spawns balls moving at the spinning objects at high speeds.
@@ -131,17 +132,13 @@ fn spawn_balls(
let circle = Circle::new(2.0);
// Compute the shooting direction.
- let (sin, cos) =
- (0.5 * time.elapsed_seconds_f64().adjust_precision().sin() - PI / 2.0).sin_cos();
+ let (sin, cos) = (0.5 * time.elapsed_secs_f64().adjust_precision().sin() - PI / 2.0).sin_cos();
let direction = Vector::new(cos, sin).rotate(Vector::X);
commands.spawn((
- MaterialMesh2dBundle {
- mesh: meshes.add(circle).into(),
- transform: Transform::from_xyz(0.0, 350.0, 0.0),
- material: materials.add(Color::srgb(0.2, 0.7, 0.9)),
- ..default()
- },
+ Mesh2d(meshes.add(circle)),
+ MeshMaterial2d(materials.add(Color::srgb(0.2, 0.7, 0.9))),
+ Transform::from_xyz(0.0, 350.0, 0.0),
RigidBody::Dynamic,
LinearVelocity(2000.0 * direction),
Friction::ZERO.with_combine_rule(CoefficientCombine::Min),
@@ -150,11 +147,20 @@ fn spawn_balls(
}
fn update_config(
- mut speculative_collision_text: Query<
- &mut Text,
- (With, Without),
+ speculative_collision_text: Single<
+ &mut TextSpan,
+ (
+ With,
+ Without,
+ ),
+ >,
+ swept_ccd_mode_text: Single<
+ &mut TextSpan,
+ (
+ With,
+ Without,
+ ),
>,
- mut swept_ccd_text: Query<&mut Text, (Without, With)>,
keys: Res>,
mut narrow_phase_config: ResMut,
mut ccd_bodies: Query<&mut SweptCcd>,
@@ -163,36 +169,35 @@ fn update_config(
// Note: This sets the default speculative margin, but it can be overridden
// for individual entities with the `SpeculativeMargin` component.
if keys.just_pressed(KeyCode::Digit1) {
- let mut text = speculative_collision_text.single_mut();
+ let mut text = speculative_collision_text;
if narrow_phase_config.default_speculative_margin == Scalar::MAX {
narrow_phase_config.default_speculative_margin = 0.0;
- text.sections[1].value = "Off".to_string();
+ text.0 = "Off".to_string();
} else {
narrow_phase_config.default_speculative_margin = Scalar::MAX;
- text.sections[1].value = "On".to_string();
+ text.0 = "On".to_string();
}
}
// Change the sweep mode and whether swept CCD is enabled.
if keys.just_pressed(KeyCode::Digit2) {
- let mut text = swept_ccd_text.single_mut();
+ let mut text = swept_ccd_mode_text;
for mut swept_ccd in &mut ccd_bodies {
if swept_ccd.mode == SweepMode::NonLinear && swept_ccd.include_dynamic {
swept_ccd.mode = SweepMode::Linear;
- text.sections[1].value = "Linear (considers only translation)".to_string();
+ text.0 = "Linear (considers only translation)".to_string();
} else if swept_ccd.mode == SweepMode::Linear {
// Disable swept CCD for collisions against dynamic bodies.
// To disable it completely, you should remove the component.
swept_ccd.include_dynamic = false;
swept_ccd.mode = SweepMode::NonLinear;
- text.sections[1].value = "Off".to_string();
+ text.0 = "Off".to_string();
} else {
// Enable swept CCD again.
swept_ccd.include_dynamic = true;
- text.sections[1].value =
- "Non-linear (considers both translation and rotation)".to_string();
+ text.0 = "Non-linear (considers both translation and rotation)".to_string();
}
}
}
diff --git a/crates/avian2d/examples/chain_2d.rs b/crates/avian2d/examples/chain_2d.rs
index 1da95f35..a9c5c9b8 100644
--- a/crates/avian2d/examples/chain_2d.rs
+++ b/crates/avian2d/examples/chain_2d.rs
@@ -1,11 +1,7 @@
#![allow(clippy::unnecessary_cast)]
use avian2d::{math::*, prelude::*};
-use bevy::{
- prelude::*,
- sprite::{MaterialMesh2dBundle, Mesh2dHandle},
- window::PrimaryWindow,
-};
+use bevy::{prelude::*, window::PrimaryWindow};
use examples_common_2d::ExampleCommonPlugin;
fn main() {
@@ -31,11 +27,11 @@ fn setup(
mut materials: ResMut>,
mut meshes: ResMut>,
) {
- commands.spawn(Camera2dBundle::default());
+ commands.spawn(Camera2d);
let particle_count = 100;
let particle_radius = 1.2;
- let particle_mesh: Mesh2dHandle = meshes.add(Circle::new(particle_radius as f32)).into();
+ let particle_mesh = meshes.add(Circle::new(particle_radius as f32));
let particle_material = materials.add(Color::srgb(0.2, 0.7, 0.9));
// Spawn kinematic particle that can follow the mouse
@@ -43,11 +39,8 @@ fn setup(
.spawn((
RigidBody::Kinematic,
FollowMouse,
- MaterialMesh2dBundle {
- mesh: particle_mesh.clone(),
- material: particle_material.clone(),
- ..default()
- },
+ Mesh2d(particle_mesh.clone()),
+ MeshMaterial2d(particle_material.clone()),
))
.id();
@@ -56,17 +49,10 @@ fn setup(
let current_particle = commands
.spawn((
RigidBody::Dynamic,
- MassPropertiesBundle::new_computed(&Collider::circle(particle_radius), 1.0),
- MaterialMesh2dBundle {
- mesh: particle_mesh.clone(),
- material: particle_material.clone(),
- transform: Transform::from_xyz(
- 0.0,
- -i as f32 * (particle_radius as f32 * 2.0 + 1.0),
- 0.0,
- ),
- ..default()
- },
+ MassPropertiesBundle::from_shape(&Circle::new(particle_radius as f32), 1.0),
+ Mesh2d(particle_mesh.clone()),
+ MeshMaterial2d(particle_material.clone()),
+ Transform::from_xyz(0.0, -i as f32 * (particle_radius as f32 * 2.0 + 1.0), 0.0),
))
.id();
@@ -93,7 +79,7 @@ fn follow_mouse(
if let Some(cursor_world_pos) = window
.cursor_position()
- .and_then(|cursor| camera.viewport_to_world_2d(camera_transform, cursor))
+ .and_then(|cursor| camera.viewport_to_world_2d(camera_transform, cursor).ok())
{
follower_position.translation =
cursor_world_pos.extend(follower_position.translation.z);
diff --git a/crates/avian2d/examples/collision_layers.rs b/crates/avian2d/examples/collision_layers.rs
index e23fc824..83f723f3 100644
--- a/crates/avian2d/examples/collision_layers.rs
+++ b/crates/avian2d/examples/collision_layers.rs
@@ -1,7 +1,7 @@
#![allow(clippy::unnecessary_cast)]
use avian2d::{math::*, prelude::*};
-use bevy::{prelude::*, sprite::MaterialMesh2dBundle};
+use bevy::prelude::*;
use examples_common_2d::ExampleCommonPlugin;
fn main() {
@@ -33,19 +33,16 @@ fn setup(
mut materials: ResMut>,
mut meshes: ResMut>,
) {
- commands.spawn(Camera2dBundle::default());
+ commands.spawn(Camera2d);
// Spawn blue platform that belongs on the blue layer and collides with blue
commands.spawn((
- SpriteBundle {
- sprite: Sprite {
- color: Color::srgb(0.2, 0.7, 0.9),
- custom_size: Some(Vec2::new(500.0, 25.0)),
- ..default()
- },
- transform: Transform::from_xyz(0.0, -50.0, 0.0),
+ Sprite {
+ color: Color::srgb(0.2, 0.7, 0.9),
+ custom_size: Some(Vec2::new(500.0, 25.0)),
..default()
},
+ Transform::from_xyz(0.0, -50.0, 0.0),
RigidBody::Static,
Collider::rectangle(500.0, 25.0),
CollisionLayers::new([Layer::Blue], [Layer::Blue]),
@@ -53,15 +50,12 @@ fn setup(
// Spawn red platform that belongs on the red layer and collides with red
commands.spawn((
- SpriteBundle {
- sprite: Sprite {
- color: Color::srgb(0.9, 0.3, 0.3),
- custom_size: Some(Vec2::new(500.0, 25.0)),
- ..default()
- },
- transform: Transform::from_xyz(0.0, -200.0, 0.0),
+ Sprite {
+ color: Color::srgb(0.9, 0.3, 0.3),
+ custom_size: Some(Vec2::new(500.0, 25.0)),
..default()
},
+ Transform::from_xyz(0.0, -200.0, 0.0),
RigidBody::Static,
Collider::rectangle(500.0, 25.0),
CollisionLayers::new([Layer::Red], [Layer::Red]),
@@ -75,16 +69,13 @@ fn setup(
for x in -6..6 {
for y in 0..4 {
commands.spawn((
- MaterialMesh2dBundle {
- mesh: marble_mesh.clone().into(),
- material: blue_material.clone(),
- transform: Transform::from_xyz(
- x as f32 * 2.5 * marble_radius,
- y as f32 * 2.5 * marble_radius + 200.0,
- 0.0,
- ),
- ..default()
- },
+ Mesh2d(marble_mesh.clone()),
+ MeshMaterial2d(blue_material.clone()),
+ Transform::from_xyz(
+ x as f32 * 2.5 * marble_radius,
+ y as f32 * 2.5 * marble_radius + 200.0,
+ 0.0,
+ ),
RigidBody::Dynamic,
Collider::circle(marble_radius as Scalar),
CollisionLayers::new([Layer::Blue], [Layer::Blue]),
@@ -97,16 +88,13 @@ fn setup(
for x in -6..6 {
for y in -4..0 {
commands.spawn((
- MaterialMesh2dBundle {
- mesh: marble_mesh.clone().into(),
- material: red_material.clone(),
- transform: Transform::from_xyz(
- x as f32 * 2.5 * marble_radius,
- y as f32 * 2.5 * marble_radius + 200.0,
- 0.0,
- ),
- ..default()
- },
+ Mesh2d(marble_mesh.clone()),
+ MeshMaterial2d(red_material.clone()),
+ Transform::from_xyz(
+ x as f32 * 2.5 * marble_radius,
+ y as f32 * 2.5 * marble_radius + 200.0,
+ 0.0,
+ ),
RigidBody::Dynamic,
Collider::circle(marble_radius as Scalar),
CollisionLayers::new([Layer::Red], [Layer::Red]),
diff --git a/crates/avian2d/examples/custom_collider.rs b/crates/avian2d/examples/custom_collider.rs
index 9e063813..a8a89539 100644
--- a/crates/avian2d/examples/custom_collider.rs
+++ b/crates/avian2d/examples/custom_collider.rs
@@ -1,7 +1,9 @@
//! An example demonstrating how to make a custom collider and use it for collision detection.
+#![allow(clippy::unnecessary_cast)]
+
use avian2d::{math::*, prelude::*};
-use bevy::{prelude::*, sprite::MaterialMesh2dBundle};
+use bevy::prelude::*;
use examples_common_2d::ExampleCommonPlugin;
fn main() {
@@ -55,23 +57,8 @@ impl AnyCollider for CircleCollider {
ColliderAabb::new(position, Vector::splat(self.radius))
}
- fn mass_properties(&self, density: Scalar) -> ColliderMassProperties {
- // In 2D, the Z length is assumed to be 1.0, so volume = area
- let volume = PI * self.radius.powi(2);
- let mass = density * volume;
- let inertia = mass * self.radius.powi(2) / 2.0;
-
- ColliderMassProperties {
- mass: Mass(mass),
- inverse_mass: InverseMass(mass.recip()),
- inertia: Inertia(inertia),
- inverse_inertia: InverseInertia(inertia.recip()),
- center_of_mass: CenterOfMass::default(),
- }
- }
-
// This is the actual collision detection part.
- // It compute all contacts between two colliders at the given positions.
+ // It computes all contacts between two colliders at the given positions.
fn contact_manifolds(
&self,
other: &Self,
@@ -124,6 +111,25 @@ impl AnyCollider for CircleCollider {
}
}
+// Implement mass computation for the collider shape.
+// This is needed for physics to behave correctly.
+impl ComputeMassProperties2d for CircleCollider {
+ fn mass(&self, density: f32) -> f32 {
+ // In 2D, the Z length is assumed to be `1.0`, so volume == area.
+ let volume = std::f32::consts::PI * self.radius.powi(2) as f32;
+ density * volume
+ }
+
+ fn unit_angular_inertia(&self) -> f32 {
+ // Angular inertia for a circle, assuming a mass of `1.0`.
+ self.radius.powi(2) as f32 / 2.0
+ }
+
+ fn center_of_mass(&self) -> Vec2 {
+ Vec2::ZERO
+ }
+}
+
// Note: This circle collider only supports uniform scaling.
impl ScalableCollider for CircleCollider {
fn scale(&self) -> Vector {
@@ -146,7 +152,7 @@ fn setup(
mut materials: ResMut>,
mut meshes: ResMut>,
) {
- commands.spawn(Camera2dBundle::default());
+ commands.spawn(Camera2d);
let center_radius = 200.0;
let particle_radius = 5.0;
@@ -158,11 +164,8 @@ fn setup(
// Spawn rotating body at the center.
commands
.spawn((
- MaterialMesh2dBundle {
- mesh: meshes.add(Circle::new(center_radius)).into(),
- material: materials.add(Color::srgb(0.7, 0.7, 0.8)).clone(),
- ..default()
- },
+ Mesh2d(meshes.add(Circle::new(center_radius))),
+ MeshMaterial2d(materials.add(Color::srgb(0.7, 0.7, 0.8)).clone()),
RigidBody::Kinematic,
CircleCollider::new(center_radius.adjust_precision()),
CenterBody,
@@ -174,12 +177,9 @@ fn setup(
for i in 0..count {
let pos = Quat::from_rotation_z(i as f32 * angle_step) * Vec3::Y * center_radius;
c.spawn((
- MaterialMesh2dBundle {
- mesh: particle_mesh.clone().into(),
- material: red.clone(),
- transform: Transform::from_translation(pos).with_scale(Vec3::ONE * 5.0),
- ..default()
- },
+ Mesh2d(particle_mesh.clone()),
+ MeshMaterial2d(red.clone()),
+ Transform::from_translation(pos).with_scale(Vec3::ONE * 5.0),
CircleCollider::new(particle_radius.adjust_precision()),
));
}
@@ -192,16 +192,13 @@ fn setup(
for x in -x_count / 2..x_count / 2 {
for y in -y_count / 2..y_count / 2 {
commands.spawn((
- MaterialMesh2dBundle {
- mesh: particle_mesh.clone().into(),
- material: blue.clone(),
- transform: Transform::from_xyz(
- x as f32 * 3.0 * particle_radius - 350.0,
- y as f32 * 3.0 * particle_radius,
- 0.0,
- ),
- ..default()
- },
+ Mesh2d(particle_mesh.clone()),
+ MeshMaterial2d(blue.clone()),
+ Transform::from_xyz(
+ x as f32 * 3.0 * particle_radius - 350.0,
+ y as f32 * 3.0 * particle_radius,
+ 0.0,
+ ),
RigidBody::Dynamic,
CircleCollider::new(particle_radius.adjust_precision()),
LinearDamping(0.4),
@@ -215,7 +212,7 @@ fn center_gravity(
mut particles: Query<(&Transform, &mut LinearVelocity), Without>,
time: Res