BehaviorTree
Core Library to create and execute Behavior Trees
Loading...
Searching...
No Matches
switch_node.h
1/* Copyright (C) 2020-2025 Davide Faconti - All Rights Reserved
2*
3* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
4* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
5* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7*
8* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
10* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11*/
12
13#pragma once
14
15#include "behaviortree_cpp/control_node.h"
16
17namespace BT
18{
19/**
20 * @brief The SwitchNode is equivalent to a switch statement, where a certain
21 * branch (child) is executed according to the value of a blackboard entry.
22 *
23 * Note that the same behaviour can be achieved with multiple Sequences, Fallbacks and
24 * Conditions reading the blackboard, but switch is shorter and more readable.
25 *
26 * Example usage:
27 *
28
29<Switch3 variable="{var}" case_1="1" case_2="42" case_3="666" >
30 <ActionA name="action_when_var_eq_1" />
31 <ActionB name="action_when_var_eq_42" />
32 <ActionC name="action_when_var_eq_666" />
33 <ActionD name="default_action" />
34 </Switch3>
35
36When the SwitchNode is executed (Switch3 is a node with 3 cases)
37the "variable" will be compared to the cases and execute the correct child
38or the default one (last).
39 *
40 */
41
42namespace details
43{
44
45bool CheckStringEquality(const std::string& v1, const std::string& v2,
46 const ScriptingEnumsRegistry* enums);
47}
48
49template <size_t NUM_CASES>
50class SwitchNode : public ControlNode
51{
52public:
53 SwitchNode(const std::string& name, const BT::NodeConfig& config);
54
55 ~SwitchNode() override = default;
56
57 SwitchNode(const SwitchNode&) = delete;
58 SwitchNode& operator=(const SwitchNode&) = delete;
59 SwitchNode(SwitchNode&&) = delete;
60 SwitchNode& operator=(SwitchNode&&) = delete;
61
62 void halt() override;
63
64 static PortsList providedPorts();
65
66private:
67 int running_child_ = -1;
68 std::vector<std::string> case_keys_;
69 virtual BT::NodeStatus tick() override;
70};
71
72//-----------------------------------------------
73//-----------------------------------------------
74
75template <size_t NUM_CASES>
76inline SwitchNode<NUM_CASES>::SwitchNode(const std::string& name,
77 const NodeConfig& config)
78 : ControlNode::ControlNode(name, config)
79{
80 setRegistrationID("Switch");
81 for(unsigned i = 1; i <= NUM_CASES; i++)
82 {
83 case_keys_.push_back(std::string("case_") + std::to_string(i));
84 }
85}
86
87template <size_t NUM_CASES>
88inline void SwitchNode<NUM_CASES>::halt()
89{
90 running_child_ = -1;
92}
93
94template <size_t NUM_CASES>
95inline PortsList SwitchNode<NUM_CASES>::providedPorts()
96{
97 static PortsList provided_ports = []() {
98 PortsList ports;
99 ports.insert(BT::InputPort<std::string>("variable"));
100 for(unsigned i = 1; i <= NUM_CASES; i++)
101 {
102 auto key = std::string("case_") + std::to_string(i);
103 ports.insert(BT::InputPort<std::string>(key));
104 }
105 return ports;
106 }();
107
108 return provided_ports;
109}
110
111template <size_t NUM_CASES>
112inline NodeStatus SwitchNode<NUM_CASES>::tick()
113{
114 if(childrenCount() != NUM_CASES + 1)
115 {
116 throw LogicError("Wrong number of children in SwitchNode; "
117 "must be (num_cases + default)");
118 }
119
120 std::string variable;
121 std::string value;
122 int match_index = int(NUM_CASES); // default index;
123
124 // no variable? jump to default
125 if(getInput("variable", variable))
126 {
127 // check each case until you find a match
128 for(int index = 0; index < int(NUM_CASES); ++index)
129 {
130 const std::string& case_key = case_keys_[index];
131 if(getInput(case_key, value))
132 {
133 if(details::CheckStringEquality(variable, value, this->config().enums.get()))
134 {
135 match_index = index;
136 break;
137 }
138 }
139 }
140 }
141
142 // if another one was running earlier, halt it
143 if(running_child_ != -1 && running_child_ != match_index)
144 {
145 haltChild(running_child_);
146 }
147
148 auto& selected_child = children_nodes_[match_index];
149 NodeStatus ret = selected_child->executeTick();
150 if(ret == NodeStatus::SKIPPED)
151 {
152 // if the matching child is SKIPPED, should I jump to default or
153 // be SKIPPED myself? Going with the former, for the time being.
154 running_child_ = -1;
155 return NodeStatus::SKIPPED;
156 }
157 else if(ret == NodeStatus::RUNNING)
158 {
159 running_child_ = match_index;
160 }
161 else
162 {
164 running_child_ = -1;
165 }
166 return ret;
167}
168
169} // namespace BT
The ControlNode is the base class for nodes that can have multiple children.
Definition: control_node.h:32
void resetChildren()
virtual void halt() override
Definition: switch_node.h:51
void halt() override
Definition: switch_node.h:88
The SwitchNode is equivalent to a switch statement, where a certain branch (child) is executed accord...
Definition: basic_types.h:515
Definition: action_node.h:24
NodeStatus
Definition: basic_types.h:34
Definition: tree_node.h:105