10

AI Toolkit: Give a brain to your game's NPCs, a header-only C++ library

 8 months ago
source link: https://github.com/linkdd/aitoolkit
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Repository files navigation

AI Toolkit

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

Why this project? Well, I wrote about it here.

Installation

Add the include folder of this repository to your include paths.

Or add it as a submodule:

$ git submodule add https://github.com/linkdd/aitoolkit.git
$ g++ -std=c++23 -Iaiotoolkit/include main.cpp -o mygame

NB: This library is compatible with C++20.

Usage

Finite State Machine

First, include the header:

#include <aitoolkit/fsm.hpp>

using namespace aitoolkit::fsm;

Then, create your blackboard type:

struct blackboard_type {
  // ...
};

Then, create a state type for each of your states:

class state_dummy final : public state<blackboard_type> {
  public:
    virtual void enter(blackboard_type& blackboard) override {
      // ...
    }

    virtual void exit(blackboard_type& blackboard) override {
      // ...
    }

    virtual void pause(blackboard_type& blackboard) override {
      // ...
    }

    virtual void resume(blackboard_type& blackboard) override {
      // ...
    }

    virtual void update(blackboard_type& blackboard) override {
      // ...
    }
};

Create your simple state machine:

auto simple_bb = blackboard_type{};
auto simple_fsm = simple_machine<blackboard_type>();

simple_fsm.set_state(std::make_shared<state_dummy>(), simple_bb);
simple_fsm.pause(simple_bb);
simple_fsm.resume(simple_bb);
simple_fsm.update(simple_bb);

Or with a stack state machine:

auto stack_bb = blackboard_type{};
auto stack_fsm = stack_machine<blackboard_type>{};

stack_fsm.push_state(std::make_shared<state_dummy>(), stack_bb);
stack_fsm.push_state(std::make_shared<state_dummy>(), stack_bb);

stack_fsm.update(stack_bb);

stack_fsm.pop_state(stack_bb);
stack_fsm.pop_state(stack_bb);

Behavior Tree

First, include the header:

#include <aitoolkit/behtree.hpp>

using namespace aitoolkit::bt;

Then, create your blackboard type:

struct blackboard_type {
  // ...
};

Then, create your tree:

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;
  })
});

Finally, evaluate it:

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

auto state = tree->evaluate(blackboard);

For more informations, consult the documentation.

Utility AI

First, include the header file:

#include <aitoolkit/utility.hpp>

using namespace aitoolkit::utility;

Then, create a blackboard type:

struct blackboard_type {
  int food{0};
  int wood{0};
  int stone{0};
  int gold{0};
};

Next, create a class for each action that you want to be able to perform:

class collect_food final : public action<blackboard_type> {
  public:
    virtual float score(const blackboard_type& blackboard) const override {
      return 50.0f;
    }

    virtual void apply(blackboard_type& blackboard) const override {
      blackboard.food += 1;
    }
};

class collect_wood final : public action<blackboard_type> {
  public:
    virtual float score(const blackboard_type& blackboard) const override {
      return 150.0f;
    }

    virtual void apply(blackboard_type& blackboard) const override {
      blackboard.wood += 1;
    }
};

class collect_stone final : public action<blackboard_type> {
  public:
    virtual float score(const blackboard_type& blackboard) const override {
      return -10.0f;
    }

    virtual void apply(blackboard_type& blackboard) const override {
      blackboard.stone += 1;
    }
};

class collect_gold final : public action<blackboard_type> {
  public:
    virtual float score(const blackboard_type& blackboard) const override {
      return 75.0f;
    }

    virtual void apply(blackboard_type& blackboard) const override {
      blackboard.gold += 1;
    }
};

Finally, create an evaluator and run it:

auto evaluator = evaluator<blackboard_type>{
  std::make_shared<collect_food>(),
  std::make_shared<collect_wood>(),
  std::make_shared<collect_stone>(),
  std::make_shared<collect_gold>()
};

auto blackboard = blackboard_type{};
evaluator.run(blackboard);

Goal Oriented Action Planning

First, include the header file:

#include <aitoolkit/goap.hpp>

using namespace aitoolkit::goap;

Then, create a blackboard class that will hold the state of the planner:

struct blackboard_type {
  bool has_axe{false};
  int wood{0};
};

Next, create a class for each action that you want to be able to perform:

class get_axe final : public action<blackboard_type> {
  public:
    virtual float cost(const blackboard_type& blackboard) const override {
      return 1.0f;
    }

    virtual bool check_preconditions(const blackboard_type& blackboard) const override {
      return !blackboard.has_axe;
    }

    virtual void apply_effects(blackboard_type& blackboard) const override {
      blackboard.has_axe = true;
    }
};

class chop_tree final : public action<blackboard_type> {
  public:
    virtual float cost(const blackboard_type& blackboard) const override {
      return 1.0f;
    }

    virtual bool check_preconditions(const blackboard_type& blackboard) const override {
      return blackboard.has_axe;
    }

    virtual void apply_effects(blackboard_type& blackboard) const override {
      blackboard.wood += 1;
    }
};

Finally, create a plan and run it:

auto actions = std::vector<action_ptr<blackboard_type>>{
  std::make_shared<get_axe>(),
  std::make_shared<chop_tree>()
};
auto initial = blackboard_type{};
auto goal = blackboard_type{
  .has_axe = true,
  .wood = 3
};

auto p = planner<blackboard_type>(actions, initial, goal);

auto blackboard = initial;
while (p) {
  p.run_next(blackboard); // will mutate the blackboard
}

For more informations, consult the documentation.

Documentation

The documentation is available online here.

You can build it locally using doxygen:

$ make docs

License

This library is released under the terms of the MIT License.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK