Struct BoardCommands

Source
pub struct BoardCommands(/* private fields */);
Expand description

The resource used to send commands to the logic board

Implementations§

Source§

impl BoardCommands

Source

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

Source§

fn default() -> BoardCommands

Returns the “default value” for a type. Read more
Source§

impl Resource for BoardCommands
where Self: Send + Sync + 'static,

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T, U> AsBindGroupShaderType<U> for T
where U: ShaderType, &'a T: for<'a> Into<U>,

Source§

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> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> Downcast for T
where T: Any,

Source§

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>

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)

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)

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
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> FromWorld for T
where T: Default,

Source§

fn from_world(_world: &mut World) -> T

Creates Self using data from the given World.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> Settings for T
where T: 'static + Send + Sync,

Source§

impl<T> WasmNotSend for T
where T: Send,

Source§

impl<T> WasmNotSync for T
where T: Sync,