Skip to content

Commit

Permalink
Merge pull request #81 from jreuben11/bevy
Browse files Browse the repository at this point in the history
bevy picking
  • Loading branch information
jreuben11 authored Jun 18, 2024
2 parents 410f482 + 2bf0366 commit 5a654fb
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 1 deletion.
6 changes: 5 additions & 1 deletion bevy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ struct GreetTimer(Timer);
```

# [three_d_shapes](three_d_shapes/src/main.rs)
- from bevy example

# [pong](pong/src/main.rs)
- from tutorial https://taintedcoders.com/bevy/pong-tutorial/
- from tutorial https://taintedcoders.com/bevy/pong-tutorial/

# [picking](picking/src/main.rs)
- from **bevy_mod_picking** crate **event_listener** example https://github.com/aevyrie/bevy_mod_picking/blob/main/examples/event_listener.rs
19 changes: 19 additions & 0 deletions bevy/picking/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "picking"
version = "0.1.0"
edition = "2021"

[dependencies]
bevy = { version = "0.13.2", features = ["dynamic_linking"] }
bevy_egui = "0.27.1"
bevy_mod_picking = { version = "0.19.0", features = ["all"] }

# Enable a small amount of optimization in debug mode
[profile.dev]
opt-level = 1

# Enable high optimizations for dependencies (incl. Bevy), but not for our code:
[profile.dev.package."*"]
opt-level = 3

[workspace]
161 changes: 161 additions & 0 deletions bevy/picking/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//! This example demonstrates how [`On<Event>`] components and event bubbling can be used to
//! propagate events up an entity hierarchy, and run callbacks when an event reaches an entity.
//!
//! The Big Idea here is to make it easy to couple interaction events with specific entities. In
//! other words, it allows you to easily implement "If entity X is hovered/clicked/dragged, do Y".
use bevy::prelude::*;
use bevy_mod_picking::prelude::*;

fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(low_latency_window_plugin()),
DefaultPickingPlugins
.build()
.disable::<DefaultHighlightingPlugin>(),
bevy_egui::EguiPlugin,
))
.insert_resource(DebugPickingMode::Normal)
.add_systems(Startup, setup)
.add_event::<DoSomethingComplex>()
.add_systems(
Update,
receive_greetings.run_if(on_event::<DoSomethingComplex>()),
)
.run();
}

fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands
// When any of this entity's children are interacted with using a pointer, those events will
// propagate up the entity hierarchy until they reach this parent. By referring to the
// `target` entity instead of the `listener` entity, we can do things to specific target
// entities, even though they lack `On<Pointer<Event>>` components.
.spawn((
PbrBundle {
mesh: meshes.add(Cuboid::default()),
material: materials.add(Color::WHITE),
..default()
},
PickableBundle::default(),
// Callbacks are just exclusive bevy systems that have access to an event data via
// `](bevy_eventlistener::prelude::Listener) and [`ListenerMut`]. This gives
// you full freedom to write normal bevy
// systems that are only called when specific entities are interacted with. Here we have
// a system that rotates a cube when it is dragged. See the comments added to the
// function for more details on the requirements of callback systems.
//
// # Performance 💀
//
// Callback systems require exclusive world access, which means the system cannot be run
// in parallel with other systems! Callback systems are very flexible, but should be
// used with care. If you want to do something complex in response to a listened event,
// prefer to instead use `send_event`, and react to your custom event in a
// normally-scheduled bevy system (see send_event usage below).
On::<Pointer<Move>>::run(change_hue_with_vertical_move),
// We can use helper methods to make callbacks even simpler. For drag-to-rotate, we use
// this little closure, because we only need to modify the target entity's Transform:
On::<Pointer<Drag>>::target_component_mut::<Transform>(|drag, transform| {
transform.rotate_local_y(drag.delta.x / 50.0)
}),
// Just like bevy systems, callbacks can be closures! Recall that the parameters can be
// any bevy system parameters, with the only requirement that the first parameter be the
// input event, and the function output is a `Bubble`.
On::<Pointer<Out>>::run(|event: Listener<Pointer<Out>>, time: Res<Time>| {
info!(
"[{:?}]: The pointer left entity {:?}",
time.elapsed_seconds(),
event.target
);
}),
// When you just want to add a `Command` to the target entity,`target_commands_mut` will
// reduce boilerplate and allow you to do this directly.
On::<Pointer<Click>>::target_commands_mut(|click, target_commands| {
if click.target != click.listener() && click.button == PointerButton::Secondary {
target_commands.despawn();
}
}),
// Sometimes you may need to do multiple things in response to an interaction. Events
// can be an easy way to handle this, as you can react to an event across many systems.
// Unlike pointer events, recall that this event is only sent when the event listener
// for this *specific* entity (or its children) are targeted. Similar to `add_command`
// this is simply a helper function that creates an event-sending callback to reduce
// boilerplate.
//
// # Performance 🚀
//
// Unlike the `run` method, this will not prevent systems from parallelizing, as the
// systems that react to this event can be scheduled normally. In fact, you can get the
// best of both worlds by using run criteria on the systems that react to your custom
// event. This allows you to run bevy systems in response to interaction with a specific
// entity, while still allowing full system parallelism.
On::<Pointer<Down>>::send_event::<DoSomethingComplex>(),
))
.with_children(|parent| {
for i in 1..=5 {
parent.spawn((
// As noted above, we are adding children here but we don't need to add an event
// listener. Events on children will bubble up to the parent!
PbrBundle {
mesh: meshes.add(Cuboid::default()),
material: materials.add(Color::RED),
transform: Transform::from_xyz(0.0, 1.0 + 0.5 * i as f32, 0.0)
.with_scale(Vec3::splat(0.4)),
..default()
},
PickableBundle::default(),
));
}
});

commands.spawn(PointLightBundle {
point_light: PointLight {
shadows_enabled: true,
..default()
},
transform: Transform::from_xyz(4.0, 8.0, 4.0),
..default()
});
commands.spawn((Camera3dBundle {
transform: Transform::from_xyz(-2.0, 4.5, 5.0).looking_at(Vec3::Y * 2.0, Vec3::Y),
..default()
},));
}

/// Change the hue of mesh's `StandardMaterial` when the mouse moves vertically over it.
fn change_hue_with_vertical_move(
// The event data accessible by the callback system
event: Listener<Pointer<Move>>,
mut materials: ResMut<Assets<StandardMaterial>>,
cube: Query<&Handle<StandardMaterial>>,
) {
let material = materials.get_mut(cube.get(event.target).unwrap()).unwrap();
let mut color = material.base_color.as_hsla_f32();
let to_u8 = 255.0 / 360.0; // we will use wrapping integer addition to make the hue wrap around
color[0] = ((color[0] * to_u8) as u8).wrapping_add_signed(event.delta.y as i8) as f32 / to_u8;
material.base_color = Color::hsla(color[0], color[1], color[2], color[3]);
}

#[derive(Event)]
struct DoSomethingComplex(Entity, f32);

impl From<ListenerInput<Pointer<Down>>> for DoSomethingComplex {
fn from(event: ListenerInput<Pointer<Down>>) -> Self {
DoSomethingComplex(event.target, event.hit.depth)
}
}

/// Unlike callback systems, this is a normal system that can be run in parallel with other systems.
fn receive_greetings(mut greetings: EventReader<DoSomethingComplex>) {
for event in greetings.read() {
info!(
"Hello {:?}, you are {:?} depth units away from the pointer",
event.0, event.1
);
}
}

0 comments on commit 5a654fb

Please sign in to comment.