Chapther 4-5 ish... ⚛️ particle system with an ounce of physics project ⚛️
The particle simulation runs on bevy, a relativly new rust game engine. The example folder contains the different stages of completetion. The src folder handles the physics behind the particles when they are spawned in. I won't go into detail how the physics behind it works only that it's based on XPBD simulation. But if your interested you can look for yourself inside the src folder.
simple.rs
- Simplest example of how bevy operates.particle_collisions.rs
- checks that the collision physics is operating correctly.different_masses.rs
- checks if the physics behind particles with the different masses works arcordingly.marble_pour.rs
- A simple testing ground for particle system.ball_stacking.rs
- Work in Progress...
To run the testing ground or any other example write cargo run --example marble_pour
. Don't add the .rs at the end!
components.rs
- Contain structs that act as components for the particles.entity.rs
- Cointain the struct ParticleBundle as well as static object bundles.lib.rs
- Contains the calculation functions (updating position and velocity) and building the the physics plugin.resources.rs
- Contains the structs involved in collision and the gravity struct.
To create a new program you need to create a app. to develop it you'll need to add resources, plugins that handles the underlying functions of your program. add_systems handles all functions that control what happens on the frontend for example, spawning in marbles and despawning them. I have also added a startup system which gets added before all other systems using .add_startup system. Dont forget to add the .run()
in the end for the app to run!
XPBDPlugin::default()
- contains the physics behind the particles movement.startup
- Startup function.spawn_marbles
- Function that handles the spawning of particles.despawn_marbles
anddespawn_marbles_at_height
- handles how the particles despawn, either from user input or height. Exists to keep performance high.
fn main() {
App::new()
.insert_resource(ClearColor(Color::rgb(0.8, 0.8, 0.9)))
.insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins)
.add_plugin(XPBDPlugin::default())
.add_plugin(bevy_editor_pls::EditorPlugin)
.add_startup_system(startup)
.add_system_set(
SystemSet::new()
.with_run_criteria(FixedTimestep::step(1. / 20.))
.with_system(spawn_marbles),
)
.add_system(despawn_marbles)
.add_system(despawn_marbles_at_height)
.run();
}
To create particles we need to create materials and meshes for them. We do that by using structs.
//Simple struct to handle the particles material
#[derive(Resource)]
struct Materials {
blue: Handle<StandardMaterial>,
}
//Simple struct to handle the particle meshes
#[derive(Resource)]
struct Meshes {
sphere: Handle<Mesh>,
}
We then need to create the meshes and materials as you see down below. For the app to recognise the meshes and material we need to insert them into the apps commands.
//Creates a resource for the circle meshes
commands.insert_resource(Meshes {
sphere: meshes.add(Mesh::from(shape::Icosphere {
radius: 1., //Radius of the circle
subdivisions: 4, //Devides the circle into n areas. More subdivisions mean rounder circle
})),
});
//Creates a resource for the color meshes
commands.insert_resource(Materials {
blue: materials.add(StandardMaterial {
base_color: Color::rgb(0.4, 0.4, 0.6), //Materials color
unlit: true, //Bool value that checks whether to apply only the base color to this material.
..Default::default()
}),
});
To see what happens on the screen when we run the program we need a camera. We'll use this simple camera3dBundle that can be found inm bevys cheatcode Book.
After creating the bundle we need to spawn it in using commands.spawn()
.
//Creates a 3d camera and adds it to the scene
commands.spawn(Camera3dBundle {
transform: Transform::from_translation(Vec3::new(0., 0., 100.)), //Where to position the camera
projection: bevy::prelude::Projection::Orthographic(OrthographicProjection {
scale: 0.01,
..Default::default()
}),
..Camera3dBundle::default()
});
To check whether the particles can collide with other surfaces we need to spawn in other objects such as a rectangle. The code below shows a simple way to do just that.
//Example of how to spawn a rectangle into the scene
let size = Vec2::new(10., 2.);
commands
.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Quad::new(Vec2::ONE))),
material: blue.clone(),
transform: Transform::from_scale(size.extend(1.)),
..Default::default()
})
.insert(StaticBoxBundle {
pos: Pos(Vec2::new(0., -3.)),
collider: BoxCollider { size },
..Default::default()
});
Looks similar to the rectangle bundle with a few changes to handle the position and velocity of the particles when spawned in. ParticleBundle contains all variables need for the particle physics (Code inside of src/entity.rs).
//Example of how to spawn in a particle
commands
.spawn(PbrBundle {
mesh: meshes.sphere.clone(),
material: materials.blue.clone(),
transform: Transform {
scale: Vec3::splat(radius),
translation: pos.extend(0.),
..Default::default()
},
..Default::default()
})
.insert(ParticleBundle {
collider: CircleCollider { radius },
..ParticleBundle::new_with_pos_and_vel(pos, vel)
});