use core::fmt;
use simple_bt::{
composite::ParallelSelector, BehaviorNode, BehaviorRunner, NodeResult, ReportStatus,
};
struct Game {
pos: glam::Vec2,
dt: std::time::Duration,
}
impl fmt::Debug for Game {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Game")
.field("pos", &self.pos)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
struct AtPoint {
tolerance: f32,
point: glam::Vec2,
}
impl BehaviorNode<Game> for AtPoint {
fn tick(self: std::sync::Arc<Self>, context: &mut Game) -> simple_bt::NodeResult<Game> {
if context.pos.distance_squared(self.point) <= self.tolerance.powi(2) {
NodeResult::Success
} else {
NodeResult::Failure
}
}
fn status(&self, parent: ReportStatus, context: &Game) -> simple_bt::BehaviorStatus {
simple_bt::BehaviorStatus {
name: "AtPoint".into(),
status: parent.unexecuted_or_else(|| {
if context.pos.distance_squared(self.point) <= self.tolerance.powi(2) {
ReportStatus::Success
} else {
ReportStatus::Failure
}
}),
children: vec![],
current: None,
}
}
}
struct Move {
speed_func: Box<dyn Fn(&Game) -> f32 + Send + Sync>,
toward: glam::Vec2,
}
impl fmt::Debug for Move {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Move").finish_non_exhaustive()
}
}
impl BehaviorNode<Game> for Move {
fn tick(self: std::sync::Arc<Self>, context: &mut Game) -> NodeResult<Game> {
let speed = (self.speed_func)(context);
context.pos +=
(self.toward - context.pos).normalize_or_zero() * speed * context.dt.as_secs_f32();
NodeResult::Running(self)
}
fn status(&self, parent: ReportStatus, _context: &Game) -> simple_bt::BehaviorStatus {
simple_bt::BehaviorStatus {
name: "Move".into(),
status: parent.unexecuted_or(ReportStatus::Running),
children: vec![],
current: None,
}
}
}
fn main() {
const FRAME_TIME: std::time::Duration = std::time::Duration::from_millis(16);
let new_game = Game {
pos: glam::Vec2::from_array([
rand::random_range(-100.0..=100.0),
rand::random_range(-100.0..=100.0),
]),
dt: FRAME_TIME,
};
game(new_game, [100.0, 100.0].into())
}
fn game(mut game: Game, goal: glam::Vec2) {
let goal_tree = ParallelSelector::<Game>::from_iter([
AtPoint {
tolerance: 1.0,
point: goal,
}
.arc(),
Move {
speed_func: Box::new(|game| game.pos.length() + 1.0),
toward: goal,
}
.arc(),
])
.arc();
let mut runner = BehaviorRunner::new(goal_tree.clone());
eprintln!(
"Initial state: {game:?} (status: {:?})",
runner.status(&game)
);
while runner.proceed(&mut game).is_none() {
eprintln!("Runner status: {:#?}", runner.status(&game));
eprintln!("Intermediate state (after {:?}): {game:?}", game.dt,);
}
eprintln!("Final state: {game:?}");
}