diff --git a/README.md b/README.md index eb5e734..543e83b 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 + +using namespace aitoolkit::bt; +``` + +Create your blackboard type: + +```cpp +struct blackboard_type { + // ... +}; +``` + +Create your tree: + +```cpp +auto tree = seq::make({ + check::make([](const blackboard_type& bb) { + // check some condition + return true; + }), + task::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 diff --git a/include/aitoolkit/behtree.hpp b/include/aitoolkit/behtree.hpp index 240c033..c879ae9 100644 --- a/include/aitoolkit/behtree.hpp +++ b/include/aitoolkit/behtree.hpp @@ -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. + +
+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
+
+ + +## Usage + +First, include the header file: + +```cpp +#include +``` + +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 waypoints; +}; +``` + +Next, create the tree: + +```cpp +using namespace aitoolkit::bt; + +auto tree = sel::make({ + seq::make({ + check::make([](const blackboard_type& bb) { + auto distance = glm::distance(bb.agent_position, bb.enemy_position); + return distance <= bb.attack_range; + }), + task::make([](blackboard_type& bb) { + // Destroy enemy + return execution_state::success; + }) + }), + seq::make({ + check::make([](const blackboard_type& bb) { + auto distance = glm::distance(bb.agent_position, bb.enemy_position); + return distance <= bb.sight_range; + }), + task::make([](blackboard_type& bb) { + // Move towards enemy + return execution_state::success; + }) + }), + seq::make({ + task::make([](blackboard_type& bb) { + // Move towards waypoint + return execution_state::success; + }), + task::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 #include #include #include 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 node { public: @@ -21,9 +156,18 @@ namespace aitoolkit::bt { std::vector>> m_children; }; + /** + * @ingroup behtree + * @brief Heap-allocated pointer to node + */ template using node_ptr = std::shared_ptr>; + /** + * @ingroup behtree + * @class seq + * @brief Sequence node, will execute all children in order until one fails + */ template class seq final : public node { public: @@ -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 sel final : public node { public: @@ -76,6 +225,11 @@ namespace aitoolkit::bt { } }; + /** + * @ingroup behtree + * @class neg + * @brief Negate node, will return the opposite of the child node + */ template class neg final : public node { public: @@ -104,6 +258,11 @@ namespace aitoolkit::bt { } }; + /** + * @ingroup behtree + * @class check + * @brief Check node, will return success if the callback returns true + */ template class check final : public node { public: @@ -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 task final : public node { public: