Skip to content

Commit

Permalink
Query::get_unique (#1263)
Browse files Browse the repository at this point in the history
Adds `get_unique` and `get_unique_mut` to extend the query api and cover a common use case. Also establishes a second impl block where non-core APIs that don't access the internal fields of queries can live.
  • Loading branch information
TheRawMeatball committed Mar 8, 2021
1 parent 2c203f7 commit 9d60563
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 10 deletions.
42 changes: 41 additions & 1 deletion crates/bevy_ecs/src/system/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
world::{Mut, World},
};
use bevy_tasks::TaskPool;
use std::any::TypeId;
use std::{any::TypeId, fmt::Debug};
use thiserror::Error;

/// Provides scoped access to a World according to a given [WorldQuery] and query filter
Expand Down Expand Up @@ -212,6 +212,38 @@ where
Err(QueryComponentError::MissingWriteAccess)
}
}

pub fn single(&self) -> Result<<Q::Fetch as Fetch<'_>>::Item, QuerySingleError>
where
Q::Fetch: ReadOnlyFetch,
{
let mut query = self.iter();
let first = query.next();
let extra = query.next().is_some();

match (first, extra) {
(Some(r), false) => Ok(r),
(None, _) => Err(QuerySingleError::NoEntities(std::any::type_name::<Self>())),
(Some(_), _) => Err(QuerySingleError::MultipleEntities(std::any::type_name::<
Self,
>())),
}
}

/// See [`Query::single`]
pub fn single_mut(&mut self) -> Result<<Q::Fetch as Fetch<'_>>::Item, QuerySingleError> {
let mut query = self.iter_mut();
let first = query.next();
let extra = query.next().is_some();

match (first, extra) {
(Some(r), false) => Ok(r),
(None, _) => Err(QuerySingleError::NoEntities(std::any::type_name::<Self>())),
(Some(_), _) => Err(QuerySingleError::MultipleEntities(std::any::type_name::<
Self,
>())),
}
}
}

/// An error that occurs when retrieving a specific [Entity]'s component from a [Query]
Expand All @@ -226,3 +258,11 @@ pub enum QueryComponentError {
#[error("The requested entity does not exist.")]
NoSuchEntity,
}

#[derive(Debug, Error)]
pub enum QuerySingleError {
#[error("No entities fit the query {0}")]
NoEntities(&'static str),
#[error("Multiple entities fit the query {0}!")]
MultipleEntities(&'static str),
}
5 changes: 2 additions & 3 deletions examples/game/alien_cake_addict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,8 @@ fn rotate_bonus(game: Res<Game>, time: Res<Time>, mut transforms: Query<&mut Tra

// update the score displayed during the game
fn scoreboard_system(game: Res<Game>, mut query: Query<&mut Text>) {
for mut text in query.iter_mut() {
text.sections[0].value = format!("Sugar Rush: {}", game.score);
}
let mut text = query.single_mut().unwrap();
text.sections[0].value = format!("Sugar Rush: {}", game.score);
}

// restart the game when pressing spacebar
Expand Down
11 changes: 5 additions & 6 deletions examples/game/breakout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ fn paddle_movement_system(
keyboard_input: Res<Input<KeyCode>>,
mut query: Query<(&Paddle, &mut Transform)>,
) {
for (paddle, mut transform) in query.iter_mut() {
if let Ok((paddle, mut transform)) = query.single_mut() {
let mut direction = 0.0;
if keyboard_input.pressed(KeyCode::Left) {
direction -= 1.0;
Expand All @@ -196,15 +196,14 @@ fn ball_movement_system(time: Res<Time>, mut ball_query: Query<(&Ball, &mut Tran
// clamp the timestep to stop the ball from escaping when the game starts
let delta_seconds = f32::min(0.2, time.delta_seconds());

for (ball, mut transform) in ball_query.iter_mut() {
if let Ok((ball, mut transform)) = ball_query.single_mut() {
transform.translation += ball.velocity * delta_seconds;
}
}

fn scoreboard_system(scoreboard: Res<Scoreboard>, mut query: Query<&mut Text>) {
for mut text in query.iter_mut() {
text.sections[1].value = scoreboard.score.to_string();
}
let mut text = query.single_mut().unwrap();
text.sections[0].value = format!("Score: {}", scoreboard.score);
}

fn ball_collision_system(
Expand All @@ -213,7 +212,7 @@ fn ball_collision_system(
mut ball_query: Query<(&mut Ball, &Transform, &Sprite)>,
collider_query: Query<(Entity, &Collider, &Transform, &Sprite)>,
) {
for (mut ball, ball_transform, sprite) in ball_query.iter_mut() {
if let Ok((mut ball, ball_transform, sprite)) = ball_query.single_mut() {
let ball_size = sprite.size;
let velocity = &mut ball.velocity;

Expand Down

0 comments on commit 9d60563

Please sign in to comment.