Skip to main content

fyrox_impl/utils/behavior/
mod.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21#![warn(missing_docs)]
22
23//! Everything related to AI behavior and behavior trees.
24//!
25//! Behavior trees are simple but very powerful mechanism to implement artificial intelligence for
26//! games. The main concept is in its name. Tree is a set of connected nodes, where each node could
27//! have single parent and zero or more children nodes. Execution path of the tree is defined by the
28//! actions of the nodes. Behavior tree has a set of hard coded nodes as well as leaf nodes with
29//! user-defined logic. Hard coded nodes are: Sequence, Selector, Leaf. Leaf is special - it has
30//! custom method `tick` that can contain any logic you want.
31//!
32//! For more info see:
33//! - [Wikipedia article](https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control))
34//! - [Gamasutra](https://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php)
35
36use crate::plugin::error::GameError;
37use crate::{
38    core::{
39        pool::{Handle, Pool},
40        visitor::prelude::*,
41    },
42    utils::behavior::{
43        composite::{CompositeNode, CompositeNodeKind},
44        inverter::Inverter,
45        leaf::LeafNode,
46    },
47};
48use fyrox_core::pool::PoolError;
49use std::{
50    fmt::Debug,
51    ops::{Index, IndexMut},
52};
53
54pub mod composite;
55pub mod inverter;
56pub mod leaf;
57
58/// Status of execution of behavior tree node.
59pub enum Status {
60    /// Action was successful.
61    Success,
62    /// Failed to perform an action.
63    Failure,
64    /// Need another iteration to perform an action.
65    Running,
66}
67
68/// Base trait for all behaviors. This trait has auto-impl.
69pub trait BaseBehavior: Visit + Default + PartialEq + Debug + Clone + 'static {}
70impl<T: Visit + Default + PartialEq + Debug + Clone + 'static> BaseBehavior for T {}
71
72/// A trait for user-defined actions for behavior tree.
73pub trait Behavior<'a>: BaseBehavior {
74    /// A context in which the behavior will be performed.
75    type Context;
76
77    /// A function that will be called each frame depending on
78    /// the current execution path of the behavior tree it belongs
79    /// to.
80    fn tick(&mut self, context: &mut Self::Context) -> Result<Status, GameError>;
81}
82
83/// Root node of the tree.
84#[derive(Debug, PartialEq, Visit, Eq, Clone)]
85pub struct RootNode<B>
86where
87    B: BaseBehavior,
88{
89    child: Handle<BehaviorNode<B>>,
90}
91
92impl<B> Default for RootNode<B>
93where
94    B: BaseBehavior,
95{
96    fn default() -> Self {
97        Self {
98            child: Default::default(),
99        }
100    }
101}
102
103/// Possible variations of behavior nodes.
104#[derive(Debug, PartialEq, Visit, Eq, Clone, Default)]
105pub enum BehaviorNode<B>
106where
107    B: BaseBehavior,
108{
109    #[doc(hidden)]
110    #[default]
111    Unknown,
112    /// Root node of the tree.
113    Root(RootNode<B>),
114    /// Composite (sequence or selector) node of the tree.
115    Composite(CompositeNode<B>),
116    /// A node with custom logic.
117    Leaf(LeafNode<B>),
118    /// A node, that inverts its child state ([`Status::Failure`] becomes [`Status::Success`] and vice versa, [`Status::Running`] remains
119    /// unchanged)
120    Inverter(Inverter<B>),
121}
122
123/// See module docs.
124#[derive(Debug, PartialEq, Visit, Clone)]
125pub struct BehaviorTree<B>
126where
127    B: BaseBehavior,
128{
129    nodes: Pool<BehaviorNode<B>>,
130    root: Handle<BehaviorNode<B>>,
131}
132
133impl<B> Default for BehaviorTree<B>
134where
135    B: BaseBehavior,
136{
137    fn default() -> Self {
138        Self {
139            nodes: Default::default(),
140            root: Default::default(),
141        }
142    }
143}
144
145impl<B> BehaviorTree<B>
146where
147    B: BaseBehavior,
148{
149    /// Creates new behavior tree with single root node.
150    pub fn new() -> Self {
151        let mut nodes = Pool::new();
152        let root = nodes.spawn(BehaviorNode::Root(RootNode {
153            child: Default::default(),
154        }));
155        Self { nodes, root }
156    }
157
158    /// Adds a node to the tree, returns its handle.
159    pub fn add_node(&mut self, node: BehaviorNode<B>) -> Handle<BehaviorNode<B>> {
160        self.nodes.spawn(node)
161    }
162
163    /// Sets entry nodes of the tree. Execution will start from this node.
164    pub fn set_entry_node(&mut self, entry: Handle<BehaviorNode<B>>) {
165        if let BehaviorNode::Root(root) = &mut self.nodes[self.root] {
166            root.child = entry;
167        } else {
168            unreachable!("must be root")
169        }
170    }
171
172    fn tick_recursive<'a, Ctx>(
173        &self,
174        handle: Handle<BehaviorNode<B>>,
175        context: &mut Ctx,
176    ) -> Result<Status, GameError>
177    where
178        B: Behavior<'a, Context = Ctx>,
179    {
180        match self.nodes[handle] {
181            BehaviorNode::Root(ref root) => {
182                if root.child.is_some() {
183                    self.tick_recursive(root.child, context)
184                } else {
185                    Ok(Status::Success)
186                }
187            }
188            BehaviorNode::Composite(ref composite) => match composite.kind {
189                CompositeNodeKind::Sequence => {
190                    let mut all_succeeded = true;
191                    for child in composite.children.iter() {
192                        match self.tick_recursive(*child, context)? {
193                            Status::Failure => {
194                                all_succeeded = false;
195                                break;
196                            }
197                            Status::Running => {
198                                return Ok(Status::Running);
199                            }
200                            _ => (),
201                        }
202                    }
203                    if all_succeeded {
204                        Ok(Status::Success)
205                    } else {
206                        Ok(Status::Failure)
207                    }
208                }
209                CompositeNodeKind::Selector => {
210                    for child in composite.children.iter() {
211                        match self.tick_recursive(*child, context)? {
212                            Status::Success => return Ok(Status::Success),
213                            Status::Running => return Ok(Status::Running),
214                            _ => (),
215                        }
216                    }
217                    Ok(Status::Failure)
218                }
219            },
220            BehaviorNode::Leaf(ref leaf) => {
221                leaf.behavior.as_ref().unwrap().borrow_mut().tick(context)
222            }
223            BehaviorNode::Inverter(ref inverter) => {
224                match self.tick_recursive(inverter.child, context)? {
225                    Status::Success => Ok(Status::Failure),
226                    Status::Failure => Ok(Status::Success),
227                    Status::Running => Ok(Status::Running),
228                }
229            }
230            BehaviorNode::Unknown => {
231                unreachable!()
232            }
233        }
234    }
235
236    /// Tries to get a shared reference to a node by given handle.
237    pub fn node(&self, handle: Handle<BehaviorNode<B>>) -> Result<&BehaviorNode<B>, PoolError> {
238        self.nodes.try_borrow(handle)
239    }
240
241    /// Tries to get a mutable reference to a node by given handle.
242    pub fn node_mut(
243        &mut self,
244        handle: Handle<BehaviorNode<B>>,
245    ) -> Result<&mut BehaviorNode<B>, PoolError> {
246        self.nodes.try_borrow_mut(handle)
247    }
248
249    /// Performs a single update tick with given context.
250    pub fn tick<'a, Ctx>(&self, context: &mut Ctx) -> Result<Status, GameError>
251    where
252        B: Behavior<'a, Context = Ctx>,
253    {
254        self.tick_recursive(self.root, context)
255    }
256}
257
258impl<B: BaseBehavior> Index<Handle<BehaviorNode<B>>> for BehaviorTree<B> {
259    type Output = BehaviorNode<B>;
260
261    fn index(&self, index: Handle<BehaviorNode<B>>) -> &Self::Output {
262        &self.nodes[index]
263    }
264}
265
266impl<B: BaseBehavior> IndexMut<Handle<BehaviorNode<B>>> for BehaviorTree<B> {
267    fn index_mut(&mut self, index: Handle<BehaviorNode<B>>) -> &mut Self::Output {
268        &mut self.nodes[index]
269    }
270}
271
272/// Creates a new sequence.
273pub fn sequence<B, const N: usize>(
274    children: [Handle<BehaviorNode<B>>; N],
275    tree: &mut BehaviorTree<B>,
276) -> Handle<BehaviorNode<B>>
277where
278    B: BaseBehavior,
279{
280    CompositeNode::new_sequence(children.to_vec()).add_to(tree)
281}
282
283/// Creates a new selector.
284pub fn selector<B, const N: usize>(
285    children: [Handle<BehaviorNode<B>>; N],
286    tree: &mut BehaviorTree<B>,
287) -> Handle<BehaviorNode<B>>
288where
289    B: BaseBehavior,
290{
291    CompositeNode::new_selector(children.to_vec()).add_to(tree)
292}
293
294/// Creates a new leaf.
295pub fn leaf<B>(behavior: B, tree: &mut BehaviorTree<B>) -> Handle<BehaviorNode<B>>
296where
297    B: BaseBehavior,
298{
299    LeafNode::new(behavior).add_to(tree)
300}
301
302/// Creates a new inverter.
303pub fn inverter<B>(
304    child: Handle<BehaviorNode<B>>,
305    tree: &mut BehaviorTree<B>,
306) -> Handle<BehaviorNode<B>>
307where
308    B: BaseBehavior,
309{
310    Inverter::new(child).add_to(tree)
311}
312
313#[cfg(test)]
314mod test {
315    use crate::plugin::error::GameError;
316    use crate::{
317        core::{futures::executor::block_on, visitor::prelude::*},
318        utils::behavior::{
319            composite::{CompositeNode, CompositeNodeKind},
320            leaf::LeafNode,
321            Behavior, BehaviorTree, Status,
322        },
323    };
324    use std::{env, fs::File, io::Write, path::PathBuf};
325
326    #[derive(Debug, PartialEq, Default, Visit, Clone)]
327    struct WalkAction;
328
329    impl Behavior<'_> for WalkAction {
330        type Context = Environment;
331
332        fn tick(&mut self, context: &mut Self::Context) -> Result<Status, GameError> {
333            if context.distance_to_door <= 0.0 {
334                Ok(Status::Success)
335            } else {
336                context.distance_to_door -= 0.1;
337                println!(
338                    "Approaching door, remaining distance: {}",
339                    context.distance_to_door
340                );
341                Ok(Status::Running)
342            }
343        }
344    }
345
346    #[derive(Debug, PartialEq, Default, Visit, Clone)]
347    struct OpenDoorAction;
348
349    impl Behavior<'_> for OpenDoorAction {
350        type Context = Environment;
351
352        fn tick(&mut self, context: &mut Self::Context) -> Result<Status, GameError> {
353            if !context.door_opened {
354                context.door_opened = true;
355                println!("Door was opened!");
356            }
357            Ok(Status::Success)
358        }
359    }
360
361    #[derive(Debug, PartialEq, Default, Visit, Clone)]
362    struct StepThroughAction;
363
364    impl Behavior<'_> for StepThroughAction {
365        type Context = Environment;
366
367        fn tick(&mut self, context: &mut Self::Context) -> Result<Status, GameError> {
368            if context.distance_to_door < -1.0 {
369                Ok(Status::Success)
370            } else {
371                context.distance_to_door -= 0.1;
372                println!(
373                    "Stepping through doorway, remaining distance: {}",
374                    -1.0 - context.distance_to_door
375                );
376                Ok(Status::Running)
377            }
378        }
379    }
380
381    #[derive(Debug, PartialEq, Default, Visit, Clone)]
382    struct CloseDoorAction;
383
384    impl Behavior<'_> for CloseDoorAction {
385        type Context = Environment;
386
387        fn tick(&mut self, context: &mut Self::Context) -> Result<Status, GameError> {
388            if context.door_opened {
389                context.door_opened = false;
390                context.done = true;
391                println!("Door was closed");
392            }
393            Ok(Status::Success)
394        }
395    }
396
397    #[derive(Debug, PartialEq, Visit, Clone, Default)]
398    enum BotBehavior {
399        #[default]
400        None,
401        Walk(WalkAction),
402        OpenDoor(OpenDoorAction),
403        StepThrough(StepThroughAction),
404        CloseDoor(CloseDoorAction),
405    }
406
407    #[derive(Default, Visit)]
408    struct Environment {
409        // > 0 - door in front of
410        // < 0 - door is behind
411        distance_to_door: f32,
412        door_opened: bool,
413        done: bool,
414    }
415
416    impl Behavior<'_> for BotBehavior {
417        type Context = Environment;
418
419        fn tick(&mut self, context: &mut Self::Context) -> Result<Status, GameError> {
420            match self {
421                BotBehavior::None => unreachable!(),
422                BotBehavior::Walk(v) => v.tick(context),
423                BotBehavior::OpenDoor(v) => v.tick(context),
424                BotBehavior::StepThrough(v) => v.tick(context),
425                BotBehavior::CloseDoor(v) => v.tick(context),
426            }
427        }
428    }
429
430    fn create_tree() -> BehaviorTree<BotBehavior> {
431        let mut tree = BehaviorTree::new();
432
433        let entry = CompositeNode::new(
434            CompositeNodeKind::Sequence,
435            vec![
436                LeafNode::new(BotBehavior::Walk(WalkAction)).add_to(&mut tree),
437                LeafNode::new(BotBehavior::OpenDoor(OpenDoorAction)).add_to(&mut tree),
438                LeafNode::new(BotBehavior::StepThrough(StepThroughAction)).add_to(&mut tree),
439                LeafNode::new(BotBehavior::CloseDoor(CloseDoorAction)).add_to(&mut tree),
440            ],
441        )
442        .add_to(&mut tree);
443
444        tree.set_entry_node(entry);
445
446        tree
447    }
448
449    #[test]
450    fn test_behavior() {
451        let tree = create_tree();
452
453        let mut ctx = Environment {
454            distance_to_door: 3.0,
455            door_opened: false,
456            done: false,
457        };
458
459        while !ctx.done {
460            tree.tick(&mut ctx).unwrap();
461        }
462    }
463
464    #[test]
465    fn test_behavior_save_load() {
466        let (bin, txt) = {
467            let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
468            let root = PathBuf::from(manifest_dir).join("test_output");
469            if !root.exists() {
470                std::fs::create_dir(&root).unwrap();
471            }
472            (
473                root.join(format!("{}.bin", "behavior_save_load")),
474                root.join(format!("{}.txt", "behavior_save_load")),
475            )
476        };
477
478        // Save
479        let mut saved_tree = create_tree();
480        let mut visitor = Visitor::new();
481        saved_tree.visit("Tree", &mut visitor).unwrap();
482        visitor.save_binary_to_file(bin.clone()).unwrap();
483        let mut file = File::create(txt).unwrap();
484        file.write_all(visitor.save_ascii_to_string().as_bytes())
485            .unwrap();
486
487        // Load
488        let mut visitor = block_on(Visitor::load_from_file(bin)).unwrap();
489        let mut loaded_tree = BehaviorTree::<BotBehavior>::default();
490        loaded_tree.visit("Tree", &mut visitor).unwrap();
491
492        assert_eq!(saved_tree, loaded_tree);
493    }
494}