Skip to content

Commit

Permalink
📝 add behavior tree documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
linkdd committed Jan 8, 2024
1 parent ba71224 commit 5dfc1f4
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 3 deletions.
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
**AI Toolkit** is a header-only C++ library which provides tools for building
the brain of your game's NPCs.

It provides:

- Finite State Machines
- Behavior Tree
- Utility AI
- Goal Oriented Action Planning

## Installation

Add the `include` folder of this repository to your include paths.
Expand All @@ -25,6 +32,62 @@ $ g++ -std=c++23 -Iaiotoolkit/include main.cpp -o mygame

## Usage

### Finite State Machine

TODO

### Behavior Tree

Include the header:

```cpp
#include <aitoolkit/behtree.hpp>

using namespace aitoolkit::bt;
```
Create your blackboard type:
```cpp
struct blackboard_type {
// ...
};
```

Create your tree:

```cpp
auto tree = seq<blackboard_type>::make({
check<blackboard_type>::make([](const blackboard_type& bb) {
// check some condition
return true;
}),
task<blackboard_type>::make([](blackboard_type& bb) {
// perform some action
return execution_state::success;
})
});
```

Evaluate it:

```cpp
auto blackboard = blackboard_type{
// ...
};

auto state = tree->evaluate(blackboard);
```
For more informations, consult the
[documentation](https://linkdd.github.io/aitoolkit/group__behtree.html).
### Utility AI
TODO
### Goal Oriented Action Planning
TODO
## Documentation
Expand Down
170 changes: 167 additions & 3 deletions include/aitoolkit/behtree.hpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,152 @@
#pragma once

/**
@defgroup behtree Behavior Tree
@brief Behavior Tree implementation
## Introduction
A behavior tree is a tree structure where each node represents a behavior. The
tree is evaluated from the root node to the leaf nodes. The leaf nodes are
either tasks or checks. A task is a node that performs an action and returns
either success or failure. A check is a node that returns either success or
failure based on some condition.
<pre class="mermaid">
flowchart TD
root{{Selector}} --> seq1
root --> seq2
root --> seq3
seq1{{Sequence}} --> seq1a1
seq1 --> seq1a2
seq2{{Sequence}} --> seq2a1
seq2 --> seq2a2
seq3{{Sequence}} --> seq3a1
seq3 --> seq3a2
seq1a1[Check enemy in attack range]
seq1a2[[Destroy enemy]]
seq2a1[Check enemy in sight]
seq2a2[[Move towards enemy]]
seq3a1[[Move towards waypoint]]
seq3a2[[Select next waypoint]]
style root fill:darkred
style seq1 fill:darkgreen
style seq2 fill:darkgreen
style seq3 fill:darkgreen
</pre>
## Usage
First, include the header file:
```cpp
#include <aitoolkit/behtree.hpp>
```
Then, create a blackboard class that will hold the state of the tree:
```cpp
struct blackboard_type {
glm::vec2 agent_position;
glm::vec2 enemy_position;
float attack_range;
float sight_range;
size_t current_waypoint;
std::vector<glm::vec2> waypoints;
};
```
Next, create the tree:
```cpp
using namespace aitoolkit::bt;
auto tree = sel<blackboard_type>::make({
seq<blackboard_type>::make({
check<blackboard_type>::make([](const blackboard_type& bb) {
auto distance = glm::distance(bb.agent_position, bb.enemy_position);
return distance <= bb.attack_range;
}),
task<blackboard_type>::make([](blackboard_type& bb) {
// Destroy enemy
return execution_state::success;
})
}),
seq<blackboard_type>::make({
check<blackboard_type>::make([](const blackboard_type& bb) {
auto distance = glm::distance(bb.agent_position, bb.enemy_position);
return distance <= bb.sight_range;
}),
task<blackboard_type>::make([](blackboard_type& bb) {
// Move towards enemy
return execution_state::success;
})
}),
seq<blackboard_type>::make({
task<blackboard_type>::make([](blackboard_type& bb) {
// Move towards waypoint
return execution_state::success;
}),
task<blackboard_type>::make([](blackboard_type& bb) {
// Select next waypoint
return execution_state::success;
})
})
});
```
Finally, evaluate the tree:
```cpp
auto bb = blackboard_type{
.agent_position = { 0.0f, 0.0f },
.enemy_position = { 1.0f, 1.0f },
.attack_range = 0.5f,
.sight_range = 1.0f
};
while (true) {
auto state = tree->evaluate(bb);
if (state == execution_state::success) {
break;
}
}
```
*/

#include <initializer_list>
#include <functional>
#include <memory>
#include <vector>

namespace aitoolkit::bt {
/**
* @ingroup behtree
* @enum execution_state
* @brief Represent the state of a node
*/
enum class execution_state {
success,
failure,
running
success, /**< The node was successful */
failure, /**< The node failed */
running /**< The node is still running */
};

/**
* @ingroup behtree
* @class node
* @brief Base abstract class for all nodes
*/
template <class T>
class node {
public:
Expand All @@ -21,9 +156,18 @@ namespace aitoolkit::bt {
std::vector<std::shared_ptr<node<T>>> m_children;
};

/**
* @ingroup behtree
* @brief Heap-allocated pointer to node
*/
template <class T>
using node_ptr = std::shared_ptr<node<T>>;

/**
* @ingroup behtree
* @class seq
* @brief Sequence node, will execute all children in order until one fails
*/
template <class T>
class seq final : public node<T> {
public:
Expand All @@ -50,6 +194,11 @@ namespace aitoolkit::bt {
}
};

/**
* @ingroup behtree
* @class sel
* @brief Selector node, will execute all children in order until one succeeds
*/
template <class T>
class sel final : public node<T> {
public:
Expand All @@ -76,6 +225,11 @@ namespace aitoolkit::bt {
}
};

/**
* @ingroup behtree
* @class neg
* @brief Negate node, will return the opposite of the child node
*/
template <class T>
class neg final : public node<T> {
public:
Expand Down Expand Up @@ -104,6 +258,11 @@ namespace aitoolkit::bt {
}
};

/**
* @ingroup behtree
* @class check
* @brief Check node, will return success if the callback returns true
*/
template <class T>
class check final : public node<T> {
public:
Expand All @@ -128,6 +287,11 @@ namespace aitoolkit::bt {
callback_type m_fn;
};

/**
* @ingroup behtree
* @class task
* @brief Task node, will execute the callback and return the result
*/
template <class T>
class task final : public node<T> {
public:
Expand Down

0 comments on commit 5dfc1f4

Please sign in to comment.