Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using System for Trigger #7

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/chase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl Trigger for Near {
// transition for each entity that is in a state that can transition on this trigger. return
// `Ok` to trigger or `Err` to not trigger.
fn trigger(
&self,
&mut self,
entity: Entity,
(transforms, _time): Self::Param<'_, '_>,
) -> Result<f32, f32> {
Expand Down
53 changes: 26 additions & 27 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ use std::{
};

use bevy::{
ecs::system::{Command, EntityCommands, SystemState},
ecs::{system::{Command, EntityCommands, SystemState}, component::Tick},
tasks::{ComputeTaskPool, ParallelSliceMut},
utils::HashMap,
};

use crate::{
prelude::*,
set::StateSet,
state::{Insert, OnEvent},
state::{Insert, OnEvent}, trigger::TriggerSystemFunction,
};

pub(crate) fn machine_plugin(app: &mut App) {
Expand All @@ -32,71 +32,70 @@ trait Transition: Debug + Send + Sync + 'static {
/// An edge in the state machine. The type parameters are the [`Trigger`] that causes this
/// transition, the previous state the function that takes the trigger's output and builds the next
/// state, and the next state itself.
struct TransitionImpl<Trig, Prev, Build, Next>
struct TransitionImpl<Ok, Err, Prev, Build, Next>
where
Trig: Trigger,
Prev: MachineState,
Build: 'static + Fn(&Prev, Trig::Ok) -> Option<Next> + Send + Sync,
Build: 'static + Fn(&Prev, Ok) -> Option<Next> + Send + Sync,
Next: Component + MachineState,
{
pub trigger: Trig,
pub trigger: Box<dyn ReadOnlySystem<In=Entity, Out=Result<Ok, Err>>>,
pub builder: Build,
// To run this, we need a [`SystemState`]. We can't initialize that until we have a [`World`],
// so it starts out empty
system_state: Option<SystemState<Trig::Param<'static, 'static>>>,
phantom: PhantomData<Prev>,
}

impl<Trig, Prev, Build, Next> Debug for TransitionImpl<Trig, Prev, Build, Next>
impl<Ok, Err, Prev, Build, Next> Debug for TransitionImpl<Ok, Err, Prev, Build, Next>
where
Trig: Trigger,
Ok: 'static,
Err: 'static,
Prev: MachineState,
Build: Fn(&Prev, Trig::Ok) -> Option<Next> + Send + Sync,
Build: Fn(&Prev, Ok) -> Option<Next> + Send + Sync,
Next: Component + MachineState,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TransitionImpl")
.field("trigger", &self.trigger.type_id())
.field("builder", &self.builder.type_id())
.field("system_state", &self.system_state.type_id())
.field("phantom", &self.phantom)
.finish()
}
}

impl<Trig, Prev, Build, Next> Transition for TransitionImpl<Trig, Prev, Build, Next>
impl<Ok, Err, Prev, Build, Next> Transition for TransitionImpl<Ok, Err, Prev, Build, Next>
where
Trig: Trigger,
Ok: 'static,
Err: 'static,
Prev: MachineState,
Build: Fn(&Prev, Trig::Ok) -> Option<Next> + Send + Sync,
Build: Fn(&Prev, Ok) -> Option<Next> + Send + Sync,
Next: Component + MachineState,
{
fn init(&mut self, world: &mut World) {
if self.system_state.is_none() {
self.system_state = Some(SystemState::new(world));
if self.trigger.get_last_run() == Tick::new(0) {
self.trigger.initialize(world);
}
}

fn run(&mut self, world: &World, entity: Entity) -> Option<(Box<dyn Insert>, TypeId)> {
let state = self.system_state.as_mut().unwrap();
let Ok(res) = self.trigger.trigger(entity, state.get(world)) else { return None };
let Ok(res) = self.trigger.run_readonly(entity, world) else { return None };
(self.builder)(Prev::from_entity(entity, world), res)
.map(|state| (Box::new(state) as Box<dyn Insert>, TypeId::of::<Next>()))
}
}

impl<Trig, Prev, Build, Next> TransitionImpl<Trig, Prev, Build, Next>
impl<Ok, Err, Prev, Build, Next> TransitionImpl<Ok, Err, Prev, Build, Next>
where
Trig: Trigger,
Ok: 'static,
Err: 'static,
Prev: MachineState,
Build: Fn(&Prev, Trig::Ok) -> Option<Next> + Send + Sync,
Build: Fn(&Prev, Ok) -> Option<Next> + Send + Sync,
Next: Component + MachineState,
{
pub fn new(trigger: Trig, builder: Build) -> Self {
pub fn new<T>(trigger: T, builder: Build) -> Self
where
T: Trigger<Ok=Ok, Err=Err>,
{
Self {
trigger,
trigger: Box::new(IntoSystem::into_system(TriggerSystemFunction(trigger))),
builder,
system_state: None,
phantom: PhantomData,
}
}
Expand Down Expand Up @@ -190,7 +189,7 @@ impl StateMachine {
) -> Self {
self.metadata_mut::<Prev>();
self.metadata_mut::<Next>();
let transition = TransitionImpl::<_, Prev, _, _>::new(trigger, builder);
let transition = TransitionImpl::<_, _, Prev, _, _>::new(trigger, builder);
self.transitions.push((
TypeId::of::<Prev>(),
Box::new(transition) as Box<dyn Transition>,
Expand Down
77 changes: 70 additions & 7 deletions src/trigger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub use input::{

use std::{any::type_name, convert::Infallible, fmt::Debug, marker::PhantomData};

use bevy::ecs::system::{ReadOnlySystemParam, SystemParam};
use bevy::ecs::system::{ReadOnlySystemParam, SystemParam, SystemParamItem};

use crate::{prelude::*, set::StateSet};

Expand All @@ -35,6 +35,69 @@ pub struct Never {
never: Infallible,
}

/// Wrapper for [`Trigger`], implements [`SystemParamFunction`].
pub struct TriggerSystemFunction<Trig: Trigger>(pub Trig);
impl<Trig: Trigger> SystemParamFunction<
fn(_: Entity, _: SystemParamItem<'static, 'static, Trig::Param<'static, 'static>>) -> Result<Trig::Ok, Trig::Err>
> for TriggerSystemFunction<Trig> {
type In = Entity;
type Out = Result<Trig::Ok, Trig::Err>;
type Param = Trig::Param<'static, 'static>;
fn run(&mut self, entity: Self::In, param_value: SystemParamItem<Self::Param>) -> Self::Out {
self.0.trigger(entity, param_value)
}
}
impl<Trig: Trigger> From<Trig> for TriggerSystemFunction<Trig> {
fn from(value: Trig) -> Self {
Self(value)
}
}

/// Wrapper for [`SystemParamFunction`], implements [`Trigger`].
pub struct SystemFunctionTrigger<F, Marker, Ok, Err>
where
F: SystemParamFunction<Marker, In=Entity, Out=Result<Ok, Err>>,
<F as SystemParamFunction<Marker>>::Param : ReadOnlySystemParam,
Marker: 'static + Send + Sync,
Ok: 'static + Send + Sync,
Err: 'static + Send + Sync,
{
func: F,
_phantom: PhantomData<Marker>,
}
impl<F, Marker, Ok, Err> SystemFunctionTrigger<F, Marker, Ok, Err>
where
F: SystemParamFunction<Marker, In=Entity, Out=Result<Ok, Err>>,
<F as SystemParamFunction<Marker>>::Param : ReadOnlySystemParam,
Marker: 'static + Send + Sync,
Ok: 'static + Send + Sync,
Err: 'static + Send + Sync,
{
/// Creates new [`SystemFunctionTrigger`]
pub fn new(func: F) -> Self {
Self { func, _phantom: PhantomData }
}
}
impl<F, Marker: 'static, Ok, Err> Trigger for SystemFunctionTrigger<F, Marker, Ok, Err>
where
F: SystemParamFunction<Marker, In=Entity, Out=Result<Ok, Err>>,
<F as SystemParamFunction<Marker>>::Param : ReadOnlySystemParam,
Marker: 'static + Send + Sync,
Ok: 'static + Send + Sync,
Err: 'static + Send + Sync,
{
type Ok = Ok;
type Err = Err;
type Param<'w, 's> = <F as SystemParamFunction<Marker>>::Param;
fn trigger(
&mut self,
entity: Entity,
param: <<Self as Trigger>::Param<'_, '_> as SystemParam>::Item<'_, '_>,
) -> Result<Self::Ok, Self::Err> {
self.func.run(entity, param)
}
}

/// Types that implement this may be used in [`StateMachine`]s to transition from one state to
/// another. Look at an example for implementing this trait, since it can be tricky.
pub trait Trigger: 'static + Send + Sized + Sync {
Expand All @@ -55,7 +118,7 @@ pub trait Trigger: 'static + Send + Sized + Sync {
/// should transition, and `Err` if it should not. In most cases, you may use
/// `&Self::Param<'_, '_>` as `param`'s type.
fn trigger(
&self,
&mut self,
entity: Entity,
param: <<Self as Trigger>::Param<'_, '_> as SystemParam>::Item<'_, '_>,
) -> Result<Self::Ok, Self::Err>;
Expand Down Expand Up @@ -107,7 +170,7 @@ impl<T: OptionTrigger> Trigger for T {
type Err = ();

fn trigger(
&self,
&mut self,
entity: Entity,
param: <<Self as Trigger>::Param<'_, '_> as SystemParam>::Item<'_, '_>,
) -> Result<Self::Ok, ()> {
Expand Down Expand Up @@ -153,7 +216,7 @@ impl Trigger for AlwaysTrigger {
type Ok = ();
type Err = Never;

fn trigger(&self, _: Entity, _: ()) -> Result<(), Never> {
fn trigger(&mut self, _: Entity, _: ()) -> Result<(), Never> {
Ok(())
}
}
Expand All @@ -168,7 +231,7 @@ impl<T: Trigger> Trigger for NotTrigger<T> {
type Err = T::Ok;

fn trigger(
&self,
&mut self,
entity: Entity,
param: <<Self as Trigger>::Param<'_, '_> as SystemParam>::Item<'_, '_>,
) -> Result<T::Err, T::Ok> {
Expand All @@ -190,7 +253,7 @@ impl<T: Trigger, U: Trigger> Trigger for AndTrigger<T, U> {
type Err = Either<T::Err, U::Err>;

fn trigger(
&self,
&mut self,
entity: Entity,
(param1, param2): <<Self as Trigger>::Param<'_, '_> as SystemParam>::Item<'_, '_>,
) -> Result<(T::Ok, U::Ok), Either<T::Err, U::Err>> {
Expand All @@ -212,7 +275,7 @@ impl<T: Trigger, U: Trigger> Trigger for OrTrigger<T, U> {
type Err = (T::Err, U::Err);

fn trigger(
&self,
&mut self,
entity: Entity,
(param1, param2): <<Self as Trigger>::Param<'_, '_> as SystemParam>::Item<'_, '_>,
) -> Result<Either<T::Ok, U::Ok>, (T::Err, U::Err)> {
Expand Down
Loading