Add a behavior tree to the enemy
Add the Beehave addon. Make the enemy do the same thing as before but with a behavior tree.
This commit is contained in:
parent
09f6925a00
commit
1aed988149
92 changed files with 4025 additions and 25 deletions
13
addons/beehave/nodes/leaves/action.gd
Normal file
13
addons/beehave/nodes/leaves/action.gd
Normal file
|
@ -0,0 +1,13 @@
|
|||
## Actions are leaf nodes that define a task to be performed by an actor.
|
||||
## Their execution can be long running, potentially being called across multiple
|
||||
## frame executions. In this case, the node should return `RUNNING` until the
|
||||
## action is completed.
|
||||
@tool
|
||||
@icon("../../icons/action.svg")
|
||||
class_name ActionLeaf extends Leaf
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"ActionLeaf")
|
||||
return classes
|
61
addons/beehave/nodes/leaves/blackboard_compare.gd
Normal file
61
addons/beehave/nodes/leaves/blackboard_compare.gd
Normal file
|
@ -0,0 +1,61 @@
|
|||
## Compares two values using the specified comparison operator.
|
||||
## Returns [code]FAILURE[/code] if any of the expression fails or the
|
||||
## comparison operation returns [code]false[/code], otherwise it returns [code]SUCCESS[/code].
|
||||
@tool
|
||||
class_name BlackboardCompareCondition extends ConditionLeaf
|
||||
|
||||
|
||||
enum Operators {
|
||||
EQUAL,
|
||||
NOT_EQUAL,
|
||||
GREATER,
|
||||
LESS,
|
||||
GREATER_EQUAL,
|
||||
LESS_EQUAL,
|
||||
}
|
||||
|
||||
|
||||
## Expression represetning left operand.
|
||||
## This value can be any valid GDScript expression.
|
||||
## In order to use the existing blackboard keys for comparison,
|
||||
## use get_value("key_name") e.g. get_value("direction").length()
|
||||
@export_placeholder(EXPRESSION_PLACEHOLDER) var left_operand: String = ""
|
||||
## Comparison operator.
|
||||
@export_enum("==", "!=", ">", "<", ">=", "<=") var operator: int = 0
|
||||
## Expression represetning right operand.
|
||||
## This value can be any valid GDScript expression.
|
||||
## In order to use the existing blackboard keys for comparison,
|
||||
## use get_value("key_name") e.g. get_value("direction").length()
|
||||
@export_placeholder(EXPRESSION_PLACEHOLDER) var right_operand: String = ""
|
||||
|
||||
|
||||
@onready var _left_expression: Expression = _parse_expression(left_operand)
|
||||
@onready var _right_expression: Expression = _parse_expression(right_operand)
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var left: Variant = _left_expression.execute([], blackboard)
|
||||
|
||||
if _left_expression.has_execute_failed():
|
||||
return FAILURE
|
||||
|
||||
var right: Variant = _right_expression.execute([], blackboard)
|
||||
|
||||
if _right_expression.has_execute_failed():
|
||||
return FAILURE
|
||||
|
||||
var result: bool = false
|
||||
|
||||
match operator:
|
||||
Operators.EQUAL: result = left == right
|
||||
Operators.NOT_EQUAL: result = left != right
|
||||
Operators.GREATER: result = left > right
|
||||
Operators.LESS: result = left < right
|
||||
Operators.GREATER_EQUAL: result = left >= right
|
||||
Operators.LESS_EQUAL: result = left <= right
|
||||
|
||||
return SUCCESS if result else FAILURE
|
||||
|
||||
|
||||
func _get_expression_sources() -> Array[String]:
|
||||
return [left_operand, right_operand]
|
25
addons/beehave/nodes/leaves/blackboard_erase.gd
Normal file
25
addons/beehave/nodes/leaves/blackboard_erase.gd
Normal file
|
@ -0,0 +1,25 @@
|
|||
## Erases the specified key from the blackboard.
|
||||
## Returns [code]FAILURE[/code] if expression execution fails, otherwise [code]SUCCESS[/code].
|
||||
@tool
|
||||
class_name BlackboardEraseAction extends ActionLeaf
|
||||
|
||||
|
||||
## Expression representing a blackboard key.
|
||||
@export_placeholder(EXPRESSION_PLACEHOLDER) var key: String = ""
|
||||
|
||||
@onready var _key_expression: Expression = _parse_expression(key)
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var key_value: Variant = _key_expression.execute([], blackboard)
|
||||
|
||||
if _key_expression.has_execute_failed():
|
||||
return FAILURE
|
||||
|
||||
blackboard.erase_value(key_value)
|
||||
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func _get_expression_sources() -> Array[String]:
|
||||
return [key]
|
23
addons/beehave/nodes/leaves/blackboard_has.gd
Normal file
23
addons/beehave/nodes/leaves/blackboard_has.gd
Normal file
|
@ -0,0 +1,23 @@
|
|||
## Returns [code]FAILURE[/code] if expression execution fails or the specified key doesn't exist.
|
||||
## Returns [code]SUCCESS[/code] if blackboard has the specified key.
|
||||
@tool
|
||||
class_name BlackboardHasCondition extends ConditionLeaf
|
||||
|
||||
|
||||
## Expression representing a blackboard key.
|
||||
@export_placeholder(EXPRESSION_PLACEHOLDER) var key: String = ""
|
||||
|
||||
@onready var _key_expression: Expression = _parse_expression(key)
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var key_value: Variant = _key_expression.execute([], blackboard)
|
||||
|
||||
if _key_expression.has_execute_failed():
|
||||
return FAILURE
|
||||
|
||||
return SUCCESS if blackboard.has_value(key_value) else FAILURE
|
||||
|
||||
|
||||
func _get_expression_sources() -> Array[String]:
|
||||
return [key]
|
34
addons/beehave/nodes/leaves/blackboard_set.gd
Normal file
34
addons/beehave/nodes/leaves/blackboard_set.gd
Normal file
|
@ -0,0 +1,34 @@
|
|||
## Sets the specified key to the specified value.
|
||||
## Returns [code]FAILURE[/code] if expression execution fails, otherwise [code]SUCCESS[/code].
|
||||
@tool
|
||||
class_name BlackboardSetAction extends ActionLeaf
|
||||
|
||||
|
||||
## Expression representing a blackboard key.
|
||||
@export_placeholder(EXPRESSION_PLACEHOLDER) var key: String = ""
|
||||
## Expression representing a blackboard value to assign to the specified key.
|
||||
@export_placeholder(EXPRESSION_PLACEHOLDER) var value: String = ""
|
||||
|
||||
|
||||
@onready var _key_expression: Expression = _parse_expression(key)
|
||||
@onready var _value_expression: Expression = _parse_expression(value)
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var key_value: Variant = _key_expression.execute([], blackboard)
|
||||
|
||||
if _key_expression.has_execute_failed():
|
||||
return FAILURE
|
||||
|
||||
var value_value: Variant = _value_expression.execute([], blackboard)
|
||||
|
||||
if _value_expression.has_execute_failed():
|
||||
return FAILURE
|
||||
|
||||
blackboard.set_value(key_value, value_value)
|
||||
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func _get_expression_sources() -> Array[String]:
|
||||
return [key, value]
|
11
addons/beehave/nodes/leaves/condition.gd
Normal file
11
addons/beehave/nodes/leaves/condition.gd
Normal file
|
@ -0,0 +1,11 @@
|
|||
## Conditions are leaf nodes that either return SUCCESS or FAILURE depending on
|
||||
## a single simple condition. They should never return `RUNNING`.
|
||||
@tool
|
||||
@icon("../../icons/condition.svg")
|
||||
class_name ConditionLeaf extends Leaf
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"ConditionLeaf")
|
||||
return classes
|
46
addons/beehave/nodes/leaves/leaf.gd
Normal file
46
addons/beehave/nodes/leaves/leaf.gd
Normal file
|
@ -0,0 +1,46 @@
|
|||
## Base class for all leaf nodes of the tree.
|
||||
@tool
|
||||
@icon("../../icons/category_leaf.svg")
|
||||
class_name Leaf extends BeehaveNode
|
||||
|
||||
|
||||
const EXPRESSION_PLACEHOLDER: String = "Insert an expression..."
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = []
|
||||
|
||||
var children: Array[Node] = get_children()
|
||||
|
||||
if children.any(func(x): return x is BeehaveNode):
|
||||
warnings.append("Leaf nodes should not have any child nodes. They won't be ticked.")
|
||||
|
||||
for source in _get_expression_sources():
|
||||
var error_text: String = _parse_expression(source).get_error_text()
|
||||
if not error_text.is_empty():
|
||||
warnings.append("Expression `%s` is invalid! Error text: `%s`" % [source, error_text])
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
func _parse_expression(source: String) -> Expression:
|
||||
var result: Expression = Expression.new()
|
||||
var error: int = result.parse(source)
|
||||
|
||||
if not Engine.is_editor_hint() and error != OK:
|
||||
push_error(
|
||||
"[Leaf] Couldn't parse expression with source: `%s` Error text: `%s`" %\
|
||||
[source, result.get_error_text()]
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func _get_expression_sources() -> Array[String]: # virtual
|
||||
return []
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"Leaf")
|
||||
return classes
|
Loading…
Add table
Add a link
Reference in a new issue