Behavior Tree addon

This commit is contained in:
Tony Bark 2023-01-21 04:04:13 -05:00
parent 5de5a0c315
commit 5fa863114a
50 changed files with 1762 additions and 3 deletions

View file

@ -0,0 +1,8 @@
extends BeehaveTree
class_name BeehaveNode, "../icons/action.svg"
enum { SUCCESS, FAILURE, RUNNING }
func tick(actor, blackboard):
pass

View file

@ -0,0 +1,84 @@
extends BeehaveTree
class_name BeehaveRoot, "../icons/tree.svg"
const Blackboard = preload("../blackboard.gd")
const SUCCESS = 0
const FAILURE = 1
const RUNNING = 2
enum ProcessMode {
PHYSICS_PROCESS,
IDLE,
MANUAL
}
export (ProcessMode) var process_mode = ProcessMode.PHYSICS_PROCESS setget set_process_mode
export (bool) var enabled = true
export (NodePath) var actor_node_path
var actor : Node
onready var blackboard = Blackboard.new()
func _ready():
if self.get_child_count() != 1:
push_error("Beehave error: Root %s should have one child (NodePath: %s)" % [self.name, self.get_path()])
disable()
return
actor = get_parent()
if actor_node_path:
actor = get_node(actor_node_path)
set_process_mode(self.process_mode)
func _process(delta):
tick(delta)
func _physics_process(delta):
tick(delta)
func tick(delta):
blackboard.set("delta", delta)
var status = self.get_child(0).tick(actor, blackboard)
if status != RUNNING:
blackboard.set("running_action", null)
func get_running_action():
if blackboard.has("running_action"):
return blackboard.get("running_action")
return null
func get_last_condition():
if blackboard.has("last_condition"):
return blackboard.get("last_condition")
return null
func get_last_condition_status():
if blackboard.has("last_condition_status"):
var status = blackboard.get("last_condition_status")
if status == SUCCESS:
return "SUCCESS"
elif status == FAILURE:
return "FAILURE"
else:
return "RUNNING"
return ""
func enable():
self.enabled = true
set_process_mode(self.process_mode)
func disable():
self.enabled = false
set_process(self.enabled)
set_physics_process(self.enabled)
func set_process_mode(value):
process_mode = value
set_process(process_mode == ProcessMode.IDLE)
set_physics_process(process_mode == ProcessMode.PHYSICS_PROCESS)

View file

@ -0,0 +1,3 @@
extends Node
class_name BeehaveTree

View file

@ -0,0 +1,8 @@
extends BeehaveNode
class_name Composite, "../../icons/category_composite.svg"
func _ready():
if self.get_child_count() < 1:
push_error("BehaviorTree Error: Composite %s should have at least one child (NodePath: %s)" % [self.name, self.get_path()])

View file

@ -0,0 +1,18 @@
extends Composite
class_name SelectorComposite, "../../icons/selector.svg"
func tick(actor, blackboard):
for c in get_children():
var response = c.tick(actor, blackboard)
if c is ConditionLeaf:
blackboard.set("last_condition", c)
blackboard.set("last_condition_status", response)
if response != FAILURE:
if c is ActionLeaf and response == RUNNING:
blackboard.set("running_action", c)
return response
return FAILURE

View file

@ -0,0 +1,33 @@
# Special implementation of a selector that will
# "wait" for running nodes and will not re-attempt
# to execute previous nodes until that node is either
# FAILED or SUCCEEDED
extends Composite
class_name SelectorStarComposite, "../../icons/selector_star.svg"
var last_execution_index = 0
func tick(actor, blackboard):
for c in get_children():
if c.get_index() < last_execution_index:
continue
var response = c.tick(actor, blackboard)
if c is ConditionLeaf:
blackboard.set("last_condition", c)
blackboard.set("last_condition_status", response)
if response != FAILURE:
if c is ActionLeaf and response == RUNNING:
blackboard.set("running_action", c)
if response == SUCCESS:
last_execution_index = 0
return response
else:
last_execution_index += 1
last_execution_index = 0
return FAILURE

View file

@ -0,0 +1,18 @@
extends Composite
class_name SequenceComposite, "../../icons/sequencer.svg"
func tick(actor, blackboard):
for c in get_children():
var response = c.tick(actor, blackboard)
if c is ConditionLeaf:
blackboard.set("last_condition", c)
blackboard.set("last_condition_status", response)
if response != SUCCESS:
if c is ActionLeaf and response == RUNNING:
blackboard.set("running_action", c)
return response
return SUCCESS

View file

@ -0,0 +1,35 @@
# Special implementation of sequencer who will execute
# successful nodes only once until all nodes were successful
extends Composite
class_name SequenceStarComposite, "../../icons/sequencer_star.svg"
var successful_index = 0
func tick(actor, blackboard):
for c in get_children():
if c.get_index() < successful_index:
continue
var response = c.tick(actor, blackboard)
if c is ConditionLeaf:
blackboard.set("last_condition", c)
blackboard.set("last_condition_status", response)
if response != SUCCESS:
if response == FAILURE:
successful_index = 0
if c is ActionLeaf and response == RUNNING:
blackboard.set("running_action", c)
return response
else:
successful_index += 1
if successful_index == get_child_count():
successful_index = 0
return SUCCESS
else:
successful_index = 0
return FAILURE

View file

@ -0,0 +1,8 @@
extends BeehaveNode
class_name Decorator, "../../icons/category_decorator.svg"
func _ready():
if self.get_child_count() != 1:
push_error("Beehave Error: Decorator %s should have only one child (NodePath: %s)" % [self.name, self.get_path()])

View file

@ -0,0 +1,11 @@
extends Decorator
class_name AlwaysFailDecorator, "../../icons/fail.svg"
func tick(action, blackboard):
for c in get_children():
var response = c.tick(action, blackboard)
if response == RUNNING:
return RUNNING
return FAILURE

View file

@ -0,0 +1,17 @@
extends Decorator
class_name InverterDecorator, "../../icons/inverter.svg"
func tick(action, blackboard):
for c in get_children():
var response = c.tick(action, blackboard)
if response == SUCCESS:
return FAILURE
if response == FAILURE:
return SUCCESS
if c is Leaf and response == RUNNING:
blackboard.set("running_action", c)
return RUNNING

View file

@ -0,0 +1,19 @@
extends Decorator
class_name LimiterDecorator, "../../icons/limiter.svg"
onready var cache_key = 'limiter_%s' % self.get_instance_id()
export (float) var max_count = 0
func tick(actor, blackboard):
var current_count = blackboard.get(cache_key)
if current_count == null:
current_count = 0
if current_count <= max_count:
blackboard.set(cache_key, current_count + 1)
return self.get_child(0).tick(actor, blackboard)
else:
return FAILED

View file

@ -0,0 +1,11 @@
extends Decorator
class_name AlwaysSucceedDecorator, "../../icons/succeed.svg"
func tick(action, blackboard):
for c in get_children():
var response = c.tick(action, blackboard)
if response == RUNNING:
return RUNNING
return SUCCESS

View file

@ -0,0 +1,3 @@
extends Leaf
class_name ActionLeaf, "../../icons/action.svg"

View file

@ -0,0 +1,3 @@
extends Leaf
class_name ConditionLeaf, "../../icons/condition.svg"

View file

@ -0,0 +1,3 @@
extends BeehaveNode
class_name Leaf, "../../icons/action.svg"