Make the Condition trait generic (#8721)

# Objective

The `Condition` trait is only implemented for systems and system
functions that take no input. This can make it awkward to write
conditions that are intended to be used with system piping.

## Solution

Add an `In` generic to the trait. It defaults to `()`.

---

## Changelog

- Made the `Condition` trait generic over system inputs.
This commit is contained in:
JoJoJet 2023-05-31 12:49:46 -04:00 committed by GitHub
parent 5b0f21c773
commit 233b26cc17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -8,13 +8,55 @@ use crate::system::{CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, Syste
use crate::world::unsafe_world_cell::UnsafeWorldCell;
use crate::world::World;
pub type BoxedCondition = Box<dyn ReadOnlySystem<In = (), Out = bool>>;
pub type BoxedCondition<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;
/// A system that determines if one or more scheduled systems should run.
///
/// Implemented for functions and closures that convert into [`System<In=(), Out=bool>`](crate::system::System)
/// Implemented for functions and closures that convert into [`System<Out=bool>`](crate::system::System)
/// with [read-only](crate::system::ReadOnlySystemParam) parameters.
pub trait Condition<Marker>: sealed::Condition<Marker> {
///
/// # Examples
/// A condition that returns true every other time it's called.
/// ```
/// # use bevy_ecs::prelude::*;
/// fn every_other_time() -> impl Condition<()> {
/// IntoSystem::into_system(|mut flag: Local<bool>| {
/// *flag = !*flag;
/// *flag
/// })
/// }
///
/// # #[derive(Resource)] struct DidRun(bool);
/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
/// # let mut schedule = Schedule::new();
/// schedule.add_systems(my_system.run_if(every_other_time()));
/// # let mut world = World::new();
/// # world.insert_resource(DidRun(false));
/// # schedule.run(&mut world);
/// # assert!(world.resource::<DidRun>().0);
/// # world.insert_resource(DidRun(false));
/// # schedule.run(&mut world);
/// # assert!(!world.resource::<DidRun>().0);
/// ```
///
/// A condition that takes a bool as an input and returns it unchanged.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// fn identity() -> impl Condition<(), bool> {
/// IntoSystem::into_system(|In(x)| x)
/// }
///
/// # fn always_true() -> bool { true }
/// # let mut schedule = Schedule::new();
/// # #[derive(Resource)] struct DidRun(bool);
/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
/// schedule.add_systems(my_system.run_if(always_true.pipe(identity())));
/// # let mut world = World::new();
/// # world.insert_resource(DidRun(false));
/// # schedule.run(&mut world);
/// # assert!(world.resource::<DidRun>().0);
pub trait Condition<Marker, In = ()>: sealed::Condition<Marker, In> {
/// Returns a new run condition that only returns `true`
/// if both this one and the passed `and_then` return `true`.
///
@ -59,7 +101,7 @@ pub trait Condition<Marker>: sealed::Condition<Marker> {
/// Note that in this case, it's better to just use the run condition [`resource_exists_and_equals`].
///
/// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals
fn and_then<M, C: Condition<M>>(self, and_then: C) -> AndThen<Self::System, C::System> {
fn and_then<M, C: Condition<M, In>>(self, and_then: C) -> AndThen<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(and_then);
let name = format!("{} && {}", a.name(), b.name());
@ -106,7 +148,7 @@ pub trait Condition<Marker>: sealed::Condition<Marker> {
/// # app.run(&mut world);
/// # assert!(world.resource::<C>().0);
/// ```
fn or_else<M, C: Condition<M>>(self, or_else: C) -> OrElse<Self::System, C::System> {
fn or_else<M, C: Condition<M, In>>(self, or_else: C) -> OrElse<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(or_else);
let name = format!("{} || {}", a.name(), b.name());
@ -114,22 +156,22 @@ pub trait Condition<Marker>: sealed::Condition<Marker> {
}
}
impl<Marker, F> Condition<Marker> for F where F: sealed::Condition<Marker> {}
impl<Marker, In, F> Condition<Marker, In> for F where F: sealed::Condition<Marker, In> {}
mod sealed {
use crate::system::{IntoSystem, ReadOnlySystem};
pub trait Condition<Marker>:
IntoSystem<(), bool, Marker, System = Self::ReadOnlySystem>
pub trait Condition<Marker, In>:
IntoSystem<In, bool, Marker, System = Self::ReadOnlySystem>
{
// This associated type is necessary to let the compiler
// know that `Self::System` is `ReadOnlySystem`.
type ReadOnlySystem: ReadOnlySystem<In = (), Out = bool>;
type ReadOnlySystem: ReadOnlySystem<In = In, Out = bool>;
}
impl<Marker, F> Condition<Marker> for F
impl<Marker, In, F> Condition<Marker, In> for F
where
F: IntoSystem<(), bool, Marker>,
F: IntoSystem<In, bool, Marker>,
F::System: ReadOnlySystem,
{
type ReadOnlySystem = F::System;