Make Query::single (and friends) return a Result (#18082)
# Objective As discussed in #14275, Bevy is currently too prone to panic, and makes the easy / beginner-friendly way to do a large number of operations just to panic on failure. This is seriously frustrating in library code, but also slows down development, as many of the `Query::single` panics can actually safely be an early return (these panics are often due to a small ordering issue or a change in game state. More critically, in most "finished" products, panics are unacceptable: any unexpected failures should be handled elsewhere. That's where the new With the advent of good system error handling, we can now remove this. Note: I was instrumental in a) introducing this idea in the first place and b) pushing to make the panicking variant the default. The introduction of both `let else` statements in Rust and the fancy system error handling work in 0.16 have changed my mind on the right balance here. ## Solution 1. Make `Query::single` and `Query::single_mut` (and other random related methods) return a `Result`. 2. Handle all of Bevy's internal usage of these APIs. 3. Deprecate `Query::get_single` and friends, since we've moved their functionality to the nice names. 4. Add detailed advice on how to best handle these errors. Generally I like the diff here, although `get_single().unwrap()` in tests is a bit of a downgrade. ## Testing I've done a global search for `.single` to track down any missed deprecated usages. As to whether or not all the migrations were successful, that's what CI is for :) ## Future work ~~Rename `Query::get_single` and friends to `Query::single`!~~ ~~I've opted not to do this in this PR, and smear it across two releases in order to ease the migration. Successive deprecations are much easier to manage than the semantics and types shifting under your feet.~~ Cart has convinced me to change my mind on this; see https://github.com/bevyengine/bevy/pull/18082#discussion_r1974536085. ## Migration guide `Query::single`, `Query::single_mut` and their `QueryState` equivalents now return a `Result`. Generally, you'll want to: 1. Use Bevy 0.16's system error handling to return a `Result` using the `?` operator. 2. Use a `let else Ok(data)` block to early return if it's an expected failure. 3. Use `unwrap()` or `Ok` destructuring inside of tests. The old `Query::get_single` (etc) methods which did this have been deprecated.
This commit is contained in:
parent
ff1ae62e1c
commit
2ad5908e58
@ -162,7 +162,7 @@ fn configure_depth_texture_usages(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find all the render target that potentially uses OIT
|
// Find all the render target that potentially uses OIT
|
||||||
let primary_window = p.get_single().ok();
|
let primary_window = p.single().ok();
|
||||||
let mut render_target_has_oit = <HashSet<_>>::default();
|
let mut render_target_has_oit = <HashSet<_>>::default();
|
||||||
for (camera, has_oit) in &cameras {
|
for (camera, has_oit) in &cameras {
|
||||||
if has_oit {
|
if has_oit {
|
||||||
|
@ -260,7 +260,7 @@ pub fn debug_draw(
|
|||||||
.map(|(entity, camera)| {
|
.map(|(entity, camera)| {
|
||||||
(
|
(
|
||||||
entity,
|
entity,
|
||||||
camera.target.normalize(primary_window.get_single().ok()),
|
camera.target.normalize(primary_window.single().ok()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.filter_map(|(entity, target)| Some(entity).zip(target))
|
.filter_map(|(entity, target)| Some(entity).zip(target))
|
||||||
|
@ -27,29 +27,29 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let data: &Foo = query.single();
|
let data: &Foo = query.single().unwrap();
|
||||||
let mut data2: Mut<Foo> = query.single_mut();
|
let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||||
//~^ E0502
|
//~^ E0502
|
||||||
assert_eq!(data, &mut *data2); // oops UB
|
assert_eq!(data, &mut *data2); // oops UB
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut data2: Mut<Foo> = query.single_mut();
|
let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||||
let data: &Foo = query.single();
|
let data: &Foo = query.single().unwrap();
|
||||||
//~^ E0502
|
//~^ E0502
|
||||||
assert_eq!(data, &mut *data2); // oops UB
|
assert_eq!(data, &mut *data2); // oops UB
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let data: &Foo = query.get_single().unwrap();
|
let data: &Foo = query.single().unwrap();
|
||||||
let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||||
//~^ E0502
|
//~^ E0502
|
||||||
assert_eq!(data, &mut *data2); // oops UB
|
assert_eq!(data, &mut *data2); // oops UB
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||||
let data: &Foo = query.get_single().unwrap();
|
let data: &Foo = query.single().unwrap();
|
||||||
//~^ E0502
|
//~^ E0502
|
||||||
assert_eq!(data, &mut *data2); // oops UB
|
assert_eq!(data, &mut *data2); // oops UB
|
||||||
}
|
}
|
||||||
|
@ -45,9 +45,9 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||||||
error[E0502]: cannot borrow `query` as mutable because it is also borrowed as immutable
|
error[E0502]: cannot borrow `query` as mutable because it is also borrowed as immutable
|
||||||
--> tests/ui/query_lifetime_safety.rs:45:39
|
--> tests/ui/query_lifetime_safety.rs:45:39
|
||||||
|
|
|
|
||||||
44 | let data: &Foo = query.get_single().unwrap();
|
44 | let data: &Foo = query.single().unwrap();
|
||||||
| ----- immutable borrow occurs here
|
| ----- immutable borrow occurs here
|
||||||
45 | let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
45 | let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||||
46 |
|
46 |
|
||||||
47 | assert_eq!(data, &mut *data2); // oops UB
|
47 | assert_eq!(data, &mut *data2); // oops UB
|
||||||
@ -56,9 +56,9 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
|||||||
error[E0502]: cannot borrow `query` as immutable because it is also borrowed as mutable
|
error[E0502]: cannot borrow `query` as immutable because it is also borrowed as mutable
|
||||||
--> tests/ui/query_lifetime_safety.rs:52:30
|
--> tests/ui/query_lifetime_safety.rs:52:30
|
||||||
|
|
|
|
||||||
51 | let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
51 | let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||||
| ----- mutable borrow occurs here
|
| ----- mutable borrow occurs here
|
||||||
52 | let data: &Foo = query.get_single().unwrap();
|
52 | let data: &Foo = query.single().unwrap();
|
||||||
| ^^^^^ immutable borrow occurs here
|
| ^^^^^ immutable borrow occurs here
|
||||||
53 |
|
53 |
|
||||||
54 | assert_eq!(data, &mut *data2); // oops UB
|
54 | assert_eq!(data, &mut *data2); // oops UB
|
||||||
|
@ -35,13 +35,13 @@ fn for_loops(mut query: Query<&mut Foo>) {
|
|||||||
fn single_mut_query(mut query: Query<&mut Foo>) {
|
fn single_mut_query(mut query: Query<&mut Foo>) {
|
||||||
// this should fail to compile
|
// this should fail to compile
|
||||||
{
|
{
|
||||||
let mut mut_foo = query.single_mut();
|
let mut mut_foo = query.single_mut().unwrap();
|
||||||
|
|
||||||
// This solves "temporary value dropped while borrowed"
|
// This solves "temporary value dropped while borrowed"
|
||||||
let readonly_query = query.as_readonly();
|
let readonly_query = query.as_readonly();
|
||||||
//~^ E0502
|
//~^ E0502
|
||||||
|
|
||||||
let ref_foo = readonly_query.single();
|
let ref_foo = readonly_query.single().unwrap();
|
||||||
|
|
||||||
*mut_foo = Foo;
|
*mut_foo = Foo;
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ fn single_mut_query(mut query: Query<&mut Foo>) {
|
|||||||
|
|
||||||
let ref_foo = readonly_query.single();
|
let ref_foo = readonly_query.single();
|
||||||
|
|
||||||
let mut mut_foo = query.single_mut();
|
let mut mut_foo = query.single_mut().unwrap();
|
||||||
//~^ E0502
|
//~^ E0502
|
||||||
|
|
||||||
println!("{ref_foo:?}");
|
println!("{ref_foo:?}");
|
||||||
|
@ -22,8 +22,8 @@ fn main() {
|
|||||||
let mut query_a = lens_a.query();
|
let mut query_a = lens_a.query();
|
||||||
let mut query_b = lens_b.query();
|
let mut query_b = lens_b.query();
|
||||||
|
|
||||||
let a = query_a.single_mut();
|
let a = query_a.single_mut().unwrap();
|
||||||
let b = query_b.single_mut(); // oops 2 mutable references to same Foo
|
let b = query_b.single_mut().unwrap(); // oops 2 mutable references to same Foo
|
||||||
assert_eq!(*a, *b);
|
assert_eq!(*a, *b);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,8 +34,8 @@ fn main() {
|
|||||||
let mut query_b = lens.query();
|
let mut query_b = lens.query();
|
||||||
//~^ E0499
|
//~^ E0499
|
||||||
|
|
||||||
let a = query_a.single_mut();
|
let a = query_a.single_mut().unwrap();
|
||||||
let b = query_b.single_mut(); // oops 2 mutable references to same Foo
|
let b = query_b.single_mut().unwrap(); // oops 2 mutable references to same Foo
|
||||||
assert_eq!(*a, *b);
|
assert_eq!(*a, *b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1557,7 +1557,7 @@ mod tests {
|
|||||||
// Since the world is always ahead, as long as changes can't get older than `u32::MAX` (which we ensure),
|
// Since the world is always ahead, as long as changes can't get older than `u32::MAX` (which we ensure),
|
||||||
// the wrapping difference will always be positive, so wraparound doesn't matter.
|
// the wrapping difference will always be positive, so wraparound doesn't matter.
|
||||||
let mut query = world.query::<Ref<C>>();
|
let mut query = world.query::<Ref<C>>();
|
||||||
assert!(query.single(&world).is_changed());
|
assert!(query.single(&world).unwrap().is_changed());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -33,7 +33,7 @@ use super::{FilteredAccess, QueryData, QueryFilter};
|
|||||||
/// .build();
|
/// .build();
|
||||||
///
|
///
|
||||||
/// // Consume the QueryState
|
/// // Consume the QueryState
|
||||||
/// let (entity, b) = query.single(&world);
|
/// let (entity, b) = query.single(&world).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub struct QueryBuilder<'w, D: QueryData = (), F: QueryFilter = ()> {
|
pub struct QueryBuilder<'w, D: QueryData = (), F: QueryFilter = ()> {
|
||||||
access: FilteredAccess<ComponentId>,
|
access: FilteredAccess<ComponentId>,
|
||||||
@ -297,13 +297,13 @@ mod tests {
|
|||||||
.with::<A>()
|
.with::<A>()
|
||||||
.without::<C>()
|
.without::<C>()
|
||||||
.build();
|
.build();
|
||||||
assert_eq!(entity_a, query_a.single(&world));
|
assert_eq!(entity_a, query_a.single(&world).unwrap());
|
||||||
|
|
||||||
let mut query_b = QueryBuilder::<Entity>::new(&mut world)
|
let mut query_b = QueryBuilder::<Entity>::new(&mut world)
|
||||||
.with::<A>()
|
.with::<A>()
|
||||||
.without::<B>()
|
.without::<B>()
|
||||||
.build();
|
.build();
|
||||||
assert_eq!(entity_b, query_b.single(&world));
|
assert_eq!(entity_b, query_b.single(&world).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -319,13 +319,13 @@ mod tests {
|
|||||||
.with_id(component_id_a)
|
.with_id(component_id_a)
|
||||||
.without_id(component_id_c)
|
.without_id(component_id_c)
|
||||||
.build();
|
.build();
|
||||||
assert_eq!(entity_a, query_a.single(&world));
|
assert_eq!(entity_a, query_a.single(&world).unwrap());
|
||||||
|
|
||||||
let mut query_b = QueryBuilder::<Entity>::new(&mut world)
|
let mut query_b = QueryBuilder::<Entity>::new(&mut world)
|
||||||
.with_id(component_id_a)
|
.with_id(component_id_a)
|
||||||
.without_id(component_id_b)
|
.without_id(component_id_b)
|
||||||
.build();
|
.build();
|
||||||
assert_eq!(entity_b, query_b.single(&world));
|
assert_eq!(entity_b, query_b.single(&world).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -385,7 +385,7 @@ mod tests {
|
|||||||
.data::<&B>()
|
.data::<&B>()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let entity_ref = query.single(&world);
|
let entity_ref = query.single(&world).unwrap();
|
||||||
|
|
||||||
assert_eq!(entity, entity_ref.id());
|
assert_eq!(entity, entity_ref.id());
|
||||||
|
|
||||||
@ -408,7 +408,7 @@ mod tests {
|
|||||||
.ref_id(component_id_b)
|
.ref_id(component_id_b)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let entity_ref = query.single(&world);
|
let entity_ref = query.single(&world).unwrap();
|
||||||
|
|
||||||
assert_eq!(entity, entity_ref.id());
|
assert_eq!(entity, entity_ref.id());
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ impl<'w> PartialEq for QueryEntityError<'w> {
|
|||||||
impl<'w> Eq for QueryEntityError<'w> {}
|
impl<'w> Eq for QueryEntityError<'w> {}
|
||||||
|
|
||||||
/// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState) as a single expected result via
|
/// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState) as a single expected result via
|
||||||
/// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut).
|
/// [`single`](crate::system::Query::single) or [`single_mut`](crate::system::Query::single_mut).
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum QuerySingleError {
|
pub enum QuerySingleError {
|
||||||
/// No entity fits the query.
|
/// No entity fits the query.
|
||||||
|
@ -763,8 +763,8 @@ mod tests {
|
|||||||
let _: Option<&Foo> = q.get(&world, e).ok();
|
let _: Option<&Foo> = q.get(&world, e).ok();
|
||||||
let _: Option<&Foo> = q.get_manual(&world, e).ok();
|
let _: Option<&Foo> = q.get_manual(&world, e).ok();
|
||||||
let _: Option<[&Foo; 1]> = q.get_many(&world, [e]).ok();
|
let _: Option<[&Foo; 1]> = q.get_many(&world, [e]).ok();
|
||||||
let _: Option<&Foo> = q.get_single(&world).ok();
|
let _: Option<&Foo> = q.single(&world).ok();
|
||||||
let _: &Foo = q.single(&world);
|
let _: &Foo = q.single(&world).unwrap();
|
||||||
|
|
||||||
// system param
|
// system param
|
||||||
let mut q = SystemState::<Query<&mut Foo>>::new(&mut world);
|
let mut q = SystemState::<Query<&mut Foo>>::new(&mut world);
|
||||||
@ -776,9 +776,9 @@ mod tests {
|
|||||||
|
|
||||||
let _: Option<&Foo> = q.get(e).ok();
|
let _: Option<&Foo> = q.get(e).ok();
|
||||||
let _: Option<[&Foo; 1]> = q.get_many([e]).ok();
|
let _: Option<[&Foo; 1]> = q.get_many([e]).ok();
|
||||||
let _: Option<&Foo> = q.get_single().ok();
|
let _: Option<&Foo> = q.single().ok();
|
||||||
let _: [&Foo; 1] = q.many([e]);
|
let _: [&Foo; 1] = q.many([e]);
|
||||||
let _: &Foo = q.single();
|
let _: &Foo = q.single().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// regression test for https://github.com/bevyengine/bevy/pull/8029
|
// regression test for https://github.com/bevyengine/bevy/pull/8029
|
||||||
|
@ -1597,43 +1597,88 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
/// This can only be called for read-only queries,
|
/// This can only be called for read-only queries,
|
||||||
/// see [`single_mut`](Self::single_mut) for write-queries.
|
/// see [`single_mut`](Self::single_mut) for write-queries.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
|
||||||
|
/// instead.
|
||||||
///
|
///
|
||||||
/// Panics if the number of query results is not exactly one. Use
|
/// # Example
|
||||||
/// [`get_single`](Self::get_single) to return a `Result` instead of panicking.
|
///
|
||||||
#[track_caller]
|
/// Sometimes, you might want to handle the error in a specific way,
|
||||||
|
/// generally by spawning the missing entity.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use bevy_ecs::prelude::*;
|
||||||
|
/// use bevy_ecs::query::QuerySingleError;
|
||||||
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct A(usize);
|
||||||
|
///
|
||||||
|
/// fn my_system(query: Query<&A>, mut commands: Commands) {
|
||||||
|
/// match query.single() {
|
||||||
|
/// Ok(a) => (), // Do something with `a`
|
||||||
|
/// Err(err) => match err {
|
||||||
|
/// QuerySingleError::NoEntities(_) => {
|
||||||
|
/// commands.spawn(A(0));
|
||||||
|
/// }
|
||||||
|
/// QuerySingleError::MultipleEntities(_) => panic!("Multiple entities found!"),
|
||||||
|
/// },
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// However in most cases, this error can simply be handled with a graceful early return.
|
||||||
|
/// If this is an expected failure mode, you can do this using the `let else` pattern like so:
|
||||||
|
/// ```rust
|
||||||
|
/// use bevy_ecs::prelude::*;
|
||||||
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct A(usize);
|
||||||
|
///
|
||||||
|
/// fn my_system(query: Query<&A>) {
|
||||||
|
/// let Ok(a) = query.single() else {
|
||||||
|
/// return;
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// // Do something with `a`
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If this is unexpected though, you should probably use the `?` operator
|
||||||
|
/// in combination with Bevy's error handling apparatus.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use bevy_ecs::prelude::*;
|
||||||
|
///
|
||||||
|
/// #[derive(Component)]
|
||||||
|
/// struct A(usize);
|
||||||
|
///
|
||||||
|
/// fn my_system(query: Query<&A>) -> Result {
|
||||||
|
/// let a = query.single()?;
|
||||||
|
///
|
||||||
|
/// // Do something with `a`
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This allows you to globally control how errors are handled in your application,
|
||||||
|
/// by setting up a custom error handler.
|
||||||
|
/// See the [`bevy_ecs::result`] module docs for more information!
|
||||||
|
/// Commonly, you might want to panic on an error during development, but log the error and continue
|
||||||
|
/// execution in production.
|
||||||
|
///
|
||||||
|
/// Simply unwrapping the [`Result`] also works, but should generally be reserved for tests.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn single<'w>(&mut self, world: &'w World) -> ROQueryItem<'w, D> {
|
pub fn single<'w>(&mut self, world: &'w World) -> Result<ROQueryItem<'w, D>, QuerySingleError> {
|
||||||
self.query(world).single_inner()
|
self.query(world).single_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a single immutable query result when there is exactly one entity matching
|
/// A deprecated alias for [`QueryState::single`].
|
||||||
/// the query.
|
#[deprecated(since = "0.16.0", note = "Please use `single` instead.")]
|
||||||
///
|
|
||||||
/// This can only be called for read-only queries,
|
|
||||||
/// see [`get_single_mut`](Self::get_single_mut) for write-queries.
|
|
||||||
///
|
|
||||||
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
|
|
||||||
/// instead.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_single<'w>(
|
pub fn get_single<'w>(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: &'w World,
|
world: &'w World,
|
||||||
) -> Result<ROQueryItem<'w, D>, QuerySingleError> {
|
) -> Result<ROQueryItem<'w, D>, QuerySingleError> {
|
||||||
self.query(world).get_single_inner()
|
self.single(world)
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a single mutable query result when there is exactly one entity matching
|
|
||||||
/// the query.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the number of query results is not exactly one. Use
|
|
||||||
/// [`get_single_mut`](Self::get_single_mut) to return a `Result` instead of panicking.
|
|
||||||
#[track_caller]
|
|
||||||
#[inline]
|
|
||||||
pub fn single_mut<'w>(&mut self, world: &'w mut World) -> D::Item<'w> {
|
|
||||||
self.query_mut(world).single_inner()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a single mutable query result when there is exactly one entity matching
|
/// Returns a single mutable query result when there is exactly one entity matching
|
||||||
@ -1641,12 +1686,25 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
///
|
///
|
||||||
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
|
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
|
||||||
/// instead.
|
/// instead.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Please see [`Query::single`] for advice on handling the error.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn single_mut<'w>(
|
||||||
|
&mut self,
|
||||||
|
world: &'w mut World,
|
||||||
|
) -> Result<D::Item<'w>, QuerySingleError> {
|
||||||
|
self.query_mut(world).single_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A deprecated alias for [`QueryState::single_mut`].
|
||||||
|
#[deprecated(since = "0.16.0", note = "Please use `single` instead.")]
|
||||||
pub fn get_single_mut<'w>(
|
pub fn get_single_mut<'w>(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: &'w mut World,
|
world: &'w mut World,
|
||||||
) -> Result<D::Item<'w>, QuerySingleError> {
|
) -> Result<D::Item<'w>, QuerySingleError> {
|
||||||
self.query_mut(world).get_single_inner()
|
self.single_mut(world)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a query result when there is exactly one entity matching the query.
|
/// Returns a query result when there is exactly one entity matching the query.
|
||||||
@ -1659,11 +1717,11 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||||
/// have unique access to the components they query.
|
/// have unique access to the components they query.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn get_single_unchecked<'w>(
|
pub unsafe fn single_unchecked<'w>(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: UnsafeWorldCell<'w>,
|
world: UnsafeWorldCell<'w>,
|
||||||
) -> Result<D::Item<'w>, QuerySingleError> {
|
) -> Result<D::Item<'w>, QuerySingleError> {
|
||||||
self.query_unchecked(world).get_single_inner()
|
self.query_unchecked(world).single_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a query result when there is exactly one entity matching the query,
|
/// Returns a query result when there is exactly one entity matching the query,
|
||||||
@ -1679,7 +1737,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
|
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
|
||||||
/// with a mismatched [`WorldId`] is unsound.
|
/// with a mismatched [`WorldId`] is unsound.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn get_single_unchecked_manual<'w>(
|
pub unsafe fn single_unchecked_manual<'w>(
|
||||||
&self,
|
&self,
|
||||||
world: UnsafeWorldCell<'w>,
|
world: UnsafeWorldCell<'w>,
|
||||||
last_run: Tick,
|
last_run: Tick,
|
||||||
@ -1689,7 +1747,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
// - The caller ensured we have the correct access to the world.
|
// - The caller ensured we have the correct access to the world.
|
||||||
// - The caller ensured that the world matches.
|
// - The caller ensured that the world matches.
|
||||||
self.query_unchecked_manual_with_ticks(world, last_run, this_run)
|
self.query_unchecked_manual_with_ticks(world, last_run, this_run)
|
||||||
.get_single_inner()
|
.single_inner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1753,7 +1811,7 @@ mod tests {
|
|||||||
let query_state = world.query::<(&A, &B)>();
|
let query_state = world.query::<(&A, &B)>();
|
||||||
let mut new_query_state = query_state.transmute::<&A>(&world);
|
let mut new_query_state = query_state.transmute::<&A>(&world);
|
||||||
assert_eq!(new_query_state.iter(&world).len(), 1);
|
assert_eq!(new_query_state.iter(&world).len(), 1);
|
||||||
let a = new_query_state.single(&world);
|
let a = new_query_state.single(&world).unwrap();
|
||||||
|
|
||||||
assert_eq!(a.0, 1);
|
assert_eq!(a.0, 1);
|
||||||
}
|
}
|
||||||
@ -1767,7 +1825,7 @@ mod tests {
|
|||||||
let query_state = world.query_filtered::<(&A, &B), Without<C>>();
|
let query_state = world.query_filtered::<(&A, &B), Without<C>>();
|
||||||
let mut new_query_state = query_state.transmute::<&A>(&world);
|
let mut new_query_state = query_state.transmute::<&A>(&world);
|
||||||
// even though we change the query to not have Without<C>, we do not get the component with C.
|
// even though we change the query to not have Without<C>, we do not get the component with C.
|
||||||
let a = new_query_state.single(&world);
|
let a = new_query_state.single(&world).unwrap();
|
||||||
|
|
||||||
assert_eq!(a.0, 0);
|
assert_eq!(a.0, 0);
|
||||||
}
|
}
|
||||||
@ -1780,7 +1838,7 @@ mod tests {
|
|||||||
|
|
||||||
let q = world.query::<()>();
|
let q = world.query::<()>();
|
||||||
let mut q = q.transmute::<Entity>(&world);
|
let mut q = q.transmute::<Entity>(&world);
|
||||||
assert_eq!(q.single(&world), entity);
|
assert_eq!(q.single(&world).unwrap(), entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1790,7 +1848,7 @@ mod tests {
|
|||||||
|
|
||||||
let q = world.query::<&A>();
|
let q = world.query::<&A>();
|
||||||
let mut new_q = q.transmute::<Ref<A>>(&world);
|
let mut new_q = q.transmute::<Ref<A>>(&world);
|
||||||
assert!(new_q.single(&world).is_added());
|
assert!(new_q.single(&world).unwrap().is_added());
|
||||||
|
|
||||||
let q = world.query::<Ref<A>>();
|
let q = world.query::<Ref<A>>();
|
||||||
let _ = q.transmute::<&A>(&world);
|
let _ = q.transmute::<&A>(&world);
|
||||||
@ -1861,7 +1919,7 @@ mod tests {
|
|||||||
|
|
||||||
let query_state = world.query::<Option<&A>>();
|
let query_state = world.query::<Option<&A>>();
|
||||||
let mut new_query_state = query_state.transmute::<&A>(&world);
|
let mut new_query_state = query_state.transmute::<&A>(&world);
|
||||||
let x = new_query_state.single(&world);
|
let x = new_query_state.single(&world).unwrap();
|
||||||
assert_eq!(x.0, 1234);
|
assert_eq!(x.0, 1234);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1886,7 +1944,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut query = query;
|
let mut query = query;
|
||||||
// Our result is completely untyped
|
// Our result is completely untyped
|
||||||
let entity_ref = query.single(&world);
|
let entity_ref = query.single(&world).unwrap();
|
||||||
|
|
||||||
assert_eq!(entity, entity_ref.id());
|
assert_eq!(entity, entity_ref.id());
|
||||||
assert_eq!(0, entity_ref.get::<A>().unwrap().0);
|
assert_eq!(0, entity_ref.get::<A>().unwrap().0);
|
||||||
@ -1901,16 +1959,16 @@ mod tests {
|
|||||||
let mut query = QueryState::<(Entity, &A, Has<B>)>::new(&mut world)
|
let mut query = QueryState::<(Entity, &A, Has<B>)>::new(&mut world)
|
||||||
.transmute_filtered::<(Entity, Has<B>), Added<A>>(&world);
|
.transmute_filtered::<(Entity, Has<B>), Added<A>>(&world);
|
||||||
|
|
||||||
assert_eq!((entity_a, false), query.single(&world));
|
assert_eq!((entity_a, false), query.single(&world).unwrap());
|
||||||
|
|
||||||
world.clear_trackers();
|
world.clear_trackers();
|
||||||
|
|
||||||
let entity_b = world.spawn((A(0), B(0))).id();
|
let entity_b = world.spawn((A(0), B(0))).id();
|
||||||
assert_eq!((entity_b, true), query.single(&world));
|
assert_eq!((entity_b, true), query.single(&world).unwrap());
|
||||||
|
|
||||||
world.clear_trackers();
|
world.clear_trackers();
|
||||||
|
|
||||||
assert!(query.get_single(&world).is_err());
|
assert!(query.single(&world).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1922,15 +1980,15 @@ mod tests {
|
|||||||
.transmute_filtered::<Entity, Changed<A>>(&world);
|
.transmute_filtered::<Entity, Changed<A>>(&world);
|
||||||
|
|
||||||
let mut change_query = QueryState::<&mut A>::new(&mut world);
|
let mut change_query = QueryState::<&mut A>::new(&mut world);
|
||||||
assert_eq!(entity_a, detection_query.single(&world));
|
assert_eq!(entity_a, detection_query.single(&world).unwrap());
|
||||||
|
|
||||||
world.clear_trackers();
|
world.clear_trackers();
|
||||||
|
|
||||||
assert!(detection_query.get_single(&world).is_err());
|
assert!(detection_query.single(&world).is_err());
|
||||||
|
|
||||||
change_query.single_mut(&mut world).0 = 1;
|
change_query.single_mut(&mut world).unwrap().0 = 1;
|
||||||
|
|
||||||
assert_eq!(entity_a, detection_query.single(&world));
|
assert_eq!(entity_a, detection_query.single(&world).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -2017,7 +2075,7 @@ mod tests {
|
|||||||
let query_2 = QueryState::<&B, Without<C>>::new(&mut world);
|
let query_2 = QueryState::<&B, Without<C>>::new(&mut world);
|
||||||
let mut new_query: QueryState<Entity, ()> = query_1.join_filtered(&world, &query_2);
|
let mut new_query: QueryState<Entity, ()> = query_1.join_filtered(&world, &query_2);
|
||||||
|
|
||||||
assert_eq!(new_query.single(&world), entity_ab);
|
assert_eq!(new_query.single(&world).unwrap(), entity_ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -270,7 +270,7 @@ use core::{
|
|||||||
/// |[`iter_combinations`]\[[`_mut`][`iter_combinations_mut`]]|Returns an iterator over all combinations of a specified number of query items.|
|
/// |[`iter_combinations`]\[[`_mut`][`iter_combinations_mut`]]|Returns an iterator over all combinations of a specified number of query items.|
|
||||||
/// |[`get`]\[[`_mut`][`get_mut`]]|Returns the query item for the specified entity.|
|
/// |[`get`]\[[`_mut`][`get_mut`]]|Returns the query item for the specified entity.|
|
||||||
/// |[`many`]\[[`_mut`][`many_mut`]],<br>[`get_many`]\[[`_mut`][`get_many_mut`]]|Returns the query items for the specified entities.|
|
/// |[`many`]\[[`_mut`][`many_mut`]],<br>[`get_many`]\[[`_mut`][`get_many_mut`]]|Returns the query items for the specified entities.|
|
||||||
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`get_single`]\[[`_mut`][`get_single_mut`]]|Returns the query item while verifying that there aren't others.|
|
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`single`]\[[`_mut`][`single_mut`]]|Returns the query item while verifying that there aren't others.|
|
||||||
///
|
///
|
||||||
/// There are two methods for each type of query operation: immutable and mutable (ending with `_mut`).
|
/// There are two methods for each type of query operation: immutable and mutable (ending with `_mut`).
|
||||||
/// When using immutable methods, the query items returned are of type [`ROQueryItem`], a read-only version of the query item.
|
/// When using immutable methods, the query items returned are of type [`ROQueryItem`], a read-only version of the query item.
|
||||||
@ -307,7 +307,7 @@ use core::{
|
|||||||
/// |[`get`]\[[`_mut`][`get_mut`]]|O(1)|
|
/// |[`get`]\[[`_mut`][`get_mut`]]|O(1)|
|
||||||
/// |([`get_`][`get_many`])[`many`]|O(k)|
|
/// |([`get_`][`get_many`])[`many`]|O(k)|
|
||||||
/// |([`get_`][`get_many_mut`])[`many_mut`]|O(k<sup>2</sup>)|
|
/// |([`get_`][`get_many_mut`])[`many_mut`]|O(k<sup>2</sup>)|
|
||||||
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`get_single`]\[[`_mut`][`get_single_mut`]]|O(a)|
|
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`single`]\[[`_mut`][`single_mut`]]|O(a)|
|
||||||
/// |Archetype based filtering ([`With`], [`Without`], [`Or`])|O(a)|
|
/// |Archetype based filtering ([`With`], [`Without`], [`Or`])|O(a)|
|
||||||
/// |Change detection filtering ([`Added`], [`Changed`])|O(a + n)|
|
/// |Change detection filtering ([`Added`], [`Changed`])|O(a + n)|
|
||||||
///
|
///
|
||||||
@ -351,8 +351,8 @@ use core::{
|
|||||||
/// [`get_many`]: Self::get_many
|
/// [`get_many`]: Self::get_many
|
||||||
/// [`get_many_mut`]: Self::get_many_mut
|
/// [`get_many_mut`]: Self::get_many_mut
|
||||||
/// [`get_mut`]: Self::get_mut
|
/// [`get_mut`]: Self::get_mut
|
||||||
/// [`get_single`]: Self::get_single
|
/// [`single`]: Self::single
|
||||||
/// [`get_single_mut`]: Self::get_single_mut
|
/// [`single_mut`]: Self::single_mut
|
||||||
/// [`iter`]: Self::iter
|
/// [`iter`]: Self::iter
|
||||||
/// [`iter_combinations`]: Self::iter_combinations
|
/// [`iter_combinations`]: Self::iter_combinations
|
||||||
/// [`iter_combinations_mut`]: Self::iter_combinations_mut
|
/// [`iter_combinations_mut`]: Self::iter_combinations_mut
|
||||||
@ -947,7 +947,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
/// friends_query: Query<&Friends>,
|
/// friends_query: Query<&Friends>,
|
||||||
/// mut counter_query: Query<&mut Counter>,
|
/// mut counter_query: Query<&mut Counter>,
|
||||||
/// ) {
|
/// ) {
|
||||||
/// let friends = friends_query.single();
|
/// let friends = friends_query.single().unwrap();
|
||||||
/// for mut counter in counter_query.iter_many_unique_inner(friends) {
|
/// for mut counter in counter_query.iter_many_unique_inner(friends) {
|
||||||
/// println!("Friend's counter: {:?}", counter.value);
|
/// println!("Friend's counter: {:?}", counter.value);
|
||||||
/// counter.value += 1;
|
/// counter.value += 1;
|
||||||
@ -1706,36 +1706,6 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
unsafe { self.reborrow_unsafe() }.get_inner(entity)
|
unsafe { self.reborrow_unsafe() }.get_inner(entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a single read-only query item when there is exactly one entity matching the query.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This method panics if the number of query items is **not** exactly one.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bevy_ecs::prelude::*;
|
|
||||||
/// # #[derive(Component)]
|
|
||||||
/// # struct Player;
|
|
||||||
/// # #[derive(Component)]
|
|
||||||
/// # struct Position(f32, f32);
|
|
||||||
/// fn player_system(query: Query<&Position, With<Player>>) {
|
|
||||||
/// let player_position = query.single();
|
|
||||||
/// // do something with player_position
|
|
||||||
/// }
|
|
||||||
/// # bevy_ecs::system::assert_is_system(player_system);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # See also
|
|
||||||
///
|
|
||||||
/// - [`get_single`](Self::get_single) for the non-panicking version.
|
|
||||||
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
|
|
||||||
#[track_caller]
|
|
||||||
pub fn single(&self) -> ROQueryItem<'_, D> {
|
|
||||||
self.get_single().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a single read-only query item when there is exactly one entity matching the query.
|
/// Returns a single read-only query item when there is exactly one entity matching the query.
|
||||||
///
|
///
|
||||||
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
|
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
|
||||||
@ -1748,7 +1718,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
/// # #[derive(Component)]
|
/// # #[derive(Component)]
|
||||||
/// # struct PlayerScore(i32);
|
/// # struct PlayerScore(i32);
|
||||||
/// fn player_scoring_system(query: Query<&PlayerScore>) {
|
/// fn player_scoring_system(query: Query<&PlayerScore>) {
|
||||||
/// match query.get_single() {
|
/// match query.single() {
|
||||||
/// Ok(PlayerScore(score)) => {
|
/// Ok(PlayerScore(score)) => {
|
||||||
/// println!("Score: {}", score);
|
/// println!("Score: {}", score);
|
||||||
/// }
|
/// }
|
||||||
@ -1765,43 +1735,16 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
///
|
///
|
||||||
/// # See also
|
/// # See also
|
||||||
///
|
///
|
||||||
/// - [`get_single_mut`](Self::get_single_mut) to get the mutable query item.
|
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
|
||||||
/// - [`single`](Self::single) for the panicking version.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
|
pub fn single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
|
||||||
self.as_readonly().get_single_inner()
|
self.as_readonly().single_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a single query item when there is exactly one entity matching the query.
|
/// A deprecated alias for [`single`](Self::single).
|
||||||
///
|
#[deprecated(note = "Please use `single` instead")]
|
||||||
/// # Panics
|
pub fn get_single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
|
||||||
///
|
self.single()
|
||||||
/// This method panics if the number of query items is **not** exactly one.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bevy_ecs::prelude::*;
|
|
||||||
/// #
|
|
||||||
/// # #[derive(Component)]
|
|
||||||
/// # struct Player;
|
|
||||||
/// # #[derive(Component)]
|
|
||||||
/// # struct Health(u32);
|
|
||||||
/// #
|
|
||||||
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
|
|
||||||
/// let mut health = query.single_mut();
|
|
||||||
/// health.0 += 1;
|
|
||||||
/// }
|
|
||||||
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # See also
|
|
||||||
///
|
|
||||||
/// - [`get_single_mut`](Self::get_single_mut) for the non-panicking version.
|
|
||||||
/// - [`single`](Self::single) to get the read-only query item.
|
|
||||||
#[track_caller]
|
|
||||||
pub fn single_mut(&mut self) -> D::Item<'_> {
|
|
||||||
self.get_single_mut().unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a single query item when there is exactly one entity matching the query.
|
/// Returns a single query item when there is exactly one entity matching the query.
|
||||||
@ -1819,7 +1762,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
/// # struct Health(u32);
|
/// # struct Health(u32);
|
||||||
/// #
|
/// #
|
||||||
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
|
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
|
||||||
/// let mut health = query.get_single_mut().expect("Error: Could not find a single player.");
|
/// let mut health = query.single_mut().expect("Error: Could not find a single player.");
|
||||||
/// health.0 += 1;
|
/// health.0 += 1;
|
||||||
/// }
|
/// }
|
||||||
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
||||||
@ -1827,19 +1770,22 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
///
|
///
|
||||||
/// # See also
|
/// # See also
|
||||||
///
|
///
|
||||||
/// - [`get_single`](Self::get_single) to get the read-only query item.
|
/// - [`single`](Self::single) to get the read-only query item.
|
||||||
/// - [`single_mut`](Self::single_mut) for the panicking version.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn single_mut(&mut self) -> Result<D::Item<'_>, QuerySingleError> {
|
||||||
|
self.reborrow().single_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A deprecated alias for [`single_mut`](Self::single_mut).
|
||||||
|
#[deprecated(note = "Please use `single_mut` instead")]
|
||||||
pub fn get_single_mut(&mut self) -> Result<D::Item<'_>, QuerySingleError> {
|
pub fn get_single_mut(&mut self) -> Result<D::Item<'_>, QuerySingleError> {
|
||||||
self.reborrow().get_single_inner()
|
self.single_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a single query item when there is exactly one entity matching the query.
|
/// Returns a single query item when there is exactly one entity matching the query.
|
||||||
/// This consumes the [`Query`] to return results with the actual "inner" world lifetime.
|
/// This consumes the [`Query`] to return results with the actual "inner" world lifetime.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
|
||||||
///
|
|
||||||
/// This method panics if the number of query items is **not** exactly one.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1852,7 +1798,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
/// # struct Health(u32);
|
/// # struct Health(u32);
|
||||||
/// #
|
/// #
|
||||||
/// fn regenerate_player_health_system(query: Query<&mut Health, With<Player>>) {
|
/// fn regenerate_player_health_system(query: Query<&mut Health, With<Player>>) {
|
||||||
/// let mut health = query.single_inner();
|
/// let mut health = query.single_inner().expect("Error: Could not find a single player.");
|
||||||
/// health.0 += 1;
|
/// health.0 += 1;
|
||||||
/// }
|
/// }
|
||||||
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
||||||
@ -1860,43 +1806,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
///
|
///
|
||||||
/// # See also
|
/// # See also
|
||||||
///
|
///
|
||||||
/// - [`get_single_inner`](Self::get_single_inner) for the non-panicking version.
|
|
||||||
/// - [`single`](Self::single) to get the read-only query item.
|
/// - [`single`](Self::single) to get the read-only query item.
|
||||||
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
|
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
|
||||||
#[track_caller]
|
|
||||||
pub fn single_inner(self) -> D::Item<'w> {
|
|
||||||
self.get_single_inner().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a single query item when there is exactly one entity matching the query.
|
|
||||||
/// This consumes the [`Query`] to return results with the actual "inner" world lifetime.
|
|
||||||
///
|
|
||||||
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use bevy_ecs::prelude::*;
|
|
||||||
/// #
|
|
||||||
/// # #[derive(Component)]
|
|
||||||
/// # struct Player;
|
|
||||||
/// # #[derive(Component)]
|
|
||||||
/// # struct Health(u32);
|
|
||||||
/// #
|
|
||||||
/// fn regenerate_player_health_system(query: Query<&mut Health, With<Player>>) {
|
|
||||||
/// let mut health = query.get_single_inner().expect("Error: Could not find a single player.");
|
|
||||||
/// health.0 += 1;
|
|
||||||
/// }
|
|
||||||
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # See also
|
|
||||||
///
|
|
||||||
/// - [`get_single`](Self::get_single) to get the read-only query item.
|
|
||||||
/// - [`get_single_mut`](Self::get_single_mut) to get the mutable query item.
|
|
||||||
/// - [`single_inner`](Self::single_inner) for the panicking version.
|
/// - [`single_inner`](Self::single_inner) for the panicking version.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_single_inner(self) -> Result<D::Item<'w>, QuerySingleError> {
|
pub fn single_inner(self) -> Result<D::Item<'w>, QuerySingleError> {
|
||||||
let mut query = self.into_iter();
|
let mut query = self.into_iter();
|
||||||
let first = query.next();
|
let first = query.next();
|
||||||
let extra = query.next().is_some();
|
let extra = query.next().is_some();
|
||||||
@ -2001,7 +1915,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
/// # world.spawn((A(10), B(5)));
|
/// # world.spawn((A(10), B(5)));
|
||||||
/// #
|
/// #
|
||||||
/// fn reusable_function(lens: &mut QueryLens<&A>) {
|
/// fn reusable_function(lens: &mut QueryLens<&A>) {
|
||||||
/// assert_eq!(lens.query().single().0, 10);
|
/// assert_eq!(lens.query().single().unwrap().0, 10);
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// // We can use the function in a system that takes the exact query.
|
/// // We can use the function in a system that takes the exact query.
|
||||||
@ -2160,7 +2074,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
/// # world.spawn((A(10), B(5)));
|
/// # world.spawn((A(10), B(5)));
|
||||||
/// #
|
/// #
|
||||||
/// fn reusable_function(mut lens: QueryLens<&A>) {
|
/// fn reusable_function(mut lens: QueryLens<&A>) {
|
||||||
/// assert_eq!(lens.query().single().0, 10);
|
/// assert_eq!(lens.query().single().unwrap().0, 10);
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// // We can use the function in a system that takes the exact query.
|
/// // We can use the function in a system that takes the exact query.
|
||||||
|
@ -408,7 +408,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo
|
|||||||
state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick)
|
state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick)
|
||||||
};
|
};
|
||||||
let single = query
|
let single = query
|
||||||
.get_single_inner()
|
.single_inner()
|
||||||
.expect("The query was expected to contain exactly one matching entity.");
|
.expect("The query was expected to contain exactly one matching entity.");
|
||||||
Single {
|
Single {
|
||||||
item: single,
|
item: single,
|
||||||
@ -432,7 +432,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo
|
|||||||
world.change_tick(),
|
world.change_tick(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let is_valid = query.get_single_inner().is_ok();
|
let is_valid = query.single_inner().is_ok();
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
system_meta.try_warn_param::<Self>();
|
system_meta.try_warn_param::<Self>();
|
||||||
}
|
}
|
||||||
@ -474,7 +474,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
|
|||||||
let query = unsafe {
|
let query = unsafe {
|
||||||
state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick)
|
state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick)
|
||||||
};
|
};
|
||||||
match query.get_single_inner() {
|
match query.single_inner() {
|
||||||
Ok(single) => Some(Single {
|
Ok(single) => Some(Single {
|
||||||
item: single,
|
item: single,
|
||||||
_filter: PhantomData,
|
_filter: PhantomData,
|
||||||
@ -500,7 +500,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
|
|||||||
world.change_tick(),
|
world.change_tick(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let result = query.get_single_inner();
|
let result = query.single_inner();
|
||||||
let is_valid = !matches!(result, Err(QuerySingleError::MultipleEntities(_)));
|
let is_valid = !matches!(result, Err(QuerySingleError::MultipleEntities(_)));
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
system_meta.try_warn_param::<Self>();
|
system_meta.try_warn_param::<Self>();
|
||||||
|
@ -2430,11 +2430,11 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
/// let mut entity = world.spawn_empty();
|
/// let mut entity = world.spawn_empty();
|
||||||
/// entity.entry().or_insert_with(|| Comp(4));
|
/// entity.entry().or_insert_with(|| Comp(4));
|
||||||
/// # let entity_id = entity.id();
|
/// # let entity_id = entity.id();
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 4);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 4);
|
||||||
///
|
///
|
||||||
/// # let mut entity = world.get_entity_mut(entity_id).unwrap();
|
/// # let mut entity = world.get_entity_mut(entity_id).unwrap();
|
||||||
/// entity.entry::<Comp>().and_modify(|mut c| c.0 += 1);
|
/// entity.entry::<Comp>().and_modify(|mut c| c.0 += 1);
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 5);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 5);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -2714,7 +2714,7 @@ impl<'w, 'a, T: Component<Mutability = Mutable>> Entry<'w, 'a, T> {
|
|||||||
/// let mut entity = world.spawn(Comp(0));
|
/// let mut entity = world.spawn(Comp(0));
|
||||||
///
|
///
|
||||||
/// entity.entry::<Comp>().and_modify(|mut c| c.0 += 1);
|
/// entity.entry::<Comp>().and_modify(|mut c| c.0 += 1);
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 1);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 1);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn and_modify<F: FnOnce(Mut<'_, T>)>(self, f: F) -> Self {
|
pub fn and_modify<F: FnOnce(Mut<'_, T>)>(self, f: F) -> Self {
|
||||||
@ -2773,11 +2773,11 @@ impl<'w, 'a, T: Component> Entry<'w, 'a, T> {
|
|||||||
///
|
///
|
||||||
/// entity.entry().or_insert(Comp(4));
|
/// entity.entry().or_insert(Comp(4));
|
||||||
/// # let entity_id = entity.id();
|
/// # let entity_id = entity.id();
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 4);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 4);
|
||||||
///
|
///
|
||||||
/// # let mut entity = world.get_entity_mut(entity_id).unwrap();
|
/// # let mut entity = world.get_entity_mut(entity_id).unwrap();
|
||||||
/// entity.entry().or_insert(Comp(15)).into_mut().0 *= 2;
|
/// entity.entry().or_insert(Comp(15)).into_mut().0 *= 2;
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 8);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 8);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn or_insert(self, default: T) -> OccupiedEntry<'w, 'a, T> {
|
pub fn or_insert(self, default: T) -> OccupiedEntry<'w, 'a, T> {
|
||||||
@ -2801,7 +2801,7 @@ impl<'w, 'a, T: Component> Entry<'w, 'a, T> {
|
|||||||
/// let mut entity = world.spawn_empty();
|
/// let mut entity = world.spawn_empty();
|
||||||
///
|
///
|
||||||
/// entity.entry().or_insert_with(|| Comp(4));
|
/// entity.entry().or_insert_with(|| Comp(4));
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 4);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 4);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> OccupiedEntry<'w, 'a, T> {
|
pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> OccupiedEntry<'w, 'a, T> {
|
||||||
@ -2827,7 +2827,7 @@ impl<'w, 'a, T: Component + Default> Entry<'w, 'a, T> {
|
|||||||
/// let mut entity = world.spawn_empty();
|
/// let mut entity = world.spawn_empty();
|
||||||
///
|
///
|
||||||
/// entity.entry::<Comp>().or_default();
|
/// entity.entry::<Comp>().or_default();
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 0);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 0);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn or_default(self) -> OccupiedEntry<'w, 'a, T> {
|
pub fn or_default(self) -> OccupiedEntry<'w, 'a, T> {
|
||||||
@ -2885,7 +2885,7 @@ impl<'w, 'a, T: Component> OccupiedEntry<'w, 'a, T> {
|
|||||||
/// o.insert(Comp(10));
|
/// o.insert(Comp(10));
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 10);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 10);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert(&mut self, component: T) {
|
pub fn insert(&mut self, component: T) {
|
||||||
@ -2943,7 +2943,7 @@ impl<'w, 'a, T: Component<Mutability = Mutable>> OccupiedEntry<'w, 'a, T> {
|
|||||||
/// o.get_mut().0 += 2
|
/// o.get_mut().0 += 2
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 17);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 17);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut(&mut self) -> Mut<'_, T> {
|
pub fn get_mut(&mut self) -> Mut<'_, T> {
|
||||||
@ -2972,7 +2972,7 @@ impl<'w, 'a, T: Component<Mutability = Mutable>> OccupiedEntry<'w, 'a, T> {
|
|||||||
/// o.into_mut().0 += 10;
|
/// o.into_mut().0 += 10;
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 15);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 15);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_mut(self) -> Mut<'a, T> {
|
pub fn into_mut(self) -> Mut<'a, T> {
|
||||||
@ -3004,7 +3004,7 @@ impl<'w, 'a, T: Component> VacantEntry<'w, 'a, T> {
|
|||||||
/// v.insert(Comp(10));
|
/// v.insert(Comp(10));
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 10);
|
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 10);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert(self, component: T) -> OccupiedEntry<'w, 'a, T> {
|
pub fn insert(self, component: T) -> OccupiedEntry<'w, 'a, T> {
|
||||||
@ -3036,7 +3036,7 @@ impl<'w, 'a, T: Component> VacantEntry<'w, 'a, T> {
|
|||||||
/// .data::<&A>()
|
/// .data::<&A>()
|
||||||
/// .build();
|
/// .build();
|
||||||
///
|
///
|
||||||
/// let filtered_entity: FilteredEntityRef = query.single(&mut world);
|
/// let filtered_entity: FilteredEntityRef = query.single(&mut world).unwrap();
|
||||||
/// let component: &A = filtered_entity.get().unwrap();
|
/// let component: &A = filtered_entity.get().unwrap();
|
||||||
///
|
///
|
||||||
/// // Here `FilteredEntityRef` is nested in a tuple, so it does not have access to `&A`.
|
/// // Here `FilteredEntityRef` is nested in a tuple, so it does not have access to `&A`.
|
||||||
@ -3044,7 +3044,7 @@ impl<'w, 'a, T: Component> VacantEntry<'w, 'a, T> {
|
|||||||
/// .data::<&A>()
|
/// .data::<&A>()
|
||||||
/// .build();
|
/// .build();
|
||||||
///
|
///
|
||||||
/// let (_, filtered_entity) = query.single(&mut world);
|
/// let (_, filtered_entity) = query.single(&mut world).unwrap();
|
||||||
/// assert!(filtered_entity.get::<A>().is_none());
|
/// assert!(filtered_entity.get::<A>().is_none());
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -3367,7 +3367,7 @@ unsafe impl TrustedEntityBorrow for FilteredEntityRef<'_> {}
|
|||||||
/// .data::<&mut A>()
|
/// .data::<&mut A>()
|
||||||
/// .build();
|
/// .build();
|
||||||
///
|
///
|
||||||
/// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world);
|
/// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world).unwrap();
|
||||||
/// let component: Mut<A> = filtered_entity.get_mut().unwrap();
|
/// let component: Mut<A> = filtered_entity.get_mut().unwrap();
|
||||||
///
|
///
|
||||||
/// // Here `FilteredEntityMut` is nested in a tuple, so it does not have access to `&mut A`.
|
/// // Here `FilteredEntityMut` is nested in a tuple, so it does not have access to `&mut A`.
|
||||||
@ -3375,7 +3375,7 @@ unsafe impl TrustedEntityBorrow for FilteredEntityRef<'_> {}
|
|||||||
/// .data::<&mut A>()
|
/// .data::<&mut A>()
|
||||||
/// .build();
|
/// .build();
|
||||||
///
|
///
|
||||||
/// let (_, mut filtered_entity) = query.single_mut(&mut world);
|
/// let (_, mut filtered_entity) = query.single_mut(&mut world).unwrap();
|
||||||
/// assert!(filtered_entity.get_mut::<A>().is_none());
|
/// assert!(filtered_entity.get_mut::<A>().is_none());
|
||||||
/// ```
|
/// ```
|
||||||
pub struct FilteredEntityMut<'w> {
|
pub struct FilteredEntityMut<'w> {
|
||||||
|
@ -226,7 +226,7 @@ pub fn dispatch_focused_input<E: Event + Clone>(
|
|||||||
windows: Query<Entity, With<PrimaryWindow>>,
|
windows: Query<Entity, With<PrimaryWindow>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
if let Ok(window) = windows.get_single() {
|
if let Ok(window) = windows.single() {
|
||||||
// If an element has keyboard focus, then dispatch the input event to that element.
|
// If an element has keyboard focus, then dispatch the input event to that element.
|
||||||
if let Some(focused_entity) = focus.0 {
|
if let Some(focused_entity) = focus.0 {
|
||||||
for ev in key_events.read() {
|
for ev in key_events.read() {
|
||||||
|
@ -251,7 +251,7 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
|
|||||||
fallback_image,
|
fallback_image,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
RenderViewIrradianceVolumeBindGroupEntries::get_single(
|
RenderViewIrradianceVolumeBindGroupEntries::single(
|
||||||
render_view_irradiance_volumes,
|
render_view_irradiance_volumes,
|
||||||
images,
|
images,
|
||||||
fallback_image,
|
fallback_image,
|
||||||
@ -295,7 +295,7 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
|
|||||||
/// Looks up and returns the bindings for any irradiance volumes visible in
|
/// Looks up and returns the bindings for any irradiance volumes visible in
|
||||||
/// the view, as well as the sampler. This is the version used when binding
|
/// the view, as well as the sampler. This is the version used when binding
|
||||||
/// arrays aren't available on the current platform.
|
/// arrays aren't available on the current platform.
|
||||||
fn get_single(
|
fn single(
|
||||||
render_view_irradiance_volumes: Option<&RenderViewLightProbes<IrradianceVolume>>,
|
render_view_irradiance_volumes: Option<&RenderViewLightProbes<IrradianceVolume>>,
|
||||||
images: &'a RenderAssets<GpuImage>,
|
images: &'a RenderAssets<GpuImage>,
|
||||||
fallback_image: &'a FallbackImage,
|
fallback_image: &'a FallbackImage,
|
||||||
|
@ -118,7 +118,7 @@ pub fn mouse_pick_events(
|
|||||||
WindowEvent::CursorMoved(event) => {
|
WindowEvent::CursorMoved(event) => {
|
||||||
let location = Location {
|
let location = Location {
|
||||||
target: match RenderTarget::Window(WindowRef::Entity(event.window))
|
target: match RenderTarget::Window(WindowRef::Entity(event.window))
|
||||||
.normalize(primary_window.get_single().ok())
|
.normalize(primary_window.single().ok())
|
||||||
{
|
{
|
||||||
Some(target) => target,
|
Some(target) => target,
|
||||||
None => continue,
|
None => continue,
|
||||||
@ -138,7 +138,7 @@ pub fn mouse_pick_events(
|
|||||||
WindowEvent::MouseButtonInput(input) => {
|
WindowEvent::MouseButtonInput(input) => {
|
||||||
let location = Location {
|
let location = Location {
|
||||||
target: match RenderTarget::Window(WindowRef::Entity(input.window))
|
target: match RenderTarget::Window(WindowRef::Entity(input.window))
|
||||||
.normalize(primary_window.get_single().ok())
|
.normalize(primary_window.single().ok())
|
||||||
{
|
{
|
||||||
Some(target) => target,
|
Some(target) => target,
|
||||||
None => continue,
|
None => continue,
|
||||||
@ -162,7 +162,7 @@ pub fn mouse_pick_events(
|
|||||||
|
|
||||||
let location = Location {
|
let location = Location {
|
||||||
target: match RenderTarget::Window(WindowRef::Entity(window))
|
target: match RenderTarget::Window(WindowRef::Entity(window))
|
||||||
.normalize(primary_window.get_single().ok())
|
.normalize(primary_window.single().ok())
|
||||||
{
|
{
|
||||||
Some(target) => target,
|
Some(target) => target,
|
||||||
None => continue,
|
None => continue,
|
||||||
@ -195,7 +195,7 @@ pub fn touch_pick_events(
|
|||||||
let pointer = PointerId::Touch(touch.id);
|
let pointer = PointerId::Touch(touch.id);
|
||||||
let location = Location {
|
let location = Location {
|
||||||
target: match RenderTarget::Window(WindowRef::Entity(touch.window))
|
target: match RenderTarget::Window(WindowRef::Entity(touch.window))
|
||||||
.normalize(primary_window.get_single().ok())
|
.normalize(primary_window.single().ok())
|
||||||
{
|
{
|
||||||
Some(target) => target,
|
Some(target) => target,
|
||||||
None => continue,
|
None => continue,
|
||||||
|
@ -224,7 +224,7 @@ impl Location {
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
if camera
|
if camera
|
||||||
.target
|
.target
|
||||||
.normalize(Some(match primary_window.get_single() {
|
.normalize(Some(match primary_window.single() {
|
||||||
Ok(w) => w,
|
Ok(w) => w,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
}))
|
}))
|
||||||
|
@ -317,7 +317,7 @@ impl Plugin for RenderPlugin {
|
|||||||
let primary_window = app
|
let primary_window = app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
|
.query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
|
||||||
.get_single(app.world())
|
.single(app.world())
|
||||||
.ok()
|
.ok()
|
||||||
.cloned();
|
.cloned();
|
||||||
let settings = render_creation.clone();
|
let settings = render_creation.clone();
|
||||||
|
@ -539,7 +539,7 @@ mod tests {
|
|||||||
// Only one synchronized entity
|
// Only one synchronized entity
|
||||||
assert!(q.iter(&render_world).count() == 1);
|
assert!(q.iter(&render_world).count() == 1);
|
||||||
|
|
||||||
let render_entity = q.get_single(&render_world).unwrap();
|
let render_entity = q.single(&render_world).unwrap();
|
||||||
let render_entity_component = main_world.get::<RenderEntity>(main_entity).unwrap();
|
let render_entity_component = main_world.get::<RenderEntity>(main_entity).unwrap();
|
||||||
|
|
||||||
assert!(render_entity_component.id() == render_entity);
|
assert!(render_entity_component.id() == render_entity);
|
||||||
|
@ -588,7 +588,8 @@ mod tests {
|
|||||||
let (scene_entity, scene_component_a) = app
|
let (scene_entity, scene_component_a) = app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.query::<(Entity, &ComponentA)>()
|
.query::<(Entity, &ComponentA)>()
|
||||||
.single(app.world());
|
.single(app.world())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(scene_component_a.x, 3.0);
|
assert_eq!(scene_component_a.x, 3.0);
|
||||||
assert_eq!(scene_component_a.y, 4.0);
|
assert_eq!(scene_component_a.y, 4.0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -631,7 +632,10 @@ mod tests {
|
|||||||
|
|
||||||
// clone only existing entity
|
// clone only existing entity
|
||||||
let mut scene_spawner = SceneSpawner::default();
|
let mut scene_spawner = SceneSpawner::default();
|
||||||
let entity = world.query_filtered::<Entity, With<A>>().single(&world);
|
let entity = world
|
||||||
|
.query_filtered::<Entity, With<A>>()
|
||||||
|
.single(&world)
|
||||||
|
.unwrap();
|
||||||
let scene = DynamicSceneBuilder::from_world(&world)
|
let scene = DynamicSceneBuilder::from_world(&world)
|
||||||
.extract_entity(entity)
|
.extract_entity(entity)
|
||||||
.build();
|
.build();
|
||||||
|
@ -763,12 +763,12 @@ mod tests {
|
|||||||
|
|
||||||
let bar_to_foo = dst_world
|
let bar_to_foo = dst_world
|
||||||
.query_filtered::<&MyEntityRef, Without<Foo>>()
|
.query_filtered::<&MyEntityRef, Without<Foo>>()
|
||||||
.get_single(&dst_world)
|
.single(&dst_world)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let foo = dst_world
|
let foo = dst_world
|
||||||
.query_filtered::<Entity, With<Foo>>()
|
.query_filtered::<Entity, With<Foo>>()
|
||||||
.get_single(&dst_world)
|
.single(&dst_world)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(foo, bar_to_foo.0);
|
assert_eq!(foo, bar_to_foo.0);
|
||||||
@ -793,7 +793,7 @@ mod tests {
|
|||||||
deserialized_scene
|
deserialized_scene
|
||||||
.write_to_world(&mut world, &mut EntityHashMap::default())
|
.write_to_world(&mut world, &mut EntityHashMap::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(&qux, world.query::<&Qux>().single(&world));
|
assert_eq!(&qux, world.query::<&Qux>().single(&world).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -115,7 +115,7 @@ fn sprite_picking(
|
|||||||
-transform.translation().z
|
-transform.translation().z
|
||||||
});
|
});
|
||||||
|
|
||||||
let primary_window = primary_window.get_single().ok();
|
let primary_window = primary_window.single().ok();
|
||||||
|
|
||||||
for (pointer, location) in pointers.iter().filter_map(|(pointer, pointer_location)| {
|
for (pointer, location) in pointers.iter().filter_map(|(pointer, pointer_location)| {
|
||||||
pointer_location.location().map(|loc| (pointer, loc))
|
pointer_location.location().map(|loc| (pointer, loc))
|
||||||
|
@ -153,7 +153,7 @@ pub fn extract_text2d_sprite(
|
|||||||
) {
|
) {
|
||||||
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
|
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
|
||||||
let scale_factor = windows
|
let scale_factor = windows
|
||||||
.get_single()
|
.single()
|
||||||
.map(|window| window.resolution.scale_factor())
|
.map(|window| window.resolution.scale_factor())
|
||||||
.unwrap_or(1.0);
|
.unwrap_or(1.0);
|
||||||
let scaling = GlobalTransform::from_scale(Vec2::splat(scale_factor.recip()).extend(1.));
|
let scaling = GlobalTransform::from_scale(Vec2::splat(scale_factor.recip()).extend(1.));
|
||||||
@ -256,7 +256,7 @@ pub fn update_text2d_layout(
|
|||||||
) {
|
) {
|
||||||
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
|
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
|
||||||
let scale_factor = windows
|
let scale_factor = windows
|
||||||
.get_single()
|
.single()
|
||||||
.ok()
|
.ok()
|
||||||
.map(|window| window.resolution.scale_factor())
|
.map(|window| window.resolution.scale_factor())
|
||||||
.or(*last_scale_factor)
|
.or(*last_scale_factor)
|
||||||
|
@ -43,7 +43,7 @@ fn calc_bounds(
|
|||||||
Ref<GlobalTransform>,
|
Ref<GlobalTransform>,
|
||||||
)>,
|
)>,
|
||||||
) {
|
) {
|
||||||
if let Ok((camera, camera_transform)) = camera.get_single() {
|
if let Ok((camera, camera_transform)) = camera.single() {
|
||||||
for (mut accessible, node, transform) in &mut nodes {
|
for (mut accessible, node, transform) in &mut nodes {
|
||||||
if node.is_changed() || transform.is_changed() {
|
if node.is_changed() || transform.is_changed() {
|
||||||
if let Ok(translation) =
|
if let Ok(translation) =
|
||||||
|
@ -726,7 +726,7 @@ mod tests {
|
|||||||
mut cameras: Query<&mut Camera>,
|
mut cameras: Query<&mut Camera>,
|
||||||
) {
|
) {
|
||||||
let primary_window = primary_window_query
|
let primary_window = primary_window_query
|
||||||
.get_single()
|
.single()
|
||||||
.expect("missing primary window");
|
.expect("missing primary window");
|
||||||
let camera_count = cameras.iter().len();
|
let camera_count = cameras.iter().len();
|
||||||
for (camera_index, mut camera) in cameras.iter_mut().enumerate() {
|
for (camera_index, mut camera) in cameras.iter_mut().enumerate() {
|
||||||
@ -783,7 +783,7 @@ mod tests {
|
|||||||
ui_schedule.run(world);
|
ui_schedule.run(world);
|
||||||
let (ui_node_entity, UiTargetCamera(target_camera_entity)) = world
|
let (ui_node_entity, UiTargetCamera(target_camera_entity)) = world
|
||||||
.query_filtered::<(Entity, &UiTargetCamera), With<MovingUiNode>>()
|
.query_filtered::<(Entity, &UiTargetCamera), With<MovingUiNode>>()
|
||||||
.get_single(world)
|
.single(world)
|
||||||
.expect("missing MovingUiNode");
|
.expect("missing MovingUiNode");
|
||||||
assert_eq!(expected_camera_entity, target_camera_entity);
|
assert_eq!(expected_camera_entity, target_camera_entity);
|
||||||
let mut ui_surface = world.resource_mut::<UiSurface>();
|
let mut ui_surface = world.resource_mut::<UiSurface>();
|
||||||
|
@ -84,7 +84,7 @@ pub fn ui_picking(
|
|||||||
.map(|(entity, camera, _)| {
|
.map(|(entity, camera, _)| {
|
||||||
(
|
(
|
||||||
entity,
|
entity,
|
||||||
camera.target.normalize(primary_window.get_single().ok()),
|
camera.target.normalize(primary_window.single().ok()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.filter_map(|(entity, target)| Some(entity).zip(target))
|
.filter_map(|(entity, target)| Some(entity).zip(target))
|
||||||
|
@ -2691,7 +2691,7 @@ pub struct DefaultUiCamera<'w, 's> {
|
|||||||
|
|
||||||
impl<'w, 's> DefaultUiCamera<'w, 's> {
|
impl<'w, 's> DefaultUiCamera<'w, 's> {
|
||||||
pub fn get(&self) -> Option<Entity> {
|
pub fn get(&self) -> Option<Entity> {
|
||||||
self.default_cameras.get_single().ok().or_else(|| {
|
self.default_cameras.single().ok().or_else(|| {
|
||||||
// If there isn't a single camera and the query isn't empty, there is two or more cameras queried.
|
// If there isn't a single camera and the query isn't empty, there is two or more cameras queried.
|
||||||
if !self.default_cameras.is_empty() {
|
if !self.default_cameras.is_empty() {
|
||||||
once!(warn!("Two or more Entities with IsDefaultUiCamera found when only one Camera with this marker is allowed."));
|
once!(warn!("Two or more Entities with IsDefaultUiCamera found when only one Camera with this marker is allowed."));
|
||||||
|
@ -183,7 +183,7 @@ fn update_accessibility_nodes(
|
|||||||
)>,
|
)>,
|
||||||
node_entities: Query<Entity, With<AccessibilityNode>>,
|
node_entities: Query<Entity, With<AccessibilityNode>>,
|
||||||
) {
|
) {
|
||||||
let Ok((primary_window_id, primary_window)) = primary_window.get_single() else {
|
let Ok((primary_window_id, primary_window)) = primary_window.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(adapter) = adapters.get_mut(&primary_window_id) else {
|
let Some(adapter) = adapters.get_mut(&primary_window_id) else {
|
||||||
|
@ -551,7 +551,7 @@ impl<T: Event> WinitAppRunnerState<T> {
|
|||||||
let mut query = self
|
let mut query = self
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.query_filtered::<Entity, With<PrimaryWindow>>();
|
.query_filtered::<Entity, With<PrimaryWindow>>();
|
||||||
let entity = query.single(&self.world());
|
let entity = query.single(&self.world()).unwrap();
|
||||||
self.world_mut()
|
self.world_mut()
|
||||||
.entity_mut(entity)
|
.entity_mut(entity)
|
||||||
.remove::<RawHandleWrapper>();
|
.remove::<RawHandleWrapper>();
|
||||||
@ -571,7 +571,7 @@ impl<T: Event> WinitAppRunnerState<T> {
|
|||||||
// handle wrapper removed when the app was suspended.
|
// handle wrapper removed when the app was suspended.
|
||||||
let mut query = self.world_mut()
|
let mut query = self.world_mut()
|
||||||
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>();
|
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>();
|
||||||
if let Ok((entity, window)) = query.get_single(&self.world()) {
|
if let Ok((entity, window)) = query.single(&self.world()) {
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
|
|
||||||
let mut create_window =
|
let mut create_window =
|
||||||
|
@ -15,7 +15,7 @@ fn draw_cursor(
|
|||||||
window: Query<&Window>,
|
window: Query<&Window>,
|
||||||
mut gizmos: Gizmos,
|
mut gizmos: Gizmos,
|
||||||
) {
|
) {
|
||||||
let Ok(window) = window.get_single() else {
|
let Ok(window) = window.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ fn draw_cursor(
|
|||||||
windows: Query<&Window>,
|
windows: Query<&Window>,
|
||||||
mut gizmos: Gizmos,
|
mut gizmos: Gizmos,
|
||||||
) {
|
) {
|
||||||
let Ok(windows) = windows.get_single() else {
|
let Ok(windows) = windows.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ fn input_handler(
|
|||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
) {
|
) {
|
||||||
if keyboard_input.just_pressed(KeyCode::Space) {
|
if keyboard_input.just_pressed(KeyCode::Space) {
|
||||||
let mesh_handle = mesh_query.get_single().expect("Query not successful");
|
let mesh_handle = mesh_query.single().expect("Query not successful");
|
||||||
let mesh = meshes.get_mut(mesh_handle).unwrap();
|
let mesh = meshes.get_mut(mesh_handle).unwrap();
|
||||||
toggle_texture(mesh);
|
toggle_texture(mesh);
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ fn drag_drop_image(
|
|||||||
mat.base_color_texture = Some(new_image.clone());
|
mat.base_color_texture = Some(new_image.clone());
|
||||||
|
|
||||||
// Despawn the image viewer instructions
|
// Despawn the image viewer instructions
|
||||||
if let Ok(text_entity) = text.get_single() {
|
if let Ok(text_entity) = text.single() {
|
||||||
commands.entity(text_entity).despawn();
|
commands.entity(text_entity).despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,7 +258,7 @@ fn get_async_loading_state(
|
|||||||
// If loaded, change the state.
|
// If loaded, change the state.
|
||||||
if is_loaded {
|
if is_loaded {
|
||||||
next_loading_state.set(LoadingState::Loaded);
|
next_loading_state.set(LoadingState::Loaded);
|
||||||
if let Ok(mut text) = text.get_single_mut() {
|
if let Ok(mut text) = text.single_mut() {
|
||||||
"Loaded!".clone_into(&mut **text);
|
"Loaded!".clone_into(&mut **text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
struct MyMusic;
|
struct MyMusic;
|
||||||
|
|
||||||
fn update_speed(music_controller: Query<&AudioSink, With<MyMusic>>, time: Res<Time>) {
|
fn update_speed(music_controller: Query<&AudioSink, With<MyMusic>>, time: Res<Time>) {
|
||||||
let Ok(sink) = music_controller.get_single() else {
|
let Ok(sink) = music_controller.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ fn pause(
|
|||||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
music_controller: Query<&AudioSink, With<MyMusic>>,
|
music_controller: Query<&AudioSink, With<MyMusic>>,
|
||||||
) {
|
) {
|
||||||
let Ok(sink) = music_controller.get_single() else {
|
let Ok(sink) = music_controller.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ fn mute(
|
|||||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
mut music_controller: Query<&mut AudioSink, With<MyMusic>>,
|
mut music_controller: Query<&mut AudioSink, With<MyMusic>>,
|
||||||
) {
|
) {
|
||||||
let Ok(mut sink) = music_controller.get_single_mut() else {
|
let Ok(mut sink) = music_controller.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ fn volume(
|
|||||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
mut music_controller: Query<&mut AudioSink, With<MyMusic>>,
|
mut music_controller: Query<&mut AudioSink, With<MyMusic>>,
|
||||||
) {
|
) {
|
||||||
let Ok(mut sink) = music_controller.get_single_mut() else {
|
let Ok(mut sink) = music_controller.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ fn screen_shake(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// return camera to the latest position of player (it's fixed in this example case)
|
// return camera to the latest position of player (it's fixed in this example case)
|
||||||
if let Ok((mut camera, mut transform)) = query.get_single_mut() {
|
if let Ok((mut camera, mut transform)) = query.single_mut() {
|
||||||
let sub_view = camera.sub_camera_view.as_mut().unwrap();
|
let sub_view = camera.sub_camera_view.as_mut().unwrap();
|
||||||
let target = screen_shake.latest_position.unwrap();
|
let target = screen_shake.latest_position.unwrap();
|
||||||
sub_view
|
sub_view
|
||||||
|
@ -69,7 +69,7 @@ fn list_all_named_entities(
|
|||||||
text_string.push_str(&format!("{:?}\n", name));
|
text_string.push_str(&format!("{:?}\n", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(mut text) = name_text_query.get_single_mut() {
|
if let Ok(mut text) = name_text_query.single_mut() {
|
||||||
*text = Text::new(text_string);
|
*text = Text::new(text_string);
|
||||||
} else {
|
} else {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
|
@ -175,7 +175,7 @@ fn handle_click(
|
|||||||
windows: Query<&Window>,
|
windows: Query<&Window>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
let Ok(windows) = windows.get_single() else {
|
let Ok(windows) = windows.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ fn move_system(mut sprites: Query<(&mut Transform, &Velocity)>) {
|
|||||||
|
|
||||||
// Bounce sprites outside the window
|
// Bounce sprites outside the window
|
||||||
fn bounce_system(window: Query<&Window>, mut sprites: Query<(&Transform, &mut Velocity)>) {
|
fn bounce_system(window: Query<&Window>, mut sprites: Query<(&Transform, &mut Velocity)>) {
|
||||||
let Ok(window) = window.get_single() else {
|
let Ok(window) = window.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let width = window.width();
|
let width = window.width();
|
||||||
|
@ -256,7 +256,7 @@ fn collisions(
|
|||||||
mut query: Query<(&mut Velocity, &mut Transform), With<Contributor>>,
|
mut query: Query<(&mut Velocity, &mut Transform), With<Contributor>>,
|
||||||
mut rng: ResMut<SharedRng>,
|
mut rng: ResMut<SharedRng>,
|
||||||
) {
|
) {
|
||||||
let Ok(window) = window.get_single() else {
|
let Ok(window) = window.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ fn run_camera_controller(
|
|||||||
) {
|
) {
|
||||||
let dt = time.delta_secs();
|
let dt = time.delta_secs();
|
||||||
|
|
||||||
let Ok((mut transform, mut controller)) = query.get_single_mut() else {
|
let Ok((mut transform, mut controller)) = query.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ fn touch_camera(
|
|||||||
mut last_position: Local<Option<Vec2>>,
|
mut last_position: Local<Option<Vec2>>,
|
||||||
mut rotations: EventReader<RotationGesture>,
|
mut rotations: EventReader<RotationGesture>,
|
||||||
) {
|
) {
|
||||||
let Ok(window) = window.get_single() else {
|
let Ok(window) = window.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ fn mouse_handler(
|
|||||||
mut rng: Local<Option<ChaCha8Rng>>,
|
mut rng: Local<Option<ChaCha8Rng>>,
|
||||||
mut wave: Local<usize>,
|
mut wave: Local<usize>,
|
||||||
) {
|
) {
|
||||||
let Ok(window) = window.get_single() else {
|
let Ok(window) = window.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -534,7 +534,7 @@ fn handle_collision(half_extents: Vec2, translation: &Vec3, velocity: &mut Vec3)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn collision_system(window: Query<&Window>, mut bird_query: Query<(&mut Bird, &Transform)>) {
|
fn collision_system(window: Query<&Window>, mut bird_query: Query<(&mut Bird, &Transform)>) {
|
||||||
let Ok(window) = window.get_single() else {
|
let Ok(window) = window.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ fn setup(
|
|||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
window: Query<&Window>,
|
window: Query<&Window>,
|
||||||
) {
|
) -> Result {
|
||||||
// circular base
|
// circular base
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh3d(meshes.add(Circle::new(4.0))),
|
Mesh3d(meshes.add(Circle::new(4.0))),
|
||||||
@ -56,7 +56,7 @@ fn setup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// cameras
|
// cameras
|
||||||
let window = window.single();
|
let window = window.single()?;
|
||||||
let width = window.resolution.width() / CAMERA_COLS as f32 * window.resolution.scale_factor();
|
let width = window.resolution.width() / CAMERA_COLS as f32 * window.resolution.scale_factor();
|
||||||
let height = window.resolution.height() / CAMERA_ROWS as f32 * window.resolution.scale_factor();
|
let height = window.resolution.height() / CAMERA_ROWS as f32 * window.resolution.scale_factor();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@ -83,6 +83,7 @@ fn setup(
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_cameras(time: Res<Time>, mut query: Query<&mut Transform, With<Camera>>) {
|
fn rotate_cameras(time: Res<Time>, mut query: Query<&mut Transform, With<Camera>>) {
|
||||||
|
@ -155,7 +155,9 @@ fn setup(mut commands: Commands, font: Res<FontHandle>, args: Res<Args>) {
|
|||||||
|
|
||||||
// System for rotating and translating the camera
|
// System for rotating and translating the camera
|
||||||
fn move_camera(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Camera>>) {
|
fn move_camera(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Camera>>) {
|
||||||
let mut camera_transform = camera_query.single_mut();
|
let Ok(mut camera_transform) = camera_query.single_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
camera_transform.rotate_z(time.delta_secs() * 0.5);
|
camera_transform.rotate_z(time.delta_secs() * 0.5);
|
||||||
*camera_transform =
|
*camera_transform =
|
||||||
*camera_transform * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_secs());
|
*camera_transform * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_secs());
|
||||||
|
@ -188,7 +188,7 @@ fn update_text(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(text) = texts.get_single() else {
|
let Ok(text) = texts.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ fn handle_input(
|
|||||||
mut dir: ResMut<ResizeDir>,
|
mut dir: ResMut<ResizeDir>,
|
||||||
example_text: Query<Entity, With<Text>>,
|
example_text: Query<Entity, With<Text>>,
|
||||||
mut writer: TextUiWriter,
|
mut writer: TextUiWriter,
|
||||||
) {
|
) -> Result {
|
||||||
use LeftClickAction::*;
|
use LeftClickAction::*;
|
||||||
if input.just_pressed(KeyCode::KeyA) {
|
if input.just_pressed(KeyCode::KeyA) {
|
||||||
*action = match *action {
|
*action = match *action {
|
||||||
@ -100,7 +100,7 @@ fn handle_input(
|
|||||||
Resize => Nothing,
|
Resize => Nothing,
|
||||||
Nothing => Move,
|
Nothing => Move,
|
||||||
};
|
};
|
||||||
*writer.text(example_text.single(), 4) = format!("{:?}", *action);
|
*writer.text(example_text.single()?, 4) = format!("{:?}", *action);
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.just_pressed(KeyCode::KeyS) {
|
if input.just_pressed(KeyCode::KeyS) {
|
||||||
@ -108,13 +108,15 @@ fn handle_input(
|
|||||||
.0
|
.0
|
||||||
.checked_sub(1)
|
.checked_sub(1)
|
||||||
.unwrap_or(DIRECTIONS.len().saturating_sub(1));
|
.unwrap_or(DIRECTIONS.len().saturating_sub(1));
|
||||||
*writer.text(example_text.single(), 7) = format!("{:?}", DIRECTIONS[dir.0]);
|
*writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.just_pressed(KeyCode::KeyD) {
|
if input.just_pressed(KeyCode::KeyD) {
|
||||||
dir.0 = (dir.0 + 1) % DIRECTIONS.len();
|
dir.0 = (dir.0 + 1) % DIRECTIONS.len();
|
||||||
*writer.text(example_text.single(), 7) = format!("{:?}", DIRECTIONS[dir.0]);
|
*writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_or_resize_windows(
|
fn move_or_resize_windows(
|
||||||
|
@ -39,7 +39,7 @@ fn spawn_player(mut commands: Commands) {
|
|||||||
|
|
||||||
fn spell_casting(mut player: Query<&mut Player>, keyboard_input: Res<ButtonInput<KeyCode>>) {
|
fn spell_casting(mut player: Query<&mut Player>, keyboard_input: Res<ButtonInput<KeyCode>>) {
|
||||||
if keyboard_input.just_pressed(KeyCode::Space) {
|
if keyboard_input.just_pressed(KeyCode::Space) {
|
||||||
let Ok(mut player) = player.get_single_mut() else {
|
let Ok(mut player) = player.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ fn test_player_spawn() {
|
|||||||
// Now that the startup systems have run, we can check if the player has
|
// Now that the startup systems have run, we can check if the player has
|
||||||
// spawned as expected.
|
// spawned as expected.
|
||||||
let expected = Player::default();
|
let expected = Player::default();
|
||||||
let actual = app.world_mut().query::<&Player>().get_single(app.world());
|
let actual = app.world_mut().query::<&Player>().single(app.world());
|
||||||
assert!(actual.is_ok(), "There should be exactly 1 player.");
|
assert!(actual.is_ok(), "There should be exactly 1 player.");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected.mana,
|
expected.mana,
|
||||||
@ -98,7 +98,11 @@ fn test_spell_casting() {
|
|||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
let expected = Player::default();
|
let expected = Player::default();
|
||||||
let actual = app.world_mut().query::<&Player>().single(app.world());
|
let actual = app
|
||||||
|
.world_mut()
|
||||||
|
.query::<&Player>()
|
||||||
|
.single(app.world())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected.mana - 1,
|
expected.mana - 1,
|
||||||
actual.mana,
|
actual.mana,
|
||||||
@ -112,7 +116,11 @@ fn test_spell_casting() {
|
|||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
// No extra spells have been cast, so no mana should have been used.
|
// No extra spells have been cast, so no mana should have been used.
|
||||||
let after_keypress_event = app.world_mut().query::<&Player>().single(app.world());
|
let after_keypress_event = app
|
||||||
|
.world_mut()
|
||||||
|
.query::<&Player>()
|
||||||
|
.single(app.world())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected.mana - 1,
|
expected.mana - 1,
|
||||||
after_keypress_event.mana,
|
after_keypress_event.mana,
|
||||||
@ -127,6 +135,10 @@ fn test_window_title() {
|
|||||||
|
|
||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
let window = app.world_mut().query::<&Window>().single(app.world());
|
let window = app
|
||||||
|
.world_mut()
|
||||||
|
.query::<&Window>()
|
||||||
|
.single(app.world())
|
||||||
|
.unwrap();
|
||||||
assert_eq!(window.title, "This is window 0!");
|
assert_eq!(window.title, "This is window 0!");
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ fn startup(mut cmds: Commands) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_window_mode(mut qry_window: Query<&mut Window>) {
|
fn toggle_window_mode(mut qry_window: Query<&mut Window>) {
|
||||||
let Ok(mut window) = qry_window.get_single_mut() else {
|
let Ok(mut window) = qry_window.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user