Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/picking/sprite_picking.rs
6592 views
1
//! Demonstrates picking for sprites and sprite atlases.
2
//! By default, the sprite picking backend considers a sprite only when a pointer is over an opaque pixel.
3
4
use bevy::{prelude::*, sprite::Anchor};
5
use std::fmt::Debug;
6
7
fn main() {
8
App::new()
9
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
10
.add_systems(Startup, (setup, setup_atlas))
11
.add_systems(Update, (move_sprite, animate_sprite))
12
.run();
13
}
14
15
fn move_sprite(
16
time: Res<Time>,
17
mut sprite: Query<&mut Transform, (Without<Sprite>, With<Children>)>,
18
) {
19
let t = time.elapsed_secs() * 0.1;
20
for mut transform in &mut sprite {
21
let new = Vec2 {
22
x: 50.0 * ops::sin(t),
23
y: 50.0 * ops::sin(t * 2.0),
24
};
25
transform.translation.x = new.x;
26
transform.translation.y = new.y;
27
}
28
}
29
30
/// Set up a scene that tests all sprite anchor types.
31
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
32
commands.spawn(Camera2d);
33
34
let len = 128.0;
35
let sprite_size = Vec2::splat(len / 2.0);
36
37
commands
38
.spawn((Transform::default(), Visibility::default()))
39
.with_children(|commands| {
40
for (anchor_index, anchor) in [
41
Anchor::TOP_LEFT,
42
Anchor::TOP_CENTER,
43
Anchor::TOP_RIGHT,
44
Anchor::CENTER_LEFT,
45
Anchor::CENTER,
46
Anchor::CENTER_RIGHT,
47
Anchor::BOTTOM_LEFT,
48
Anchor::BOTTOM_CENTER,
49
Anchor::BOTTOM_RIGHT,
50
]
51
.iter()
52
.enumerate()
53
{
54
let i = (anchor_index % 3) as f32;
55
let j = (anchor_index / 3) as f32;
56
57
// Spawn black square behind sprite to show anchor point
58
commands
59
.spawn((
60
Sprite::from_color(Color::BLACK, sprite_size),
61
Transform::from_xyz(i * len - len, j * len - len, -1.0),
62
Pickable::default(),
63
))
64
.observe(recolor_on::<Pointer<Over>>(Color::srgb(0.0, 1.0, 1.0)))
65
.observe(recolor_on::<Pointer<Out>>(Color::BLACK))
66
.observe(recolor_on::<Pointer<Press>>(Color::srgb(1.0, 1.0, 0.0)))
67
.observe(recolor_on::<Pointer<Release>>(Color::srgb(0.0, 1.0, 1.0)));
68
69
commands
70
.spawn((
71
Sprite {
72
image: asset_server.load("branding/bevy_bird_dark.png"),
73
custom_size: Some(sprite_size),
74
color: Color::srgb(1.0, 0.0, 0.0),
75
..default()
76
},
77
anchor.to_owned(),
78
// 3x3 grid of anchor examples by changing transform
79
Transform::from_xyz(i * len - len, j * len - len, 0.0)
80
.with_scale(Vec3::splat(1.0 + (i - 1.0) * 0.2))
81
.with_rotation(Quat::from_rotation_z((j - 1.0) * 0.2)),
82
Pickable::default(),
83
))
84
.observe(recolor_on::<Pointer<Over>>(Color::srgb(0.0, 1.0, 0.0)))
85
.observe(recolor_on::<Pointer<Out>>(Color::srgb(1.0, 0.0, 0.0)))
86
.observe(recolor_on::<Pointer<Press>>(Color::srgb(0.0, 0.0, 1.0)))
87
.observe(recolor_on::<Pointer<Release>>(Color::srgb(0.0, 1.0, 0.0)));
88
}
89
});
90
}
91
92
#[derive(Component)]
93
struct AnimationIndices {
94
first: usize,
95
last: usize,
96
}
97
98
#[derive(Component, Deref, DerefMut)]
99
struct AnimationTimer(Timer);
100
101
fn animate_sprite(
102
time: Res<Time>,
103
mut query: Query<(&AnimationIndices, &mut AnimationTimer, &mut Sprite)>,
104
) {
105
for (indices, mut timer, mut sprite) in &mut query {
106
let Some(texture_atlas) = &mut sprite.texture_atlas else {
107
continue;
108
};
109
110
timer.tick(time.delta());
111
112
if timer.just_finished() {
113
texture_atlas.index = if texture_atlas.index == indices.last {
114
indices.first
115
} else {
116
texture_atlas.index + 1
117
};
118
}
119
}
120
}
121
122
fn setup_atlas(
123
mut commands: Commands,
124
asset_server: Res<AssetServer>,
125
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
126
) {
127
let texture_handle = asset_server.load("textures/rpg/chars/gabe/gabe-idle-run.png");
128
let layout = TextureAtlasLayout::from_grid(UVec2::new(24, 24), 7, 1, None, None);
129
let texture_atlas_layout_handle = texture_atlas_layouts.add(layout);
130
// Use only the subset of sprites in the sheet that make up the run animation
131
let animation_indices = AnimationIndices { first: 1, last: 6 };
132
commands
133
.spawn((
134
Sprite::from_atlas_image(
135
texture_handle,
136
TextureAtlas {
137
layout: texture_atlas_layout_handle,
138
index: animation_indices.first,
139
},
140
),
141
Transform::from_xyz(300.0, 0.0, 0.0).with_scale(Vec3::splat(6.0)),
142
animation_indices,
143
AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)),
144
Pickable::default(),
145
))
146
.observe(recolor_on::<Pointer<Over>>(Color::srgb(0.0, 1.0, 1.0)))
147
.observe(recolor_on::<Pointer<Out>>(Color::srgb(1.0, 1.0, 1.0)))
148
.observe(recolor_on::<Pointer<Press>>(Color::srgb(1.0, 1.0, 0.0)))
149
.observe(recolor_on::<Pointer<Release>>(Color::srgb(0.0, 1.0, 1.0)));
150
}
151
152
// An observer that changes the target entity's color.
153
fn recolor_on<E: EntityEvent + Debug + Clone + Reflect>(
154
color: Color,
155
) -> impl Fn(On<E>, Query<&mut Sprite>) {
156
move |ev, mut sprites| {
157
let Ok(mut sprite) = sprites.get_mut(ev.entity()) else {
158
return;
159
};
160
sprite.color = color;
161
}
162
}
163
164