Skip to content

Commit 1359c77

Browse files
committed
Centralize expression evaluation into BTExpression object. delta variable is now accessible into expressions
1 parent 29db10e commit 1359c77

File tree

6 files changed

+112
-62
lines changed

6 files changed

+112
-62
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ The callable condition node is a *leaf* node. The node calls a function from an
142142
- `method_name` : name of the function to call in the *method owner node*. Default is *empty*,
143143
- `method_arguments`: array of arguments to pass when calling the function. Arguments are expressions that will be evaluated by Godot Engine at runtime to produce the desired value. See [Godot Expression](https://docs.godotengine.org/en/latest/classes/class_expression.html) for details. In expression, user has access to two predefined variables:
144144
- `actor`: the node the tree is describing action for,
145-
- `blackboard`: the tree blackboard.
145+
- `blackboard`: the tree blackboard,
146+
- `delta`: the *_process* or *_physics_process* delta value, as a `float`.
146147
Number and types of arguments must match function prototype, or an error will occurs at runtime. Default is an *empty array* meaning no argument.
147148

148149
### ![icon](addons/yet_another_behavior_tree/src/Assets/Icons/btconditionblackboardkeyexists.png) BTConditionBlackboardKeyExists
@@ -189,7 +190,8 @@ The callable action node is a *leaf* node. At each tick, the node calls a functi
189190
- `method_name` : name of the function to call in the *method owner node*. Default is *empty*,
190191
- `method_arguments`: array of arguments to pass when calling the function. Arguments are expressions that will be evaluated by Godot Engine at runtime to produce the desired value. See [Godot Expression](https://docs.godotengine.org/en/latest/classes/class_expression.html) for details. In expression, user has access to two predefined variables:
191192
- `actor`: the node the tree is describing action for,
192-
- `blackboard`: the tree blackboard.
193+
- `blackboard`: the tree blackboard,
194+
- `delta`: the *_process* or *_physics_process* delta value, as a `float`.
193195
Number and types of arguments must match function prototype, or an error will occurs at runtime. Default is an *empty array* meaning no argument.
194196

195197

@@ -213,7 +215,8 @@ The blackboard set action node is a *leaf* node. It allows to set a value in the
213215
- `blackboard_key`: name of the key that must be set,
214216
- `expression` : an expression representing the value to associated to the given key. The expression will be evaluated by Godot Engine during child execution. It should be simple. See [Godot Expression](https://docs.godotengine.org/en/latest/classes/class_expression.html) for details. In expression, user has access to two predefined variables:
215217
- `actor`: the node the tree is describing action for,
216-
- `blackboard`: the tree blackboard
218+
- `blackboard`: the tree blackboard,
219+
- `delta`: the *_process* or *_physics_process* delta value, as a `float`.
217220
- `can_overwrite_value` : a boolean indicating if the value must be overwritten if it already exists.
218221

219222
⚠️ Due to GDScript 2.0 restrictions, only string type keys can be set, since its not possible to export Variant variables.

addons/yet_another_behavior_tree/src/Nodes/Leaves/BTActionBlackboardSet.gd

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ class_name BTActionBlackboardSet
1818

1919
@export_multiline var expression:String = "" :
2020
set(value):
21-
expression = value
22-
update_configuration_warnings()
21+
if value != expression:
22+
expression = value
23+
_update_expression()
24+
update_configuration_warnings()
2325

2426
@export var can_overwrite_value:bool = false
2527

@@ -31,7 +33,7 @@ class_name BTActionBlackboardSet
3133
# Variables privées
3234
#------------------------------------------
3335

34-
var _parsed_compared_value:Variant
36+
var _expression:BTExpression = BTExpression.new()
3537

3638
#------------------------------------------
3739
# Fonctions Godot redéfinies
@@ -54,7 +56,7 @@ func _get_configuration_warnings() -> PackedStringArray:
5456

5557
func tick(actor:Node2D, blackboard:BTBlackboard) -> int:
5658
if can_overwrite_value or not blackboard.has_data(blackboard_key):
57-
var value:Variant = _execute_expression(actor, blackboard)
59+
var value:Variant = _expression.evaluate(actor, blackboard)
5860
blackboard.set_data(blackboard_key, value)
5961
return BTTickResult.SUCCESS
6062

@@ -77,22 +79,7 @@ func _expression_key_is_set() -> bool:
7779
return expression != null and not expression.is_empty()
7880

7981
func _expression_is_valid() -> bool:
80-
return _parse_expression() != null
81-
82-
func _parse_expression() -> Expression:
83-
var expr:Expression = Expression.new()
84-
var parse_code:int = expr.parse(expression, ["actor", "blackboard"])
85-
if parse_code != OK:
86-
push_error("Unable to parse expression '%s' : %s" % [expression, expr.get_error_text()])
87-
return null
88-
return expr
89-
90-
func _execute_expression(actor:Node2D, blackboard:BTBlackboard) -> Variant:
91-
var result:Variant = null
92-
var expr:Expression = _parse_expression()
93-
if expr != null:
94-
result = expr.execute([actor, blackboard], self, true)
95-
if expr.has_execute_failed():
96-
result = null
97-
push_error("Unable to execute expression '%s' : %s" % [expression, expr.get_error_text()])
98-
return result
82+
return _expression.is_valid()
83+
84+
func _update_expression() -> void:
85+
_expression.expression = expression

addons/yet_another_behavior_tree/src/Nodes/Leaves/BTActionCallable.gd

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@ class_name BTActionCallable
3333
#------------------------------------------
3434

3535
var _method_owner:Node
36+
var _cached_method_arguments:Array[String] = []
37+
var _argument_expression:Array[BTExpression] = []
3638

3739
#------------------------------------------
3840
# Fonctions Godot redéfinies
3941
#------------------------------------------
4042

4143
func _ready() -> void:
4244
_update_method_owner_from_path()
45+
_update_argument_expressions()
4346

4447
func _get_configuration_warnings() -> PackedStringArray:
4548
var warnings:PackedStringArray = []
@@ -55,7 +58,8 @@ func _get_configuration_warnings() -> PackedStringArray:
5558
#------------------------------------------
5659

5760
func tick(actor:Node2D, blackboard:BTBlackboard) -> int:
58-
var arguments:Array[Variant] = method_arguments.map(func(e): return _execute_expression(e, actor, blackboard))
61+
_update_argument_expressions()
62+
var arguments:Array[Variant] = _argument_expression.map(func(expr):return expr.evaluate(actor, blackboard))
5963
var result:Variant = _method_owner.callv(method_name, arguments)
6064
if result is bool:
6165
return BTTickResult.SUCCESS if result else BTTickResult.FAILURE
@@ -86,20 +90,11 @@ func _update_method_owner_from_path() -> void:
8690
# Fallback : si le chemin donné n'était pas relatif à la scene courante, on le check en absolu
8791
_method_owner = get_tree().current_scene.get_node_or_null(method_owner_path)
8892

89-
func _parse_expression(string_expr:String) -> Expression:
90-
var expr:Expression = Expression.new()
91-
var parse_code:int = expr.parse(string_expr, ["actor", "blackboard"])
92-
if parse_code != OK:
93-
push_error("Unable to parse expression '%s' : %s" % [string_expr, expr.get_error_text()])
94-
return null
95-
return expr
96-
97-
func _execute_expression(string_expr:String, actor:Node2D, blackboard:BTBlackboard) -> Variant:
98-
var result:Variant = null
99-
var expr:Expression = _parse_expression(string_expr)
100-
if expr != null:
101-
result = expr.execute([actor, blackboard], self, true)
102-
if expr.has_execute_failed():
103-
result = null
104-
push_error("Unable to execute expression '%s' : %s" % [string_expr, expr.get_error_text()])
105-
return result
93+
func _update_argument_expressions() -> void:
94+
if _cached_method_arguments != method_arguments:
95+
_cached_method_arguments = Array(method_arguments)
96+
_argument_expression.clear()
97+
for expr in _cached_method_arguments:
98+
var btexpression:BTExpression = BTExpression.new()
99+
btexpression.expression = expr
100+
_argument_expression.append(btexpression)

addons/yet_another_behavior_tree/src/Nodes/Leaves/BTConditionCallable.gd

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@ class_name BTConditionCallable
3333
#------------------------------------------
3434

3535
var _method_owner:Node
36+
var _cached_method_arguments:Array[String] = []
37+
var _argument_expression:Array[BTExpression] = []
3638

3739
#------------------------------------------
3840
# Fonctions Godot redéfinies
3941
#------------------------------------------
4042

4143
func _ready() -> void:
4244
_update_method_owner_from_path()
45+
_update_argument_expressions()
4346

4447
func _get_configuration_warnings() -> PackedStringArray:
4548
var warnings:PackedStringArray = []
@@ -55,7 +58,8 @@ func _get_configuration_warnings() -> PackedStringArray:
5558
#------------------------------------------
5659

5760
func tick(actor:Node2D, blackboard:BTBlackboard) -> int:
58-
var arguments:Array[Variant] = method_arguments.map(func(e): return _execute_expression(e, actor, blackboard))
61+
_update_argument_expressions()
62+
var arguments:Array[Variant] = _argument_expression.map(func(expr):return expr.evaluate(actor, blackboard))
5963
var result:bool = _method_owner.callv(method_name, arguments)
6064
return BTTickResult.SUCCESS if result else BTTickResult.FAILURE
6165

@@ -82,20 +86,11 @@ func _update_method_owner_from_path() -> void:
8286
# Fallback : si le chemin donné n'était pas relatif à la scene courante, on le check en absolu
8387
_method_owner = get_tree().current_scene.get_node_or_null(method_owner_path)
8488

85-
func _parse_expression(string_expr:String) -> Expression:
86-
var expr:Expression = Expression.new()
87-
var parse_code:int = expr.parse(string_expr, ["actor", "blackboard"])
88-
if parse_code != OK:
89-
push_error("Unable to parse expression '%s' : %s" % [string_expr, expr.get_error_text()])
90-
return null
91-
return expr
92-
93-
func _execute_expression(string_expr:String, actor:Node2D, blackboard:BTBlackboard) -> Variant:
94-
var result:Variant = null
95-
var expr:Expression = _parse_expression(string_expr)
96-
if expr != null:
97-
result = expr.execute([actor, blackboard], self, true)
98-
if expr.has_execute_failed():
99-
result = null
100-
push_error("Unable to execute expression '%s' : %s" % [string_expr, expr.get_error_text()])
101-
return result
89+
func _update_argument_expressions() -> void:
90+
if _cached_method_arguments != method_arguments:
91+
_cached_method_arguments = Array(method_arguments)
92+
_argument_expression.clear()
93+
for expr in _cached_method_arguments:
94+
var btexpression:BTExpression = BTExpression.new()
95+
btexpression.expression = expr
96+
_argument_expression.append(btexpression)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
extends RefCounted
2+
class_name BTExpression
3+
4+
#------------------------------------------
5+
# Signaux
6+
#------------------------------------------
7+
8+
#------------------------------------------
9+
# Exports
10+
#------------------------------------------
11+
12+
#------------------------------------------
13+
# Variables publiques
14+
#------------------------------------------
15+
16+
var expression:String = "":
17+
set(value):
18+
if value != expression:
19+
expression = value
20+
_expression = _parse_expression(expression)
21+
22+
#------------------------------------------
23+
# Variables privées
24+
#------------------------------------------
25+
26+
var _expression:Expression
27+
28+
#------------------------------------------
29+
# Fonctions Godot redéfinies
30+
#------------------------------------------
31+
32+
#------------------------------------------
33+
# Fonctions publiques
34+
#------------------------------------------
35+
36+
func is_valid() -> bool:
37+
return _expression != null
38+
39+
func evaluate(actor:Node2D, blackboard:BTBlackboard) -> Variant:
40+
var arguments:Array[Variant] = [actor, blackboard, blackboard.get_delta()]
41+
return _execute_expression(arguments)
42+
43+
#------------------------------------------
44+
# Fonctions privées
45+
#------------------------------------------
46+
47+
func _parse_expression(string_expr:String) -> Expression:
48+
var expr:Expression = Expression.new()
49+
var parse_code:int = expr.parse(string_expr, ["actor", "blackboard", "delta"])
50+
if parse_code != OK:
51+
push_error("Unable to parse expression '%s' : %s" % [string_expr, expr.get_error_text()])
52+
return null
53+
return expr
54+
55+
func _execute_expression(arguments:Array[Variant]) -> Variant:
56+
var result:Variant = null
57+
if _expression == null:
58+
_expression = _parse_expression(expression)
59+
if _expression != null:
60+
result = _expression.execute(arguments, self, true)
61+
if _expression.has_execute_failed():
62+
result = null
63+
push_error("Unable to execute expression '%s' : %s" % [expression, _expression.get_error_text()])
64+
return result

project.godot

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ _global_script_classes=[{
6969
"language": &"GDScript",
7070
"path": "res://addons/yet_another_behavior_tree/src/Nodes/Decorators/BTDecorator.gd"
7171
}, {
72+
"base": "RefCounted",
73+
"class": &"BTExpression",
74+
"language": &"GDScript",
75+
"path": "res://addons/yet_another_behavior_tree/src/Utils/BTExpression.gd"
76+
}, {
7277
"base": "BTDecorator",
7378
"class": &"BTFailure",
7479
"language": &"GDScript",
@@ -172,6 +177,7 @@ _global_script_class_icons={
172177
"BTConditionBlackboardValuesComparison": "res://addons/yet_another_behavior_tree/src/Assets/Icons/btconditionblackboardvaluescomparison.png",
173178
"BTConditionCallable": "res://addons/yet_another_behavior_tree/src/Assets/Icons/btconditioncallable.png",
174179
"BTDecorator": "res://addons/yet_another_behavior_tree/src/Assets/Icons/btdecorator.png",
180+
"BTExpression": "",
175181
"BTFailure": "res://addons/yet_another_behavior_tree/src/Assets/Icons/btfailure.png",
176182
"BTInverter": "res://addons/yet_another_behavior_tree/src/Assets/Icons/btinverter.png",
177183
"BTLeaf": "res://addons/yet_another_behavior_tree/src/Assets/Icons/btleaf.png",

0 commit comments

Comments
 (0)