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
40
addons/beehave/nodes/composites/composite.gd
Normal file
40
addons/beehave/nodes/composites/composite.gd
Normal file
|
@ -0,0 +1,40 @@
|
|||
## A Composite node controls the flow of execution of its children in a specific manner.
|
||||
@tool
|
||||
@icon("../../icons/category_composite.svg")
|
||||
class_name Composite extends BeehaveNode
|
||||
|
||||
|
||||
var running_child: BeehaveNode = null
|
||||
|
||||
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if self.get_child_count() < 1:
|
||||
push_warning("BehaviorTree Error: Composite %s should have at least one child (NodePath: %s)" % [self.name, self.get_path()])
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = super._get_configuration_warnings()
|
||||
|
||||
if get_children().filter(func(x): return x is BeehaveNode).size() < 2:
|
||||
warnings.append("Any composite node should have at least two children. Otherwise it is not useful.")
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
if running_child != null:
|
||||
running_child.interrupt(actor, blackboard)
|
||||
running_child = null
|
||||
|
||||
|
||||
func after_run(actor: Node, blackboard: Blackboard) -> void:
|
||||
running_child = null
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"Composite")
|
||||
return classes
|
57
addons/beehave/nodes/composites/selector.gd
Normal file
57
addons/beehave/nodes/composites/selector.gd
Normal file
|
@ -0,0 +1,57 @@
|
|||
## Selector nodes will attempt to execute each of its children until one of
|
||||
## them return `SUCCESS`. If all children return `FAILURE`, this node will also
|
||||
## return `FAILURE`.
|
||||
## If a child returns `RUNNING` it will tick again.
|
||||
@tool
|
||||
@icon("../../icons/selector.svg")
|
||||
class_name SelectorComposite extends Composite
|
||||
|
||||
var last_execution_index: int = 0
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < last_execution_index:
|
||||
continue
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
c.after_run(actor, blackboard)
|
||||
return SUCCESS
|
||||
FAILURE:
|
||||
last_execution_index += 1
|
||||
c.after_run(actor, blackboard)
|
||||
RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
return FAILURE
|
||||
|
||||
|
||||
func after_run(actor: Node, blackboard: Blackboard) -> void:
|
||||
last_execution_index = 0
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
after_run(actor, blackboard)
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SelectorComposite")
|
||||
return classes
|
88
addons/beehave/nodes/composites/selector_random.gd
Normal file
88
addons/beehave/nodes/composites/selector_random.gd
Normal file
|
@ -0,0 +1,88 @@
|
|||
## This node will attempt to execute all of its children just like a
|
||||
## [code]SelectorStar[/code] would, with the exception that the children
|
||||
## will be executed in a random order.
|
||||
@tool
|
||||
@icon("../../icons/selector_random.svg")
|
||||
class_name SelectorRandomComposite extends Composite
|
||||
|
||||
## Sets a predicable seed
|
||||
@export var random_seed:int = 0:
|
||||
set(rs):
|
||||
random_seed = rs
|
||||
if random_seed != 0:
|
||||
seed(random_seed)
|
||||
else:
|
||||
randomize()
|
||||
|
||||
|
||||
## A shuffled list of the children that will be executed in reverse order.
|
||||
var _children_bag: Array[Node] = []
|
||||
var c: Node
|
||||
|
||||
func _ready() -> void:
|
||||
if random_seed == 0:
|
||||
randomize()
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
if _children_bag.is_empty():
|
||||
_reset()
|
||||
|
||||
# We need to traverse the array in reverse since we will be manipulating it.
|
||||
for i in _get_reversed_indexes():
|
||||
c = _children_bag[i]
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
_children_bag.erase(c)
|
||||
c.after_run(actor, blackboard)
|
||||
return SUCCESS
|
||||
FAILURE:
|
||||
_children_bag.erase(c)
|
||||
c.after_run(actor, blackboard)
|
||||
RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
return FAILURE
|
||||
|
||||
|
||||
func after_run(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
after_run(actor, blackboard)
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _get_reversed_indexes() -> Array[int]:
|
||||
var reversed: Array[int]
|
||||
reversed.assign(range(_children_bag.size()))
|
||||
reversed.reverse()
|
||||
return reversed
|
||||
|
||||
|
||||
## Generates a new shuffled list of the children.
|
||||
func _reset() -> void:
|
||||
_children_bag = get_children().duplicate()
|
||||
_children_bag.shuffle()
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SelectorRandomComposite")
|
||||
return classes
|
45
addons/beehave/nodes/composites/selector_reactive.gd
Normal file
45
addons/beehave/nodes/composites/selector_reactive.gd
Normal file
|
@ -0,0 +1,45 @@
|
|||
## Selector Reactive nodes will attempt to execute each of its children until one of
|
||||
## them return `SUCCESS`. If all children return `FAILURE`, this node will also
|
||||
## return `FAILURE`.
|
||||
## If a child returns `RUNNING` it will restart.
|
||||
@tool
|
||||
@icon("../../icons/selector_reactive.svg")
|
||||
class_name SelectorReactiveComposite extends Composite
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
# Interrupt any child that was RUNNING before.
|
||||
if c != running_child:
|
||||
interrupt(actor, blackboard)
|
||||
c.after_run(actor, blackboard)
|
||||
return SUCCESS
|
||||
FAILURE:
|
||||
c.after_run(actor, blackboard)
|
||||
RUNNING:
|
||||
if c != running_child:
|
||||
interrupt(actor, blackboard)
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
return FAILURE
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SelectorReactiveComposite")
|
||||
return classes
|
64
addons/beehave/nodes/composites/sequence.gd
Normal file
64
addons/beehave/nodes/composites/sequence.gd
Normal file
|
@ -0,0 +1,64 @@
|
|||
## Sequence nodes will attempt to execute all of its children and report
|
||||
## `SUCCESS` in case all of the children report a `SUCCESS` status code.
|
||||
## If at least one child reports a `FAILURE` status code, this node will also
|
||||
## return `FAILURE` and restart.
|
||||
## In case a child returns `RUNNING` this node will tick again.
|
||||
@tool
|
||||
@icon("../../icons/sequence.svg")
|
||||
class_name SequenceComposite extends Composite
|
||||
|
||||
|
||||
var successful_index: int = 0
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
|
||||
if c.get_index() < successful_index:
|
||||
continue
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
successful_index += 1
|
||||
c.after_run(actor, blackboard)
|
||||
FAILURE:
|
||||
# Interrupt any child that was RUNNING before.
|
||||
interrupt(actor, blackboard)
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
RUNNING:
|
||||
if c != running_child:
|
||||
if running_child != null:
|
||||
running_child.interrupt(actor, blackboard)
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
_reset()
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
func _reset() -> void:
|
||||
successful_index = 0
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SequenceComposite")
|
||||
return classes
|
96
addons/beehave/nodes/composites/sequence_random.gd
Normal file
96
addons/beehave/nodes/composites/sequence_random.gd
Normal file
|
@ -0,0 +1,96 @@
|
|||
## This node will attempt to execute all of its children just like a
|
||||
## [code]SequenceStar[/code] would, with the exception that the children
|
||||
## will be executed in a random order.
|
||||
@tool
|
||||
@icon("../../icons/sequence_random.svg")
|
||||
class_name SequenceRandomComposite extends Composite
|
||||
|
||||
|
||||
## Whether the sequence should start where it left off after a previous failure.
|
||||
@export var resume_on_failure: bool = false
|
||||
## Whether the sequence should start where it left off after a previous interruption.
|
||||
@export var resume_on_interrupt: bool = false
|
||||
## Sets a predicable seed
|
||||
@export var random_seed: int = 0:
|
||||
set(rs):
|
||||
random_seed = rs
|
||||
if random_seed != 0:
|
||||
seed(random_seed)
|
||||
else:
|
||||
randomize()
|
||||
|
||||
## A shuffled list of the children that will be executed in reverse order.
|
||||
var _children_bag: Array[Node] = []
|
||||
var c: Node
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if random_seed == 0:
|
||||
randomize()
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
if _children_bag.is_empty():
|
||||
_reset()
|
||||
|
||||
# We need to traverse the array in reverse since we will be manipulating it.
|
||||
for i in _get_reversed_indexes():
|
||||
c = _children_bag[i]
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
_children_bag.erase(c)
|
||||
c.after_run(actor, blackboard)
|
||||
FAILURE:
|
||||
_children_bag.erase(c)
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func after_run(actor: Node, blackboard: Blackboard) -> void:
|
||||
if not resume_on_failure:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
if not resume_on_interrupt:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _get_reversed_indexes() -> Array[int]:
|
||||
var reversed: Array[int]
|
||||
reversed.assign(range(_children_bag.size()))
|
||||
reversed.reverse()
|
||||
return reversed
|
||||
|
||||
|
||||
## Generates a new shuffled list of the children.
|
||||
func _reset() -> void:
|
||||
_children_bag = get_children().duplicate()
|
||||
_children_bag.shuffle()
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SequenceRandomComposite")
|
||||
return classes
|
62
addons/beehave/nodes/composites/sequence_reactive.gd
Normal file
62
addons/beehave/nodes/composites/sequence_reactive.gd
Normal file
|
@ -0,0 +1,62 @@
|
|||
## Reactive Sequence nodes will attempt to execute all of its children and report
|
||||
## `SUCCESS` in case all of the children report a `SUCCESS` status code.
|
||||
## If at least one child reports a `FAILURE` status code, this node will also
|
||||
## return `FAILURE` and restart.
|
||||
## In case a child returns `RUNNING` this node will restart.
|
||||
@tool
|
||||
@icon("../../icons/sequence_reactive.svg")
|
||||
class_name SequenceReactiveComposite extends Composite
|
||||
|
||||
|
||||
var successful_index: int = 0
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < successful_index:
|
||||
continue
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
successful_index += 1
|
||||
c.after_run(actor, blackboard)
|
||||
FAILURE:
|
||||
# Interrupt any child that was RUNNING before.
|
||||
interrupt(actor, blackboard)
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
RUNNING:
|
||||
_reset()
|
||||
if running_child != c:
|
||||
interrupt(actor, blackboard)
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
_reset()
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
func _reset() -> void:
|
||||
successful_index = 0
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SequenceReactiveComposite")
|
||||
return classes
|
58
addons/beehave/nodes/composites/sequence_star.gd
Normal file
58
addons/beehave/nodes/composites/sequence_star.gd
Normal file
|
@ -0,0 +1,58 @@
|
|||
## Sequence Star nodes will attempt to execute all of its children and report
|
||||
## `SUCCESS` in case all of the children report a `SUCCESS` status code.
|
||||
## If at least one child reports a `FAILURE` status code, this node will also
|
||||
## return `FAILURE` and tick again.
|
||||
## In case a child returns `RUNNING` this node will restart.
|
||||
@tool
|
||||
@icon("../../icons/sequence_reactive.svg")
|
||||
class_name SequenceStarComposite extends Composite
|
||||
|
||||
|
||||
var successful_index: int = 0
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < successful_index:
|
||||
continue
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
successful_index += 1
|
||||
c.after_run(actor, blackboard)
|
||||
FAILURE:
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
_reset()
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _reset() -> void:
|
||||
successful_index = 0
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SequenceStarComposite")
|
||||
return classes
|
Loading…
Add table
Add a link
Reference in a new issue