Compare commits
6 Commits
96583f4cea
...
50c802fdab
| Author | SHA1 | Date |
|---|---|---|
|
|
50c802fdab | 1 month ago |
|
|
882aac6e6e | 1 month ago |
|
|
acbf89a077 | 1 month ago |
|
|
c3cd7f87b6 | 1 month ago |
|
|
602d6a923e | 1 month ago |
|
|
1e2d37ed60 | 1 month ago |
File diff suppressed because it is too large
Load Diff
@ -1,222 +0,0 @@
|
||||
use bevy::{
|
||||
color::palettes::css::{BLUE, GREEN},
|
||||
render::mesh::{SphereKind, SphereMeshBuilder},
|
||||
};
|
||||
use bevy_rapier3d::rapier::prelude::CollisionEventFlags;
|
||||
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() {
|
||||
App::new()
|
||||
.add_plugins(BaseGamePlugin::default())
|
||||
.init_resource::<InsideArea>()
|
||||
.add_systems(
|
||||
Startup,
|
||||
(
|
||||
setup_physics_scene,
|
||||
setup_ui,
|
||||
position_camera.after(create_camera_3d),
|
||||
),
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
control_ball,
|
||||
process_events,
|
||||
sync_resource_to_ui::<InsideArea>.run_if(resource_changed::<InsideArea>),
|
||||
)
|
||||
.run_if(in_state(LoadingState::Idle)),
|
||||
)
|
||||
.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(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
// Create the ground.
|
||||
// Make this a physical looking object
|
||||
commands.spawn((
|
||||
Collider::cuboid(100.0, 0.1, 100.0),
|
||||
Transform::from_xyz(0.0, -1.0, 0.0),
|
||||
Pickable::IGNORE,
|
||||
Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(50.0)))),
|
||||
MeshMaterial3d(materials.add(StandardMaterial {
|
||||
base_color: RED.into(),
|
||||
..Default::default()
|
||||
})),
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
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),
|
||||
));
|
||||
|
||||
// Create the bouncing ball.
|
||||
commands.spawn((
|
||||
RigidBody::Dynamic,
|
||||
GravityScale(1.0),
|
||||
Collider::ball(0.5),
|
||||
ColliderMassProperties::Mass(2.0),
|
||||
Restitution::coefficient(0.7),
|
||||
ExternalImpulse::default(),
|
||||
Transform::from_xyz(0.0, 4.0, 0.0),
|
||||
ActiveEvents::all(),
|
||||
Name::new("Ball"),
|
||||
Mesh3d(meshes.add(SphereMeshBuilder::new(
|
||||
0.5,
|
||||
SphereKind::Uv {
|
||||
sectors: 10,
|
||||
stacks: 10,
|
||||
},
|
||||
))),
|
||||
MeshMaterial3d(materials.add(StandardMaterial {
|
||||
base_color: WHITE.into(),
|
||||
..Default::default()
|
||||
})),
|
||||
));
|
||||
|
||||
// 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))),
|
||||
Name::new("Green Area"),
|
||||
MeshMaterial3d(materials.add(StandardMaterial {
|
||||
base_color: GREEN.with_alpha(0.5).into(),
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
..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))),
|
||||
Name::new("Blue Area"),
|
||||
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
|
||||
fn position_camera(mut query: Query<&mut Transform, (With<Camera>, With<Camera3d>)>) {
|
||||
query.iter_mut().for_each(|mut t| {
|
||||
*t = Transform::from_xyz(10.0, 10.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y);
|
||||
})
|
||||
}
|
||||
|
||||
/// Control the ball based on arrow keys
|
||||
fn control_ball(keys: Res<ButtonInput<KeyCode>>, mut ball: Single<&mut ExternalImpulse>) {
|
||||
if keys.pressed(KeyCode::ArrowUp) {
|
||||
ball.torque_impulse = Vec3::new(0.0, 0.0, 0.1);
|
||||
}
|
||||
|
||||
if keys.pressed(KeyCode::ArrowDown) {
|
||||
ball.torque_impulse = Vec3::new(0.0, 0.0, -0.1);
|
||||
}
|
||||
|
||||
if keys.pressed(KeyCode::ArrowLeft) {
|
||||
ball.torque_impulse = Vec3::new(0.1, 0.0, 0.0);
|
||||
}
|
||||
|
||||
if keys.pressed(KeyCode::ArrowRight) {
|
||||
ball.torque_impulse = Vec3::new(-0.1, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps CollisionEvent to a game-specific construct (did X enter/exit an area)
|
||||
#[derive(Debug)]
|
||||
enum WithinBounds {
|
||||
Enter(Entity, Entity),
|
||||
Exit(Entity, Entity),
|
||||
}
|
||||
|
||||
/// Resource tracking what box the sphere is in
|
||||
#[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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read collision events and procss them into resource updates to be displayed to the user
|
||||
fn process_events(
|
||||
mut events: EventReader<CollisionEvent>,
|
||||
areas: Query<&AreaMarker>,
|
||||
mut inside: ResMut<InsideArea>,
|
||||
) {
|
||||
events
|
||||
.read()
|
||||
.filter_map(|collision_event| match collision_event {
|
||||
CollisionEvent::Started(a, b, flags) => {
|
||||
(*flags == CollisionEventFlags::SENSOR).then_some(WithinBounds::Enter(*a, *b))
|
||||
}
|
||||
CollisionEvent::Stopped(a, b, flags) => {
|
||||
(*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;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue