-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.rs
161 lines (153 loc) · 7.58 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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
);
}
}