1
- # Custom initialization and/or construction
1
+ # Pass additional arguments during initialization and/or construction
2
2
3
3
In every single example we explored so far we were "forced" to provide a
4
4
constructor with the following signature
@@ -11,21 +11,25 @@ constructor with the following signature
11
11
In same cases, it is desirable to pass to the constructor of our class
12
12
additional arguments, parameters, pointers, references, etc.
13
13
14
- We will just use the word _"parameter"_ for the rest of the tutorial.
14
+ **Many people use blackboards to do that: this is not recomendable.**
15
15
16
- Even if, theoretically, these parameters can be passed using Input Ports,
16
+ We will just use the word _"arguments"_ for the rest of the tutorial.
17
+
18
+ Even if, theoretically, these arguments **could** be passed using Input Ports,
17
19
that would be the wrong way to do it if:
18
20
19
- - The parameters are known at _deployment-time_.
20
- - The parameters don't change at _run-time_.
21
- - The parameters don't need to be from the _XML_.
21
+ - The arguments are known at _deployment-time_.
22
+ - The arguments don't change at _run-time_.
23
+ - The arguments don't need to be set from the _XML_.
24
+
25
+ If all these conditions are met, using ports or the blackboard is cumbersome and highly discouraged.
22
26
23
- If all these conditions are met, using ports is just cumbersome and highly discouraged.
27
+ ## Method 1: register a custom builder
24
28
25
- ## The C++ example
29
+ Consider the following custom node called **Action_A**.
26
30
27
- Next, we can see two alternative ways to pass parameters to a class:
28
- either as arguments of the constructor of the class or in an `init()` method .
31
+ We want to pass three additional arguments; they can be arbitrarily complex objects,
32
+ you are not limited to built- in types .
29
33
30
34
```C++
31
35
// Action_A has a different constructor than the default one.
@@ -41,114 +45,106 @@ public:
41
45
_arg2(arg2),
42
46
_arg3(arg3) {}
43
47
44
- NodeStatus tick() override
45
- {
46
- std::cout << "Action_A: " << _arg1 << " / " << _arg2 << " / "
47
- << _arg3 << std::endl;
48
- return NodeStatus::SUCCESS;
49
- }
50
48
// this example doesn't require any port
51
49
static PortsList providedPorts() { return {}; }
52
50
51
+ // tick() can access the private members
52
+ NodeStatus tick() override;
53
+
53
54
private:
54
55
int _arg1;
55
56
double _arg2;
56
57
std::string _arg3;
57
58
};
59
+ ```
60
+
61
+ This node should be registered as shown further:
62
+
63
+ ``` C++
64
+ BehaviorTreeFactory factory;
65
+
66
+ // A node builder is a functor that creates a std::unique_ptr<TreeNode>.
67
+ // Using lambdas or std::bind, we can easily "inject" additional arguments.
68
+ NodeBuilder builder_A =
69
+ [](const std::string& name, const NodeConfiguration& config)
70
+ {
71
+ return std::make_unique<Action_A>( name, config, 42, 3.14, "hello world" );
72
+ };
73
+
74
+ // BehaviorTreeFactory::registerBuilder is a more general way to
75
+ // register a custom node.
76
+ factory.registerBuilder<Action_A>( " Action_A" , builder_A);
77
+
78
+ // Register more custom nodes, if needed.
79
+ // ....
80
+
81
+ // The rest of your code, where you create and tick the tree, goes here.
82
+ // ....
83
+ ```
84
+
85
+ ## Method 2: use an init method
86
+
87
+ Alternatively, you may call an init method before ticking the tree.
88
+
89
+ ``` C++
58
90
59
- // Action_B implements an init(...) method that must be called once
60
- // before the first tick()
61
91
class Action_B : public SyncActionNode
62
92
{
63
93
64
94
public:
95
+ // The constructor looks as usual.
65
96
Action_B(const std::string& name, const NodeConfiguration& config):
66
97
SyncActionNode(name, config) {}
67
98
68
- // we want this method to be called ONCE and BEFORE the first tick()
69
- void init( int arg1, double arg2, std::string arg3 )
99
+ // We want this method to be called ONCE and BEFORE the first tick()
100
+ void init( int arg1, double arg2, const std::string& arg3 )
70
101
{
71
102
_arg1 = (arg1);
72
103
_arg2 = (arg2);
73
104
_arg3 = (arg3);
74
105
}
75
106
76
- NodeStatus tick() override
77
- {
78
- std::cout << "Action_B: " << _arg1 << " / " << _arg2 << " / "
79
- << _arg3 << std::endl;
80
- return NodeStatus::SUCCESS;
81
- }
82
107
// this example doesn't require any port
83
108
static PortsList providedPorts() { return {}; }
84
109
110
+ // tick() can access the private members
111
+ NodeStatus tick() override;
112
+
85
113
private:
86
114
int _ arg1;
87
115
double _ arg2;
88
116
std::string _ arg3;
89
117
};
90
118
```
91
119
92
- The way we register and initialize them in our ` main ` is slightly different.
93
-
120
+ The way we register and initialize Action_B is slightly different:
94
121
95
122
``` C++
96
- static const char * xml_text = R"(
97
-
98
- <root >
99
- <BehaviorTree>
100
- <Sequence>
101
- <Action_A/>
102
- <Action_B/>
103
- </Sequence>
104
- </BehaviorTree>
105
- </root>
106
- )" ;
107
-
108
- int main ()
109
- {
110
- BehaviorTreeFactory factory;
111
123
112
- // A node builder is nothing more than a function pointer to create a
113
- // std::unique_ptr<TreeNode>.
114
- // Using lambdas or std::bind, we can easily "inject" additional arguments.
115
- NodeBuilder builder_A =
116
- [](const std::string& name, const NodeConfiguration& config)
117
- {
118
- return std::make_unique<Action_A>( name, config, 42, 3.14, "hello world" );
119
- };
124
+ BehaviorTreeFactory factory;
120
125
121
- // BehaviorTreeFactory::registerBuilder is a more general way to
122
- // register a custom node.
123
- factory.registerBuilder<Action_A >( "Action_A", builder_A );
126
+ // The regitration of Action_B is done as usual, but remember
127
+ // that we still need to call Action_B::init()
128
+ factory.registerNodeType<Action_B >( " Action_B " );
124
129
125
- // The regitration of Action_B is done as usual, but remember
126
- // that we still need to call Action_B::init()
127
- factory.registerNodeType<Action_B>( "Action_B" );
130
+ // Register more custom nodes, if needed.
131
+ // ....
128
132
129
- auto tree = factory.createTreeFromText(xml_text);
133
+ // Create the whole tree
134
+ auto tree = factory.createTreeFromText(xml_text);
130
135
131
- // Iterate through all the nodes and call init() if it is an Action_B
132
- for( auto& node: tree.nodes )
136
+ // Iterate through all the nodes and call init() if it is an Action_B
137
+ for ( auto & node: tree.nodes )
138
+ {
139
+ // Not a typo: it is "=", not "=="
140
+ if( auto action_B = dynamic_cast<Action_B*>( node.get() ))
133
141
{
134
- if( auto action_B_node = dynamic_cast<Action_B*>( node.get() ))
135
- {
136
- action_B_node->init( 69, 9.99, "interesting_value" );
137
- }
142
+ action_B->init( 42, 3.14, "hello world");
138
143
}
139
-
140
- tree.tickRoot();
141
-
142
- return 0;
143
144
}
144
145
145
-
146
- /* Expected output:
147
-
148
- Action_A: 42 / 3.14 / hello world
149
- Action_B: 69 / 9.99 / interesting_value
150
- */
151
-
146
+ // The rest of your code, where you tick the tree, goes here.
147
+ // ....
152
148
```
153
149
154
150
0 commit comments