From 299f7a815bd882131477f29660f44d0c47c73374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= <kzm@zkat.tech> Date: Tue, 13 Apr 2021 09:08:25 -0700 Subject: [PATCH] support adding Thinkers as plain components Fixes: https://github.com/zkat/big-brain/issues/13 --- README.md | 16 ++++++++-------- examples/thirst.rs | 21 +++++++++------------ src/lib.rs | 2 ++ src/thinker.rs | 45 +++++++++++++++++++++++++++++++++++++++------ 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index efd6620..90b5ab2 100644 --- a/README.md +++ b/README.md @@ -104,16 +104,16 @@ fn drink_action_system( ### Thinker Definition -Finally, you can use it when define the `Thinker`: +Finally, you can use it when define the `Thinker`, which you can attach as a +regular Component: ```rust -let actor = cmd.spawn().insert(Thirst::new(70.0, 2.0)).id(); - -let thinker = Thinker::build() - .picker(FirstToScore { threshold: 80.0 }) - .when(Thirsty::build(), Drink::build()) - .attach(&mut cmd, actor); -cmd.entity(actor).push_children(&[thinker]); +cmd.spawn().insert(Thirst::new(70.0, 2.0)).insert( + Thinker::build() + .picker(FirstToScore { threshold: 80.0 }) + .when(Thirsty::build(), Drink::build()) + .component(), +); ``` ## License diff --git a/examples/thirst.rs b/examples/thirst.rs index 062eeca..0f3b5d7 100644 --- a/examples/thirst.rs +++ b/examples/thirst.rs @@ -150,18 +150,15 @@ pub fn thirsty_scorer_system( // to have AI behavior should have one *or more* Thinkers attached to it. pub fn init_entities(mut cmd: Commands) { // Create the entity and throw the Thirst component in there. Nothing special here. - let actor = cmd.spawn().insert(Thirst::new(70.0, 2.0)).id(); - - // And finally, we put all the pieces together! - let thinker = Thinker::build() - .picker(FirstToScore { threshold: 80.0 }) - // Note that what we pass in are _builders_, not components! - .when(Thirsty::build(), Drink::build()) - // .attach will do all the necessary work of attaching this component - // and hooking it up to the AI system. - .attach(&mut cmd, actor); - // TODO: this is a footgun and a pita. Please ignore. - cmd.entity(actor).push_children(&[thinker]); + cmd.spawn().insert(Thirst::new(70.0, 2.0)).insert( + // Thinker::build().component() will return a regular component you + // can attach normally! + Thinker::build() + .picker(FirstToScore { threshold: 80.0 }) + // Note that what we pass in are _builders_, not components! + .when(Thirsty::build(), Drink::build()) + .component(), + ); } fn main() { diff --git a/src/lib.rs b/src/lib.rs index c7b7de9..d184f7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ pub struct BigBrainPlugin; impl Plugin for BigBrainPlugin { fn build(&self, app: &mut AppBuilder) { app.add_system(thinker_system.system()); + app.add_system(thinker_component_attach_system.system()); + app.add_system(thinker_component_detach_system.system()); app.add_system(steps_system.system()); app.add_system(fixed_score_system.system()); app.add_system(all_or_nothing_system.system()); diff --git a/src/thinker.rs b/src/thinker.rs index a9ec2df..e00918b 100644 --- a/src/thinker.rs +++ b/src/thinker.rs @@ -51,24 +51,29 @@ impl ThinkerBuilder { } } - pub fn picker(&mut self, picker: impl Picker + 'static) -> &mut Self { + pub fn picker(mut self, picker: impl Picker + 'static) -> Self { self.picker = Some(Arc::new(picker)); self } pub fn when( - &mut self, + mut self, scorer: impl ScorerBuilder + 'static, action: impl ActionBuilder + 'static, - ) -> &mut Self { - self.choices.push(ChoiceBuilder::new(Arc::new(scorer), Arc::new(action))); + ) -> Self { + self.choices + .push(ChoiceBuilder::new(Arc::new(scorer), Arc::new(action))); self } - pub fn otherwise(&mut self, otherwise: impl ActionBuilder + 'static) -> &mut Self { + pub fn otherwise(mut self, otherwise: impl ActionBuilder + 'static) -> Self { self.otherwise = Some(ActionBuilderWrapper::new(Arc::new(otherwise))); self } + + pub fn component(self) -> ThinkerComponent { + ThinkerComponent(self) + } } impl ActionBuilder for ThinkerBuilder { @@ -95,6 +100,34 @@ impl ActionBuilder for ThinkerBuilder { } } +#[derive(Debug)] +pub struct ThinkerComponent(ThinkerBuilder); + +pub fn thinker_component_attach_system( + mut cmd: Commands, + q: Query<(Entity, &ThinkerComponent), Without<HasThinker>>, +) { + for (entity, thinker_comp) in q.iter() { + let thinker = thinker_comp.0.attach(&mut cmd, entity); + cmd.entity(entity) + .insert(HasThinker(thinker)) + .push_children(&[thinker]); + } +} + +pub fn thinker_component_detach_system( + mut cmd: Commands, + q: Query<(Entity, &HasThinker), Without<ThinkerComponent>>, +) { + for (actor, HasThinker(thinker)) in q.iter() { + cmd.entity(*thinker).despawn_recursive(); + cmd.entity(actor).remove::<HasThinker>(); + } +} + +#[derive(Debug)] +pub struct HasThinker(Entity); + #[derive(Debug)] pub struct ActiveThinker(bool); @@ -192,7 +225,7 @@ pub fn thinker_system( actions::ActionState::Init | actions::ActionState::Success | actions::ActionState::Failure => { - cmd.entity(current.0.0).despawn_recursive(); + cmd.entity(current.0 .0).despawn_recursive(); thinker.current_action = None; } _ => { -- GitLab