Basic area intersection done(?)

main
Elijah Voigt 4 months ago
parent 2d5ab5ea31
commit f12c5358a1

@ -1,29 +1,59 @@
use bevy::{color::palettes::css::{BLUE, GREEN}, render::mesh::{SphereKind, SphereMeshBuilder}}; use bevy::{
color::palettes::css::{BLUE, GREEN},
render::mesh::{SphereKind, SphereMeshBuilder},
};
use bevy_rapier3d::rapier::prelude::CollisionEventFlags; use bevy_rapier3d::rapier::prelude::CollisionEventFlags;
use games::*; use games::*;
/// Example showing using Rapier3d to do area intersection
///
/// Have you ever wanted to detect if a character is within some bounds?
/// This shows how to do that.
fn main() { fn main() {
App::new() App::new()
.add_plugins(BaseGamePlugin) .add_plugins(BaseGamePlugin)
.add_systems(Startup, ( .init_resource::<InsideArea>()
.add_systems(
Startup,
(
setup_physics_scene, setup_physics_scene,
position_camera.after(setup_camera) setup_ui,
).chain()) position_camera.after(setup_camera),
.add_systems(Update, (control_ball, read_events)) ),
)
.add_systems(
Update,
(
control_ball,
process_events,
sync_resource_to_ui::<InsideArea>.run_if(resource_changed::<InsideArea>),
),
)
.run(); .run();
} }
/// Which area the sphere is in/touching
#[derive(Component, Debug, Clone)]
enum AreaMarker {
Green,
Blue,
}
/// Setup a basic scene with:
/// * Floor
/// * A light for dramatic shadows
/// * A sphere that can move
/// * Two cubes that designate area intersection
fn setup_physics_scene( fn setup_physics_scene(
mut commands: Commands, mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>> mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// Create the ground. // Create the ground.
// Make this a physical looking object // Make this a physical looking object
commands commands.spawn((
.spawn((
Collider::cuboid(100.0, 0.1, 100.0), Collider::cuboid(100.0, 0.1, 100.0),
Transform::from_xyz(0.0, -2.0, 0.0), Transform::from_xyz(0.0, -1.0, 0.0),
Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(50.0)))), Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(50.0)))),
MeshMaterial3d(materials.add(StandardMaterial { MeshMaterial3d(materials.add(StandardMaterial {
base_color: RED.into(), base_color: RED.into(),
@ -32,13 +62,17 @@ fn setup_physics_scene(
)); ));
commands.spawn(( commands.spawn((
SpotLight { color: WHITE.into(), intensity: 10_000_000.0, shadows_enabled: true, ..default() }, SpotLight {
color: WHITE.into(),
intensity: 10_000_000.0,
shadows_enabled: true,
..default()
},
Transform::from_xyz(0.0, 10.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y), Transform::from_xyz(0.0, 10.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
)); ));
// Create the bouncing ball. // Create the bouncing ball.
commands commands.spawn((
.spawn((
RigidBody::Dynamic, RigidBody::Dynamic,
GravityScale(1.0), GravityScale(1.0),
Collider::ball(0.5), Collider::ball(0.5),
@ -46,7 +80,6 @@ fn setup_physics_scene(
Restitution::coefficient(0.7), Restitution::coefficient(0.7),
ExternalImpulse::default(), ExternalImpulse::default(),
Transform::from_xyz(0.0, 4.0, 0.0), Transform::from_xyz(0.0, 4.0, 0.0),
Marker,
ActiveEvents::all(), ActiveEvents::all(),
Mesh3d(meshes.add(SphereMeshBuilder::new( Mesh3d(meshes.add(SphereMeshBuilder::new(
0.5, 0.5,
@ -56,43 +89,63 @@ fn setup_physics_scene(
}, },
))), ))),
MeshMaterial3d(materials.add(StandardMaterial { MeshMaterial3d(materials.add(StandardMaterial {
base_color: BLUE.into(), base_color: WHITE.into(),
..Default::default() ..Default::default()
})), })),
)); ));
// Create a box that the ball can pass into/through // Create a box that the ball can pass into/through
// TODO: Transparent box to visualize area commands.spawn((
commands
.spawn((
Sensor, Sensor,
Marker,
Collider::cuboid(1.0, 1.0, 1.0), Collider::cuboid(1.0, 1.0, 1.0),
Transform::from_xyz(0.0, 0.0, 0.0), Transform::from_xyz(0.0, 0.0, 2.0),
Mesh3d(meshes.add(Cuboid::new(2.0, 2.0, 2.0))), Mesh3d(meshes.add(Cuboid::new(2.0, 2.0, 2.0))),
MeshMaterial3d(materials.add(StandardMaterial { MeshMaterial3d(materials.add(StandardMaterial {
base_color: GREEN.with_alpha(0.5).into(), base_color: GREEN.with_alpha(0.5).into(),
alpha_mode: AlphaMode::Blend, alpha_mode: AlphaMode::Blend,
..Default::default() ..Default::default()
})), })),
AreaMarker::Green,
));
// Create a box that the ball can pass into/through
commands.spawn((
Sensor,
Collider::cuboid(1.0, 1.0, 1.0),
Transform::from_xyz(0.0, 0.0, -2.0),
Mesh3d(meshes.add(Cuboid::new(2.0, 2.0, 2.0))),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: BLUE.with_alpha(0.5).into(),
alpha_mode: AlphaMode::Blend,
..Default::default()
})),
AreaMarker::Blue,
));
}
/// Setup the UI for this example that indicates if the game "sees" the sphere
/// is inside the cube
fn setup_ui(mut commands: Commands) {
commands.spawn((
Node {
align_self: AlignSelf::Start,
justify_self: JustifySelf::Center,
..default()
},
Text("Placeholder".into()),
SyncResource::<InsideArea>::default(),
)); ));
} }
/// Position the world camera to get a better view /// Position the world camera to get a better view
fn position_camera( fn position_camera(mut query: Query<&mut Transform, (With<Camera>, With<Camera3d>)>) {
mut query: Query<(Entity, &mut Transform), (With<Camera>, With<Camera3d>)>, query.iter_mut().for_each(|mut t| {
mut commands: Commands, *t = Transform::from_xyz(10.0, 10.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y);
) {
query.iter_mut().for_each(|(e, mut t)| {
*t = Transform::from_xyz(10.0, 10.0, 10.0)
.looking_at(Vec3::ZERO, Vec3::Y);
}) })
} }
fn control_ball( /// Control the ball based on arrow keys
keys: Res<ButtonInput<KeyCode>>, fn control_ball(keys: Res<ButtonInput<KeyCode>>, mut ball: Single<&mut ExternalImpulse>) {
mut ball: Single<&mut ExternalImpulse>,
) {
if keys.pressed(KeyCode::ArrowUp) { if keys.pressed(KeyCode::ArrowUp) {
ball.torque_impulse = Vec3::new(0.0, 0.0, 0.1); ball.torque_impulse = Vec3::new(0.0, 0.0, 0.1);
} }
@ -110,29 +163,57 @@ fn control_ball(
} }
} }
/// Maps CollisionEvent to a game-specific construct (did X enter/exit an area)
#[derive(Debug)] #[derive(Debug)]
enum WithinBounds { enum WithinBounds {
Enter(Entity, Entity), Enter(Entity, Entity),
Exit(Entity, Entity), Exit(Entity, Entity),
} }
#[derive(Component, Debug)] /// Resource tracking what box the sphere is in
struct Marker; #[derive(Resource, Debug, Default, Clone)]
struct InsideArea(Option<AreaMarker>);
impl Display for InsideArea {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.0 {
Some(AreaMarker::Blue) => write!(f, "Ball IS touching the BLUE box"),
Some(AreaMarker::Green) => write!(f, "Ball IS touching the GREEN box"),
None => write!(f, "Ball is NOT touching ANY box"),
}
}
}
fn read_events( /// Read collision events and procss them into resource updates to be displayed to the user
fn process_events(
mut events: EventReader<CollisionEvent>, mut events: EventReader<CollisionEvent>,
mut commands: Commands, areas: Query<&AreaMarker>,
mut inside: ResMut<InsideArea>,
) { ) {
events.read().filter_map(|collision_event| { events
match collision_event { .read()
.filter_map(|collision_event| match collision_event {
CollisionEvent::Started(a, b, flags) => { CollisionEvent::Started(a, b, flags) => {
(*flags == CollisionEventFlags::SENSOR).then_some(WithinBounds::Enter(*a, *b)) (*flags == CollisionEventFlags::SENSOR).then_some(WithinBounds::Enter(*a, *b))
}, }
CollisionEvent::Stopped(a, b, flags) => { CollisionEvent::Stopped(a, b, flags) => {
(*flags == CollisionEventFlags::SENSOR).then_some(WithinBounds::Exit(*a, *b)) (*flags == CollisionEventFlags::SENSOR).then_some(WithinBounds::Exit(*a, *b))
} }
})
.for_each(|within_bounds| {
match within_bounds {
WithinBounds::Enter(a, b) => {
if let Ok(x) = areas.get(a) {
inside.0 = Some(x.clone());
} else if let Ok(x) = areas.get(b) {
inside.0 = Some(x.clone());
}
}
WithinBounds::Exit(a, b) => {
if areas.contains(a) || areas.contains(b) {
inside.0 = None;
}
}
} }
}).for_each(|within_bounds| {
info!("Event: {:?}", within_bounds);
}) })
} }

@ -1,4 +1,5 @@
#![allow(ambiguous_glob_reexports)] #![allow(ambiguous_glob_reexports)]
#![allow(clippy::type_complexity)]
mod base_game; mod base_game;
mod debug; mod debug;

Loading…
Cancel
Save