pub struct BoardCommands(/* private fields */);
Expand description
The resource used to send commands to the logic board
Implementations§
Source§impl BoardCommands
impl BoardCommands
Sourcepub fn push(&mut self, command: BoardCommand) -> Result<(), &str>
pub fn push(&mut self, command: BoardCommand) -> Result<(), &str>
Pushes a new command onto the command queue
§Examples
use bevy::prelude::*;
use bevy_match3::prelude::*;
fn example_system(
mut board_commands: ResMut<BoardCommands>,
) {
board_commands.push(BoardCommand::Swap(
[0, 0].into(),
[0, 1].into(),
))
.unwrap();
}
Examples found in repository?
examples/basic.rs (lines 156-158)
116fn consume_events(
117 mut commands: Commands,
118 mut events: ResMut<BoardEvents>,
119 mut texture_atlases: ResMut<Assets<TextureAtlas>>,
120 mut board_commands: ResMut<BoardCommands>,
121 ass: Res<AssetServer>,
122 mut board: Query<(Entity, &mut VisibleBoard)>,
123 animations: Query<(), With<MoveTo>>,
124) {
125 if animations.iter().count() == 0 {
126 if let Ok(event) = events.pop() {
127 let (board_entity, mut board) = board.single_mut();
128 match event {
129 BoardEvent::Swapped(pos1, pos2) => {
130 let gem1 = board.0.get(&pos1).copied().unwrap();
131 let gem2 = board.0.get(&pos2).copied().unwrap();
132 commands
133 .entity(gem1)
134 .insert(MoveTo(board_pos_to_world_pos(&pos2)));
135
136 commands
137 .entity(gem2)
138 .insert(MoveTo(board_pos_to_world_pos(&pos1)));
139
140 board.0.insert(pos2, gem1);
141 board.0.insert(pos1, gem2);
142 }
143 BoardEvent::Popped(pos) => {
144 let gem = board.0.get(&pos).copied().unwrap();
145 board.0.remove(&pos);
146 commands.entity(gem).despawn_recursive();
147 spawn_explosion(
148 &ass,
149 &mut texture_atlases,
150 &mut commands,
151 &board_pos_to_world_pos(&pos),
152 );
153 }
154 BoardEvent::Matched(matches) => {
155 board_commands
156 .push(BoardCommand::Pop(
157 matches.without_duplicates().iter().copied().collect(),
158 ))
159 .unwrap();
160 }
161 BoardEvent::Dropped(drops) => {
162 // Need to keep a buffered board clone because we read and write at the same time
163 let mut new_board = board.clone();
164 for Drop { from, to } in drops {
165 let gem = board.0.get(&from).copied().unwrap();
166 new_board.0.insert(to, gem);
167 new_board.0.remove(&from);
168 commands
169 .entity(gem)
170 .insert(MoveTo(board_pos_to_world_pos(&to)));
171 }
172 // And copy the buffer to the resource
173 *board = new_board;
174 }
175 BoardEvent::Spawned(spawns) => {
176 let mut new_board = board.clone();
177
178 for (pos, typ) in spawns {
179 let world_pos = board_pos_to_world_pos(&pos);
180 let gem = commands
181 .spawn(SpriteBundle {
182 texture: ass.load(&map_type_to_path(typ)),
183 transform: Transform::from_xyz(world_pos.x, 200.0, 0.0),
184 sprite: Sprite {
185 custom_size: Some([50.0, 50.0].into()),
186 ..Sprite::default()
187 },
188 ..SpriteBundle::default()
189 })
190 .insert(MoveTo(world_pos))
191 .id();
192 new_board.0.insert(pos, gem);
193 commands.entity(board_entity).add_child(gem);
194 }
195 *board = new_board;
196 }
197 BoardEvent::Shuffled(moves) => {
198 let mut temp_board = board.clone();
199 for (from, to) in moves {
200 let gem = board.0.get(&from).copied().unwrap();
201
202 commands
203 .entity(gem)
204 .insert(MoveTo(board_pos_to_world_pos(&to)));
205
206 temp_board.0.insert(to, gem);
207 }
208 *board = temp_board;
209 }
210 _ => {
211 println!("Received unimplemented event");
212 }
213 }
214 }
215 }
216}
217
218fn board_pos_to_world_pos(pos: &UVec2) -> Vec2 {
219 Vec2::new(
220 pos.x as f32 * GEM_SIDE_LENGTH,
221 -(pos.y as f32) * GEM_SIDE_LENGTH,
222 )
223}
224
225#[derive(Default, Clone, Copy, Resource)]
226struct Selection(Option<Entity>);
227
228fn input(
229 window_query: Query<&Window, With<PrimaryWindow>>,
230 mut selection: ResMut<Selection>,
231 mut button_events: EventReader<MouseButtonInput>,
232 camera: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
233 board: Query<&VisibleBoard>,
234) {
235 for event in button_events.read() {
236 if let MouseButtonInput {
237 button: MouseButton::Left,
238 state: ButtonState::Pressed,
239 ..
240 } = event
241 {
242 let window = window_query.single();
243 let (camera, camera_transform) = camera.single();
244 if let Some(world_position) = window.cursor_position()
245 .and_then(|cursor| camera.viewport_to_world(camera_transform, cursor))
246 .map(|ray| ray.origin.truncate())
247 {
248 // round down to the gem coordinate
249 let coordinates: IVec2 = (
250 ((world_position.x + GEM_SIDE_LENGTH / 2.0) / GEM_SIDE_LENGTH) as i32,
251 ((GEM_SIDE_LENGTH / 2.0 - world_position.y) / GEM_SIDE_LENGTH) as i32,
252 )
253 .into();
254
255 if coordinates.x >= 0 && coordinates.y >= 0 {
256 selection.0 = board
257 .single()
258 .0
259 .get::<UVec2>(&UVec2::new(coordinates.x as u32, coordinates.y as u32))
260 .copied();
261 }
262 }
263 }
264 }
265}
266
267#[derive(Component)]
268struct SelectionRectangle;
269
270fn visualize_selection(
271 mut commands: Commands,
272 selection: Res<Selection>,
273 ass: Res<AssetServer>,
274 g_transforms: Query<&GlobalTransform>,
275 mut rectangle: Query<(Entity, &mut Transform), With<SelectionRectangle>>,
276) {
277 if selection.is_changed() {
278 if let Some(selected_gem) = selection.0 {
279 let transform = g_transforms.get(selected_gem).unwrap();
280 if let Ok((_, mut old_transform)) = rectangle.get_single_mut() {
281 *old_transform = (*transform).into();
282 } else {
283 commands
284 .spawn(SpriteBundle {
285 texture: ass.load("rectangle.png"),
286 sprite: Sprite {
287 custom_size: Some([50.0, 50.0].into()),
288 ..Sprite::default()
289 },
290 transform: (*transform).into(),
291 ..SpriteBundle::default()
292 })
293 .insert(SelectionRectangle);
294 }
295 } else if let Ok((entity, _)) = rectangle.get_single_mut() {
296 commands.entity(entity).despawn();
297 }
298 }
299}
300
301fn control(
302 mut board_commands: ResMut<BoardCommands>,
303 mut selection: ResMut<Selection>,
304 mut last_selection: Local<Selection>,
305 transforms: Query<&Transform>,
306) {
307 if selection.is_changed() {
308 if let Some(selected_gem) = selection.0 {
309 if let Some(last_selected_gem) = last_selection.0 {
310 let selected_pos = transforms.get(selected_gem).unwrap().translation.xy() / 50.0;
311 let last_selected_pos =
312 transforms.get(last_selected_gem as Entity).unwrap().translation.xy() / 50.0;
313
314 board_commands
315 .push(BoardCommand::Swap(
316 [selected_pos.x as u32, -selected_pos.y as u32].into(),
317 [last_selected_pos.x as u32, -last_selected_pos.y as u32].into(),
318 ))
319 .map_err(|err| println!("{err}"))
320 .unwrap();
321 selection.0 = None;
322 last_selection.0 = None;
323 } else {
324 *last_selection = *selection;
325 }
326 } else {
327 last_selection.0 = None
328 }
329 }
330}
331
332#[derive(Component)]
333struct AnimationTimer(Timer);
334
335fn animate_once(
336 mut commands: Commands,
337 time: Res<Time>,
338 texture_atlases: Res<Assets<TextureAtlas>>,
339 mut timers: Query<(
340 Entity,
341 &mut AnimationTimer,
342 &mut TextureAtlasSprite,
343 &Handle<TextureAtlas>,
344 )>,
345) {
346 for (entity, mut timer, mut sprite, texture_atlas_handle) in timers.iter_mut() {
347 timer.0.tick(time.delta());
348 if timer.0.just_finished() {
349 let texture_atlas = texture_atlases.get(texture_atlas_handle).unwrap();
350 if sprite.index == 3 {
351 commands.entity(entity).despawn_recursive();
352 } else {
353 sprite.index = (sprite.index + 1) % texture_atlas.textures.len();
354 }
355 }
356 }
357}
358
359fn spawn_explosion(
360 ass: &AssetServer,
361 texture_atlases: &mut Assets<TextureAtlas>,
362 commands: &mut Commands,
363 pos: &Vec2,
364) {
365 let texture_handle = ass.load("explosion.png");
366 let texture_atlas =
367 TextureAtlas::from_grid(texture_handle, Vec2::new(49.0, 50.0), 4, 1, None, None);
368 let texture_atlas_handle = texture_atlases.add(texture_atlas);
369 commands
370 .spawn(SpriteSheetBundle {
371 texture_atlas: texture_atlas_handle,
372 transform: Transform::from_translation(pos.extend(0.0)),
373 ..SpriteSheetBundle::default()
374 })
375 .insert(AnimationTimer(Timer::from_seconds(
376 0.1,
377 TimerMode::Repeating,
378 )));
379}
380
381fn shuffle(
382 mut board_commands: ResMut<BoardCommands>,
383 mut key_event: EventReader<KeyboardInput>,
384 animations: Query<(), With<MoveTo>>,
385) {
386 if animations.iter().count() == 0 {
387 for event in key_event.read() {
388 if let KeyboardInput {
389 key_code: Some(KeyCode::S),
390 state: ButtonState::Pressed,
391 ..
392 } = event
393 {
394 board_commands.push(BoardCommand::Shuffle).unwrap();
395 }
396 }
397 }
398}
Trait Implementations§
Source§impl Default for BoardCommands
impl Default for BoardCommands
Source§fn default() -> BoardCommands
fn default() -> BoardCommands
Returns the “default value” for a type. Read more
impl Resource for BoardCommands
Auto Trait Implementations§
impl Freeze for BoardCommands
impl RefUnwindSafe for BoardCommands
impl Send for BoardCommands
impl Sync for BoardCommands
impl Unpin for BoardCommands
impl UnwindSafe for BoardCommands
Blanket Implementations§
Source§impl<T, U> AsBindGroupShaderType<U> for T
impl<T, U> AsBindGroupShaderType<U> for T
Source§fn as_bind_group_shader_type(&self, _images: &RenderAssets<Image>) -> U
fn as_bind_group_shader_type(&self, _images: &RenderAssets<Image>) -> U
Return the
T
ShaderType
for self
. When used in AsBindGroup
derives, it is safe to assume that all images in self
exist.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> FromWorld for Twhere
T: Default,
impl<T> FromWorld for Twhere
T: Default,
Source§fn from_world(_world: &mut World) -> T
fn from_world(_world: &mut World) -> T
Creates
Self
using data from the given World
.