bevy/examples/ecs/iter_combinations.rs
Paweł Grabarz 07ed1d053e Implement and require #[derive(Component)] on all component structs (#2254)
This implements the most minimal variant of #1843 - a derive for marker trait. This is a prerequisite to more complicated features like statically defined storage type or opt-out component reflection.

In order to make component struct's purpose explicit and avoid misuse, it must be annotated with `#[derive(Component)]` (manual impl is discouraged for compatibility). Right now this is just a marker trait, but in the future it might be expanded. Making this change early allows us to make further changes later without breaking backward compatibility for derive macro users.

This already prevents a lot of issues, like using bundles in `insert` calls. Primitive types are no longer valid components as well. This can be easily worked around by adding newtype wrappers and deriving `Component` for them.

One funny example of prevented bad code (from our own tests) is when an newtype struct or enum variant is used. Previously, it was possible to write `insert(Newtype)` instead of `insert(Newtype(value))`. That code compiled, because function pointers (in this case newtype struct constructor) implement `Send + Sync + 'static`, so we allowed them to be used as components. This is no longer the case and such invalid code will trigger a compile error.


Co-authored-by: = <=>
Co-authored-by: TheRawMeatball <therawmeatball@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-10-03 19:23:44 +00:00

158 lines
4.9 KiB
Rust

use bevy::{core::FixedTimestep, prelude::*};
use rand::{thread_rng, Rng};
#[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)]
struct FixedUpdateStage;
const DELTA_TIME: f64 = 0.01;
fn main() {
App::new()
.insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins)
.add_startup_system(generate_bodies)
.add_stage_after(
CoreStage::Update,
FixedUpdateStage,
SystemStage::parallel()
.with_run_criteria(FixedTimestep::step(DELTA_TIME))
.with_system(interact_bodies)
.with_system(integrate),
)
.run();
}
const GRAVITY_CONSTANT: f32 = 0.001;
const SOFTENING: f32 = 0.01;
const NUM_BODIES: usize = 100;
#[derive(Component, Default)]
struct Mass(f32);
#[derive(Component, Default)]
struct Acceleration(Vec3);
#[derive(Component, Default)]
struct LastPos(Vec3);
#[derive(Bundle, Default)]
struct BodyBundle {
#[bundle]
pbr: PbrBundle,
mass: Mass,
last_pos: LastPos,
acceleration: Acceleration,
}
fn generate_bodies(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let mesh = meshes.add(Mesh::from(shape::Icosphere {
radius: 1.0,
subdivisions: 3,
}));
let pos_range = 1.0..15.0;
let color_range = 0.5..1.0;
let vel_range = -0.5..0.5;
let mut rng = thread_rng();
for _ in 0..NUM_BODIES {
let mass_value_cube_root: f32 = rng.gen_range(0.5..4.0);
let mass_value: f32 = mass_value_cube_root * mass_value_cube_root * mass_value_cube_root;
let position = Vec3::new(
rng.gen_range(-1.0..1.0),
rng.gen_range(-1.0..1.0),
rng.gen_range(-1.0..1.0),
)
.normalize()
* rng.gen_range(pos_range.clone());
commands.spawn_bundle(BodyBundle {
pbr: PbrBundle {
transform: Transform {
translation: position,
scale: Vec3::splat(mass_value_cube_root * 0.1),
..Default::default()
},
mesh: mesh.clone(),
material: materials.add(
Color::rgb_linear(
rng.gen_range(color_range.clone()),
rng.gen_range(color_range.clone()),
rng.gen_range(color_range.clone()),
)
.into(),
),
..Default::default()
},
mass: Mass(mass_value),
acceleration: Acceleration(Vec3::ZERO),
last_pos: LastPos(
position
- Vec3::new(
rng.gen_range(vel_range.clone()),
rng.gen_range(vel_range.clone()),
rng.gen_range(vel_range.clone()),
) * DELTA_TIME as f32,
),
});
}
// add bigger "star" body in the center
commands
.spawn_bundle(BodyBundle {
pbr: PbrBundle {
transform: Transform {
scale: Vec3::splat(0.5),
..Default::default()
},
mesh: meshes.add(Mesh::from(shape::Icosphere {
radius: 1.0,
subdivisions: 5,
})),
material: materials.add((Color::ORANGE_RED * 10.0).into()),
..Default::default()
},
mass: Mass(1000.0),
..Default::default()
})
.insert(PointLight {
color: Color::ORANGE_RED,
..Default::default()
});
commands.spawn_bundle(PerspectiveCameraBundle {
transform: Transform::from_xyz(0.0, 10.5, -20.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});
}
fn interact_bodies(mut query: Query<(&Mass, &GlobalTransform, &mut Acceleration)>) {
let mut iter = query.iter_combinations_mut();
while let Some([(Mass(m1), transform1, mut acc1), (Mass(m2), transform2, mut acc2)]) =
iter.fetch_next()
{
let delta = transform2.translation - transform1.translation;
let distance_sq: f32 = delta.length_squared();
let f = GRAVITY_CONSTANT / (distance_sq * (distance_sq + SOFTENING).sqrt());
let force_unit_mass = delta * f;
acc1.0 += force_unit_mass * *m2;
acc2.0 -= force_unit_mass * *m1;
}
}
fn integrate(mut query: Query<(&mut Acceleration, &mut Transform, &mut LastPos)>) {
let dt_sq = (DELTA_TIME * DELTA_TIME) as f32;
for (mut acceleration, mut transform, mut last_pos) in query.iter_mut() {
// verlet integration
// x(t+dt) = 2x(t) - x(t-dt) + a(t)dt^2 + O(dt^4)
let new_pos =
transform.translation + transform.translation - last_pos.0 + acceleration.0 * dt_sq;
acceleration.0 = Vec3::ZERO;
last_pos.0 = transform.translation;
transform.translation = new_pos;
}
}