行为树的一个C++实现
1. 基本概念
行为树,英文是Behavior Tree,简称BT,是由行为节点组成的树状结构。在BT中,节点是有层次的,子节点由其父节点来控制。
每个节点的执行都有一个结果(成功Success,失败Failure或运行Running),该节点的执行结果都由其父节点来管理,从而决定接下来做
什么,父节点的类型决定了不同的控制类型。
节点从结构上分为两类:组合节点、叶节点,所谓组合节点就是出度大于0的节点,叶节点一般用来放置执行逻辑和条件判断。
1)序列(Sequence)节点:组合结点,顺序执行所有子节点,如果子节点全部执行成功,返回成功。如果某个子节点失败则返回失败,且不再执行下一个子节点。
2)选择(Selector)结点:组合结点,顺序执行所有子节点,只要碰到一个子节点返回成功,则返回成功,否则继续执行下一个。如果子结点全都执行失败,则返回失败。
3)并行(Parallel)结点:组合结点,并行其下所有子节点,所有节点成功则返回成功(有任意子节点失败则失败)。
4)条件(Condition)节点:叶子结点,根据条件的比较结果,返回成功或失败。
5)动作(Action)节点:叶子结点,根据动作结果返回成功,失败,或运行。
6)等待(Wait)节点:叶子节点,当指定的时间过去后返回成功。
7)循环(Loop)节点:叶子节点,循环执行子节点到指定次数后返回成功,如果循环次数为-1,则无限循环。
比如我们要实现这个行为:如果碰到主角,打招呼;否则,睡觉。

2. 通过Json建立行为树
输入使用Json格式。从结构上看,所有的数据最终都可以分解成三种类型:
1)标量(scalar):也就是一个单独的字符串或数字。
2)序列(sequence):也就是若干个相关的数据按照一定顺序并列在一起,又叫做数组(array)或列表(List)。
3)映射(mapping):也就是一个名/值对(Name/value),即数据有一个名称,还有一个与之相对应的值,这又称作散列(hash)或字典(dictionary)。
Json的书写规则如下:
1) 并列的数据之间用逗号(",")分隔。
2) 映射用冒号(":")表示。
3) 并列数据的集合(数组)用方括号("[]")表示。
4) 映射的集合(对象)用大括号("{}")表示。
下面举一个由输入Json来构建行为树的例子,执行效果是车辆先播放音乐,然后右转,然后停下来。

3. 生成结点的工厂类实现
class BtNodeFactory
{
public:
virtual ~BtNodeFactory() {}
virtual BtNode* CreateNode() = 0;
public:
static BtNode* CreateNode(json_t *btJson)
{
const char *key;
json_t *value;
std::string nodeType;
json_object_foreach(btJson, key, value)
{
if (strncmp(key, "type", 2) == 0 && json_is_string(value))
{
nodeType = json_string_value(value);
break;
}
}
std::map<std::string, BtNodeFactory*> *factoriesMap = BtNodeFactory::GetNodeFactoriesMap_();
std::map<std::string, BtNodeFactory*>::iterator it = factoriesMap->find(nodeType);
if (it == factoriesMap->end()) return NULL; // Not registered
BtNode *node = it->second->CreateNode();
if (node)
{
if(node->Load(btJson)) return node;
else delete node;
}
return NULL;
}
protected:
static std::map<std::string, BtNodeFactory*> *GetNodeFactoriesMap_()
{
static std::map<std::string, BtNodeFactory*> factoriesMap;
return &factoriesMap;
}
};
template <class NodeClass>
class BtNodeFactoryTemplate : public BtNodeFactory
{
public:
virtual BtNode* CreateNode() { return new NodeClass(); }
BtNodeFactoryTemplate(const std::string type) { (*BtNodeFactory::GetNodeFactoriesMap_())[type] = this; }
};
#define REGISTER_BT_NODE(_class_, __type__) static const BtNodeFactoryTemplate<_class_> _nodeFactory##_class_(__type__);
4. 所有结点的基础类型(抽象接口)实现
1)btNode.h代码
class BtNode
{
public:
// Enumerates the states every node can be in after execution during a particular time step:
// - "Idle" indicates that the node hasn't run yet.
// - "Running" indicates that the node has successfully moved forward during this time step, but the task is not yet complete;
// - "Success" indicates that the node has completed running during this time step;
// - "Failure" indicates that the node has determined it will not be able to complete its task;
// - "Halted" indicates that the node has been halted by its father.
enum NodeState { Idle, Running, Success, Failure, Halted, Exit };
public:
BtNode(std::string const& type) : parent(NULL), name_("null"), _type(type), _state(Idle) {}
virtual ~BtNode() { _state = BtNode::Exit; }
public:
// Load or Dump Behavior Tree
virtual bool Load(json_t *btJson);
virtual json_t *Dump();
NodeState Play(BtBlackBoard& blackBoard);
NodeState Halt(BtBlackBoard& blackBoard);
NodeState Stop(BtBlackBoard& blackBoard);
// The method that retrive the state of the node
NodeState GetNodeState() { return _state; }
BtNode *parent;
protected:
virtual NodeState OnStart_(BtBlackBoard& blackBoard) = 0;
virtual NodeState OnPause_(BtBlackBoard& blackBoard) = 0;
virtual NodeState OnResume_(BtBlackBoard& blackBoard) = 0;
virtual NodeState OnStop_(BtBlackBoard& blackBoard) = 0;
std::string name_;
private:
std::string _type;
NodeState _state;
};
2)btNode.cpp代码
Play、Halt、Stop代码进行了状态判断,比如只有当开始状态是Idle时,才能执行OnStart_。
bool BtNode::Load(json_t *btJson)
{
if(json_is_object(btJson) == false) return false;
const char *key;
json_t *value;
json_object_foreach(btJson, key, value)
{
if(strncmp(key, "type", 2) == 0 && json_is_string(value))
if (strcmp(json_string_value(value), _type.c_str())) // 类型对不上
return false;
else if(strncmp(key, "name", 2) == 0 && json_is_string(value))
name_ = json_string_value(value);
}
return true;
}
json_t *BtNode::Dump()
{
json_t *btJson = json_object();
if (btJson)
{
json_object_set_new(btJson, "type", json_string(_type.c_str()));
json_object_set_new(btJson, "name", json_string(name_.c_str()));
std::string stateString = "Error Value";
const char* stateStringList_[] = {"Idle", "Running", "Success", "Failure", "Halted", "Exit"};
if (_state >= BtNode::Idle && _state <= BtNode::Exit)
stateString = stateStringList_[_state];
json_object_set_new(btJson, "[state]", json_string(stateString.c_str()));
}
return btJson;
}
BtNode::NodeState BtNode::Play(BtBlackBoard& blackBoard)
{
if (_state == BtNode::Running || _state == BtNode::Halted)
_state = OnResume_(blackBoard);
else if (_state == BtNode::Idle)
_state = OnStart_(blackBoard);
return _state;
}
BtNode::NodeState BtNode::Halt(BtBlackBoard& blackBoard)
{
if (_state == BtNode::Running)
_state = OnPause_(blackBoard);
return _state;
}
BtNode::NodeState BtNode::Stop(BtBlackBoard& blackBoard)
{
if (_state == BtNode::Running || _state == BtNode::Halted || _state == BtNode::Success || _state == BtNode::Failure)
_state = OnStop_(blackBoard);
return _state;
}
5. 组合结点的基础类型实现
继承BtNode,是组合结点类型的基类,如parallel、selector、sequence等需要继承这个类,而叶子结点类型只需要继承BtNode类。
1)BtGroupNode.h代码
class BtGroupNode : public BtNode
{
public:
// Enumerates the options for when a parallel node is considered to have failed:
// - "FailOnOne" indicates that the node will return failure as soon as one of its children fails;
// - "FailOnAll" indicates that all of the node's children must fail before it returns failure.
enum FailurePolicy {FailOnOne, FailOnAll};
// Enumerates the options for when a parallel node is considered to have succeeded:
// - "SucceedOnOne" indicates that the node will return success as soon as one of its children succeeds;
// - "SucceedOnAll" indicates that all of the node's children must succeed before it returns success.
enum SuccessPolicy {SucceedOnOne, SucceedOnAll};
public:
BtGroupNode(std::string const& type);
virtual ~BtGroupNode();
public:
// Load or Dump Behavior Tree
virtual bool Load(json_t *btJson);
virtual json_t *Dump();
protected:
std::vector<BtNode *> childNodes_;
SuccessPolicy successPolicy_;
FailurePolicy failurePolicy_;
};
2)BtGroupNode.cpp代码
BtGroupNode::BtGroupNode(std::string const& type)
: BtNode(type)
, successPolicy_(SucceedOnAll)
, failurePolicy_(FailOnAll)
{
}
BtGroupNode::~BtGroupNode()
{
std::vector<BtNode *>::iterator iter;
for(iter = childNodes_.begin(); iter != childNodes_.end(); iter++)
{
if (*iter)
delete (*iter);
}
childNodes_.clear();
}
bool BtGroupNode::Load(json_t *btJson)
{
if(BtNode::Load(btJson) == false) return false;
const char *key;
json_t *value;
json_object_foreach(btJson, key, value)
{
if(strncmp(key, "behaviors", 2) == 0 && json_is_array(value))
{
unsigned int index;
json_t *child;
json_array_foreach(value, index, child)
{
BtNode *node = BtNodeFactory::CreateNode(child);
if (node)
{
childNodes_.push_back(node);
node->parent = this;
}
}
}
else if(strncmp(key, "successPolicy", 2) == 0 && json_is_string(value))
{
std::string valueString = json_string_value(value);
if (valueString == "SucceedOnOne")
successPolicy_ = SucceedOnOne;
}
else if(strncmp(key, "failurePolicy", 2) == 0 && json_is_string(value))
{
std::string valueString = json_string_value(value);
if (valueString == "FailOnAll")
failurePolicy_ = FailOnAll;
}
}
return true;
}
json_t* BtGroupNode::Dump()
{
json_t *btJson = BtNode::Dump();
if (btJson == NULL)
return NULL;
json_object_set_new(btJson, "successPolicy", json_string(successPolicy_ == SucceedOnOne ? "SucceedOnOne" : "SucceedOnAll"));
json_object_set_new(btJson, "failurePolicy", json_string(failurePolicy_ == FailOnAll ? "FailOnAll" : "FailOnOne"));
json_t *childrenJson = json_array();
if (childrenJson)
{
std::vector<BtNode *>::iterator iter;
for (iter = childNodes_.begin(); iter != childNodes_.end(); ++iter)
{
if (*iter)
{
json_t *childJson = (*iter)->Dump();
json_array_append_new(childrenJson, childJson);
}
}
json_object_set_new(btJson, "children", childrenJson);
}
return btJson;
}
6. parallel并行节点类实现
1)BtParallelNode.h代码
class BtParallelNode : public BtGroupNode
{
public:
BtParallelNode() : BtGroupNode(NODE_TYPE) {};
~BtParallelNode() {};
protected:
virtual NodeState OnStart_(BtBlackBoard& blackBoard);
virtual NodeState OnPause_(BtBlackBoard& blackBoard);
virtual NodeState OnResume_(BtBlackBoard& blackBoard);
virtual NodeState OnStop_(BtBlackBoard& blackBoard);
};
2)BtParallelNode.cpp代码
// from Idle to Running, Success, Failure
BtNode::NodeState BtParallelNode::OnStart_(BtBlackBoard& blackBoard)
{
unsigned int failureCount = successCount = 0;
std::vector<BtNode *>::iterator iter;
for(iter = childNodes_.begin(); iter != childNodes_.end(); ++iter)
{
BtNode::NodeState result = (*iter)->Play(blackBoard);
if (result == Running)
continue;
if(result == BtNode::Success)
successCount++;
else if(result == BtNode::Failure)
failureCount++;
}
if(failurePolicy_ == FailOnOne && failureCount) return BtNode::Failure; // 孩子节点中存在 Failure
if(successPolicy_ == SucceedOnOne && successCount) return BtNode::Success; // 孩子结点中存在 Success
if(failurePolicy_ == FailOnAll && failureCount = childNodes_.size()) return BtNode::Failure; // 孩子节点全部 Failure
if(successPolicy_ == SucceedOnAll && successCount = childNodes_.size()) return BtNode::Success; // 孩子结点全部 Success
if((failureCount + successCount) == childNodes_.size()) return BtNode::Failure;
return BtNode::Running; // 孩子结点全部都是 Running 状态
}
// from Halt, Running to Running, Success, Failure
BtNode::NodeState BtParallelNode::OnResume_(BtBlackBoard& blackBoard)
{
unsigned int failureCount = successCount = 0;
std::vector<BtNode *>::iterator iter;
for(iter = childNodes_.begin(); iter != childNodes_.end(); ++iter)
{
BtNode::NodeState result = (*iter)->Play(blackBoard);
if (result == Running)
continue;
if(result == BtNode::Success)
successCount++;
else if(result == BtNode::Failure)
failureCount++;
}
if(failurePolicy_ == FailOnOne && failureCount) return BtNode::Failure; // 孩子节点中存在 Failure
if(successPolicy_ == SucceedOnOne && successCount) return BtNode::Success; // 孩子结点中存在 Success
if(failurePolicy_ == FailOnAll && failureCount = childNodes_.size()) return BtNode::Failure; // 孩子节点全部 Failure
if(successPolicy_ == SucceedOnAll && successCount = childNodes_.size()) return BtNode::Success; // 孩子结点全部 Success
if((failureCount + successCount) == childNodes_.size()) return BtNode::Failure;
return BtNode::Running; // 孩子结点全部都是 Running 状态
}
// from Running to Halt
BtNode::NodeState BtParallelNode::OnPause_(BtBlackBoard& blackBoard)
{
std::vector<BtNode *>::iterator iter;
for(iter = childNodes_.begin(); iter != childNodes_.end(); ++iter)
(*iter)->Halt(blackBoard);
return BtNode::Halted;
}
// from Success, Failure, Running, Halt to Idle
BtNode::NodeState BtParallelNode::OnStop_(BtBlackBoard& blackBoard)
{
std::vector<BtNode *>::iterator iter;
for(iter = childNodes_.begin(); iter != childNodes_.end(); iter++)
(*iter)->Stop(blackBoard);
return BtNode::Idle;
}
7. sequence序列节点类实现
1)BtSequenceNode.h代码
class BtSequenceNode : public BtGroupNode
{
public:
BtSequenceNode() : BtGroupNode("sequence")
~BtSequenceNode() {}
protected:
virtual NodeState OnStart_(BtBlackBoard& blackBoard);
virtual NodeState OnPause_(BtBlackBoard& blackBoard);
virtual NodeState OnResume_(BtBlackBoard& blackBoard);
virtual NodeState OnStop_(BtBlackBoard& blackBoard);
};
2)BtSequenceNode.cpp代码
// from Idle to Running, Success, Failure
BtNode::NodeState BtSequenceNode::OnStart_(BtBlackBoard& blackBoard)
{
unsigned int failureCount = successCount = 0;
std::vector<BtNode *>::iterator iter;
for (iter = childNodes_.begin(); iter != childNodes_.end(); ++iter)
{
BtNode::NodeState result = (*iter)->Play(blackBoard);
if (result == Running)
return BtNode::Running;
if (result == BtNode::Success)
successCount++;
else if(result == BtNode::Failure)
failureCount++;
}
if(failurePolicy_ == FailOnOne && failureCount) return BtNode::Failure; // 孩子节点中存在 Failure
if(successPolicy_ == SucceedOnOne && successCount) return BtNode::Success; // 孩子结点中存在 Success
if(failurePolicy_ == FailOnAll && failureCount = childNodes_.size()) return BtNode::Failure; // 孩子节点全部 Failure
if(successPolicy_ == SucceedOnAll && successCount = childNodes_.size()) return BtNode::Success; // 孩子结点全部 Success
if((failureCount + successCount) == childNodes_.size()) return BtNode::Failure;
return BtNode::Failure;
}
// from Halt, Running to Running, Success, Failure
BtNode::NodeState BtSequenceNode::OnResume_(BtBlackBoard& blackBoard)
{
unsigned int failureCount = successCount = 0;
std::vector<BtNode *>::iterator iter;
for(iter = childNodes_.begin(); iter != childNodes_.end(); ++iter)
{
BtNode::NodeState result = (*iter)->Play(blackBoard);
if(result == BtNode::Running)
return result;
if (result == Success)
successCount++;
else if(result == BtNode::Failure)
failureCount++;
}
if(failurePolicy_ == FailOnOne && failureCount) return BtNode::Failure; // 孩子节点中存在 Failure
if(successPolicy_ == SucceedOnOne && successCount) return BtNode::Success; // 孩子结点中存在 Success
if(failurePolicy_ == FailOnAll && failureCount = childNodes_.size()) return BtNode::Failure; // 孩子节点全部 Failure
if(successPolicy_ == SucceedOnAll && successCount = childNodes_.size()) return BtNode::Success; // 孩子结点全部 Success
if((failureCount + successCount) == childNodes_.size()) return BtNode::Failure;
return BtNode::Failure;
}
// from Running to Halt
BtNode::NodeState BtSequenceNode::OnPause_(BtBlackBoard& blackBoard)
{
std::vector<BtNode *>::iterator iter;
for (iter = childNodes_.begin(); iter != childNodes_.end(); ++iter)
{
BtNode::NodeState state = (*iter)->GetNodeState();
if (state == BtNode::Running)
return (*iter)->Halt(blackBoard);
}
return BtNode::Failure;
}
// from Success, Failure, Running, Halt to Idle
BtNode::NodeState BtSequenceNode::OnStop_(BtBlackBoard& blackBoard)
{
std::vector<BtNode *>::iterator iter;
for (iter = childNodes_.begin(); iter != childNodes_.end(); ++iter)
(*iter)->Stop(blackBoard);
return BtNode::Idle;
}
8. action动作节点类实现
1)BtActionNode.h代码
class BtActionNode : public BtNode
{
public:
class IPlayer
{
public:
typedef enum
{
STATE_UNKNOWN = 0,
STATE_STOP = 1,
STATE_PLAY = 2,
STATE_PAUSE = 3
}PlayerStatus;
IPlayer() {}
virtual ~IPlayer() {}
virtual bool Play(std::string const& actionName, float duration) = 0;
virtual bool Pause(std::string const& actionName) = 0;
virtual bool Resume(std::string const& actionName) = 0;
virtual bool Stop(std::string const& actionName) = 0;
virtual bool GetStatus(std::string const &action, PlayerStatus &status) = 0;
};
public:
BtActionNode() : BtNode("action"), _duration(0)
~BtActionNode();
public:
// Load or Dump Behavior Tree
virtual bool Load(json_t *btJson);
virtual json_t *Dump();
protected:
virtual NodeState OnStart_(BtBlackBoard& blackBoard);
virtual NodeState OnPause_(BtBlackBoard& blackBoard);
virtual NodeState OnResume_(BtBlackBoard& blackBoard);
virtual NodeState OnStop_(BtBlackBoard& blackBoard);
private:
std::string _action;
float _duration;
};
2)BtActionNode.cpp代码
bool BtActionNode::Load(json_t *btJson)
{
if (BtNode::Load(btJson) == false) return false;
const char *key;
json_t *value;
json_object_foreach(btJson, key, value)
{
if (strncmp(key, "duration", 2) == 0 && json_is_number(value))
_duration = json_number_value(value) * 1000;
else if (strncmp(key, "action", 2) == 0 && json_is_string(value))
_action = json_string_value(value);
}
return true;
}
json_t *BtActionNode::Dump()
{
json_t *btJson = BtNode::Dump();
if (btJson)
{
json_object_set_new(btJson, "action", json_string(_action.c_str()));
json_object_set_new(btJson, "duration", json_integer(_duration));
}
return btJson;
}
BtNode::NodeState BtActionNode::OnStart_(BtBlackBoard& blackBoard)
{
IPlayer *player = NULL;
if (blackBoard.GetValue("Action", player) && player)
return player->Play(_action, _duration) ? BtNode::Running : BtNode::Failure;
return BtNode::Failure;
}
BtNode::NodeState BtActionNode::OnPause_(BtBlackBoard& blackBoard)
{
IPlayer *player = NULL;
if (blackBoard.GetValue("Action", player) && player)
return player->Pause(_action) ? BtNode::Halted : BtNode::Failure;
return BtNode::Failure;
}
BtNode::NodeState BtActionNode::OnResume_(BtBlackBoard& blackBoard)
{
IPlayer *player = NULL;
if (blackBoard.GetValue("Action", player) && player)
return player->Resume(_action) ? BtNode::Running : BtNode::Failure;
return BtNode::Failure;
}
BtNode::NodeState BtActionNode::OnStop_(BtBlackBoard& blackBoard)
{
IPlayer *player = NULL;
if (blackBoard.GetValue("Action", player) && player)
return player->Stop(_action) ? BtNode::Idle : BtNode::Failure;
return BtNode::Failure;
}
浙公网安备 33010602011771号