AAMZTMKZ5IFWOTPGR3Q57QO6HJUIAXRU4R4NT276A4O4VTL42KEQC
}
#[derive(Debug)]
struct MoveTo {
part: f32,
goal: Vec2,
}
impl BehaviorNode for MoveTo {
fn tick(&self, blackboard: &mut Blackboard, _commands: &mut Commands) -> NodeResult {
let Some(BlackboardValue::Point(position)) = blackboard.get_mut("position") else {
return NodeResult::Failure;
};
let movement = (self.goal - *position) * self.part.recip();
*position += movement;
const ERROR: f32 = 0.001;
if (self.goal - *position).length() < ERROR {
NodeResult::Success
} else {
NodeResult::Running(
Self {
part: self.part,
goal: self.goal,
}
.into(),
)
}
}
}
#[test]
fn test_seequence() {
let (mut cq, world) = create_testing_state();
let mut commands = Commands::new(&mut cq, &world);
let tree1 = MoveTo {
part: 5.0,
goal: Vec2::ZERO,
}
.arc();
let tree2 = [
MoveTo {
part: 5.0,
goal: Vec2::ZERO,
}
.arc(),
MoveTo {
part: 5.0,
goal: Vec2::splat(5.0),
}
.arc(),
]
.into_iter()
.collect::<Sequence>()
.arc();
// With no info, the node should fail
{
let mut runner1 = BehaviorRunner::new(tree1.clone());
check!(!runner1.is_running());
check!(runner1.proceed(&mut commands) == Some(false));
let mut runner2 = BehaviorRunner::new(tree2.clone());
check!(!runner2.is_running());
check!(runner2.proceed(&mut commands) == Some(false));
}
// Set some position
let starting_position = Vec2::X * 5.0;
// If a position is set, we will execute the action
{
let mut runner1 = BehaviorRunner::new(tree1.clone());
runner1
.blackboard_mut()
.set("position", BlackboardValue::into_point(starting_position));
check!(runner1.proceed(&mut commands) == None);
while let Some(BlackboardValue::Point(_)) = runner1.blackboard().get("position") {
if let Some(res) = runner1.proceed(&mut commands) {
// we should succeed
check!(res);
break;
}
}
let mut runner2 = BehaviorRunner::new(tree2.clone());
runner2
.blackboard_mut()
.set("position", BlackboardValue::into_point(starting_position));
while let Some(BlackboardValue::Point(pos)) = runner2.blackboard().get("position") {
eprintln!("position = {pos:?}\nrunner = {runner2:?}");
if let Some(res) = runner2.proceed(&mut commands) {
// we should succeed
check!(res);
break;
}
}
}
use std::sync::Arc;
use super::{BehaviorArc, BehaviorNode, NodeResult};
pub struct Sequence {
sub: Arc<[Arc<dyn BehaviorNode>]>,
}
impl std::fmt::Debug for Sequence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("Sequence<{:p}>", self.sub.as_ref()))
.field("sub", &self.sub)
.finish()
}
}
impl<I: Into<Arc<dyn BehaviorNode>>> FromIterator<I> for Sequence {
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
Self {
sub: Arc::from(iter.into_iter().map(Into::into).collect::<Vec<_>>()),
}
}
}
impl Sequence {
fn resume(seq: Arc<[Arc<dyn BehaviorNode>]>, index: usize, resume: BehaviorArc) -> BehaviorArc {
SequenceResume { seq, resume, index }.into()
}
}
impl BehaviorNode for Sequence {
fn tick(
&self,
blackboard: &mut super::Blackboard,
commands: &mut bevy::prelude::Commands,
) -> NodeResult {
for (idx, sub) in self.sub.iter().enumerate() {
match sub.tick(blackboard, commands) {
NodeResult::Success => {}
NodeResult::Failure => return NodeResult::Failure,
NodeResult::Running(resume) => {
return NodeResult::Running(Self::resume(self.sub.clone(), idx, resume))
}
}
}
NodeResult::Success
}
}
struct SequenceResume {
seq: Arc<[Arc<dyn BehaviorNode>]>,
resume: BehaviorArc,
index: usize,
}
impl std::fmt::Debug for SequenceResume {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("SequenceResume<{:p}>", self.seq.as_ref()))
.field("resume", &self.resume)
.field("index", &self.index)
.finish_non_exhaustive()
}
}
impl BehaviorNode for SequenceResume {
fn tick(
&self,
blackboard: &mut super::Blackboard,
commands: &mut bevy::prelude::Commands,
) -> NodeResult {
// Tick the node we want to resume on
match self.resume.0.tick(blackboard, commands) {
NodeResult::Success => {}
NodeResult::Failure => return NodeResult::Failure,
NodeResult::Running(resume) => {
return NodeResult::Running(Sequence::resume(self.seq.clone(), self.index, resume))
}
}
for (idx, sub) in self.seq.iter().enumerate().skip(self.index) {
match sub.tick(blackboard, commands) {
NodeResult::Success => {}
NodeResult::Failure => return NodeResult::Failure,
NodeResult::Running(resume) => {
return NodeResult::Running(Sequence::resume(self.seq.clone(), idx, resume))
}
}
}
NodeResult::Success
}
}
pub mod inverter;
pub mod selector;
pub mod sequence;
use crate::simple_bt::{BehaviorArc, BehaviorNode, Blackboard, NodeResult};
use std::sync::Arc;
pub struct Sequence {
pub(crate) sub: Arc<[Arc<dyn BehaviorNode>]>,
}
impl std::fmt::Debug for Sequence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("Sequence<{:p}>", self.sub.as_ref()))
.field("sub", &self.sub)
.finish()
}
}
impl<I: Into<Arc<dyn BehaviorNode>>> FromIterator<I> for Sequence {
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
Self {
sub: Arc::from(iter.into_iter().map(Into::into).collect::<Vec<_>>()),
}
}
}
impl Sequence {
pub(crate) fn resume(
seq: Arc<[Arc<dyn BehaviorNode>]>,
index: usize,
resume: BehaviorArc,
) -> BehaviorArc {
SequenceResume { seq, resume, index }.into()
}
}
impl BehaviorNode for Sequence {
fn tick(
&self,
blackboard: &mut Blackboard,
commands: &mut bevy::prelude::Commands,
) -> NodeResult {
for (idx, sub) in self.sub.iter().enumerate() {
match sub.tick(blackboard, commands) {
NodeResult::Success => {}
NodeResult::Failure => return NodeResult::Failure,
NodeResult::Running(resume) => {
return NodeResult::Running(Self::resume(self.sub.clone(), idx, resume))
}
}
}
NodeResult::Success
}
}
pub(crate) struct SequenceResume {
pub(crate) seq: Arc<[Arc<dyn BehaviorNode>]>,
pub(crate) resume: BehaviorArc,
pub(crate) index: usize,
}
impl std::fmt::Debug for SequenceResume {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("SequenceResume<{:p}>", self.seq.as_ref()))
.field("resume", &self.resume)
.field("index", &self.index)
.finish_non_exhaustive()
}
}
impl BehaviorNode for SequenceResume {
fn tick(
&self,
blackboard: &mut Blackboard,
commands: &mut bevy::prelude::Commands,
) -> NodeResult {
// Tick the node we want to resume on
match self.resume.0.tick(blackboard, commands) {
NodeResult::Success => {}
NodeResult::Failure => return NodeResult::Failure,
NodeResult::Running(resume) => {
return NodeResult::Running(Sequence::resume(self.seq.clone(), self.index, resume))
}
}
for (idx, sub) in self.seq.iter().enumerate().skip(self.index) {
match sub.tick(blackboard, commands) {
NodeResult::Success => {}
NodeResult::Failure => return NodeResult::Failure,
NodeResult::Running(resume) => {
return NodeResult::Running(Sequence::resume(self.seq.clone(), idx, resume))
}
}
}
NodeResult::Success
}
}
#[cfg(test)]
mod tests {
use super::{BehaviorNode, NodeResult, Sequence};
use crate::simple_bt::{
tests::create_testing_state, BehaviorRunner, Blackboard, BlackboardValue,
};
use assert2::check;
use bevy::prelude::*;
#[derive(Debug)]
struct MoveTo {
part: f32,
goal: Vec2,
}
impl BehaviorNode for MoveTo {
fn tick(&self, blackboard: &mut Blackboard, _commands: &mut Commands) -> NodeResult {
let Some(BlackboardValue::Point(position)) = blackboard.get_mut("position") else {
return NodeResult::Failure;
};
let movement = (self.goal - *position) * self.part.recip();
*position += movement;
const ERROR: f32 = 0.001;
if (self.goal - *position).length() < ERROR {
NodeResult::Success
} else {
NodeResult::Running(
Self {
part: self.part,
goal: self.goal,
}
.into(),
)
}
}
}
#[test]
fn test_seequence() {
let (mut cq, world) = create_testing_state();
let mut commands = Commands::new(&mut cq, &world);
let tree1 = MoveTo {
part: 5.0,
goal: Vec2::ZERO,
}
.arc();
let tree2 = [
MoveTo {
part: 5.0,
goal: Vec2::ZERO,
}
.arc(),
MoveTo {
part: 5.0,
goal: Vec2::splat(5.0),
}
.arc(),
]
.into_iter()
.collect::<Sequence>()
.arc();
// With no info, the node should fail
{
let mut runner1 = BehaviorRunner::new(tree1.clone());
check!(!runner1.is_running());
check!(runner1.proceed(&mut commands) == Some(false));
let mut runner2 = BehaviorRunner::new(tree2.clone());
check!(!runner2.is_running());
check!(runner2.proceed(&mut commands) == Some(false));
}
// Set some position
let starting_position = Vec2::X * 5.0;
// If a position is set, we will execute the action
{
let mut runner1 = BehaviorRunner::new(tree1.clone());
runner1
.blackboard_mut()
.set("position", BlackboardValue::into_point(starting_position));
check!(runner1.proceed(&mut commands) == None);
while let Some(BlackboardValue::Point(_)) = runner1.blackboard().get("position") {
if let Some(res) = runner1.proceed(&mut commands) {
// we should succeed
check!(res);
break;
}
}
let mut runner2 = BehaviorRunner::new(tree2.clone());
runner2
.blackboard_mut()
.set("position", BlackboardValue::into_point(starting_position));
while let Some(BlackboardValue::Point(pos)) = runner2.blackboard().get("position") {
eprintln!("position = {pos:?}\nrunner = {runner2:?}");
if let Some(res) = runner2.proceed(&mut commands) {
// we should succeed
check!(res);
break;
}
}
}
}
}
use crate::simple_bt::{BehaviorArc, BehaviorNode, Blackboard, NodeResult};
use std::sync::Arc;
pub struct Selector {
pub(crate) sub: Arc<[Arc<dyn BehaviorNode>]>,
}
impl std::fmt::Debug for Selector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("Selector<{:p}>", self.sub.as_ref()))
.field("sub", &self.sub)
.finish()
}
}
impl<I: Into<Arc<dyn BehaviorNode>>> FromIterator<I> for Selector {
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
Self {
sub: Arc::from(iter.into_iter().map(Into::into).collect::<Vec<_>>()),
}
}
}
impl Selector {
pub(crate) fn resume(
seq: Arc<[Arc<dyn BehaviorNode>]>,
index: usize,
resume: BehaviorArc,
) -> BehaviorArc {
SelectorResume { seq, resume, index }.into()
}
}
impl BehaviorNode for Selector {
fn tick(
&self,
blackboard: &mut Blackboard,
commands: &mut bevy::prelude::Commands,
) -> NodeResult {
for (idx, sub) in self.sub.iter().enumerate() {
match sub.tick(blackboard, commands) {
NodeResult::Failure => {}
NodeResult::Success => return NodeResult::Success,
NodeResult::Running(resume) => {
return NodeResult::Running(Self::resume(self.sub.clone(), idx, resume))
}
}
}
NodeResult::Failure
}
}
pub(crate) struct SelectorResume {
pub(crate) seq: Arc<[Arc<dyn BehaviorNode>]>,
pub(crate) resume: BehaviorArc,
pub(crate) index: usize,
}
impl std::fmt::Debug for SelectorResume {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("SelectorResume<{:p}>", self.seq.as_ref()))
.field("resume", &self.resume)
.field("index", &self.index)
.finish_non_exhaustive()
}
}
impl BehaviorNode for SelectorResume {
fn tick(
&self,
blackboard: &mut Blackboard,
commands: &mut bevy::prelude::Commands,
) -> NodeResult {
// Tick the node we want to resume on
match self.resume.0.tick(blackboard, commands) {
NodeResult::Failure => {}
NodeResult::Success => return NodeResult::Success,
NodeResult::Running(resume) => {
return NodeResult::Running(Selector::resume(self.seq.clone(), self.index, resume))
}
}
for (idx, sub) in self.seq.iter().enumerate().skip(self.index) {
match sub.tick(blackboard, commands) {
NodeResult::Failure => {}
NodeResult::Success => return NodeResult::Success,
NodeResult::Running(resume) => {
return NodeResult::Running(Selector::resume(self.seq.clone(), idx, resume))
}
}
}
NodeResult::Failure
}
}
use crate::simple_bt::{BehaviorArc, BehaviorNode, Blackboard, NodeResult};
use std::sync::Arc;
#[derive(Debug)]
pub struct Inverter {
child: Arc<dyn BehaviorNode>,
}
impl Inverter {
pub fn new(child: Arc<dyn BehaviorNode>) -> Self {
Self { child }
}
}
impl From<BehaviorArc> for Inverter {
fn from(value: BehaviorArc) -> Self {
Self {
child: value.0.clone(),
}
}
}
impl BehaviorNode for Inverter {
fn tick(
&self,
blackboard: &mut Blackboard,
commands: &mut bevy::prelude::Commands,
) -> crate::simple_bt::NodeResult {
match self.child.tick(blackboard, commands) {
NodeResult::Success => NodeResult::Failure,
NodeResult::Failure => NodeResult::Success,
NodeResult::Running(resume) => NodeResult::Running(Inverter::from(resume).into()),
}
}
}
#[cfg(test)]
mod tests {}