行为树的一个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; }