gai-ca2/project/scripts/visualization/BehaviorTreeVisualizer.gd

108 lines
3.4 KiB
GDScript

class_name BehaviorTreeVisualizer
extends Window
const D_TREE_NODE: PackedScene = preload("res://scripts/visualization/d_tree_node.tscn")
@onready var graph_edit: GraphEdit = %GraphEdit
#
var behavior_tree: BehaviorTree
#
var x_spacing: int = 400
var y_spacing: int = 100
#
var all_nodes: Array[DTreeNode] = []
#
var current_lowest_node_pos: Vector2 = Vector2(0, 0)
func build_tree() -> void:
if not behavior_tree:
push_error("No behavior tree set.")
return
# reset current visualization
graph_edit.clear_connections()
for node in all_nodes:
node.queue_free()
all_nodes.clear()
var root_task: Task = behavior_tree.behavior_tree
if not root_task:
push_error("Root behavior tree node is null.")
return
current_lowest_node_pos = Vector2(0, 0)
build_tree_from_task(root_task, 0)
func build_tree_from_task(task: Task, depth: int) -> DTreeNode:
var child_nodes: Array[DTreeNode] = []
var child_node_positions: Array[Vector2] = []
for child in task.get_children():
var child_node: DTreeNode = build_tree_from_task(child, depth + 1)
child_nodes.append(child_node)
child_node_positions.append(child_node.position_offset)
var current_node: DTreeNode = D_TREE_NODE.instantiate()
graph_edit.add_child(current_node)
current_node.name = task.get_name() + str(randf())
current_node.title = transform_string(task.get_name())
current_node.add_label("status", true, true)
all_nodes.append(current_node)
for child in child_nodes:
graph_edit.connect_node(current_node.name, 0, child.name, 0)
if child_node_positions.size() > 0:
var average_position: Vector2 = Vector2(0, 0)
for pos in child_node_positions:
average_position += pos
average_position /= child_node_positions.size()
current_node.position_offset = average_position - Vector2(x_spacing, 0)
# print("as parent: ", current_node.name, " ", current_node.position_offset, " ", depth, " ", child_node_positions)
else:
current_node.position_offset = Vector2(current_lowest_node_pos)
current_node.position_offset.x += depth * x_spacing
current_lowest_node_pos.y += y_spacing
# print("as leaf: ", current_node.name, " ", current_node.position_offset, " ", depth)
pass
return current_node
func transform_string(input: String) -> String:
var prefixes: Dictionary = {"sl_": "Selector: ", "sq_": "Sequence: ", "Task": ""}
var selected_prefix: String = ""
for prefix in prefixes.keys():
if input.begins_with(prefix):
selected_prefix = prefix
input = input.substr(prefix.length())
break
var words: Array[Variant] = []
var current_word: String = ""
for i in range(input.length()):
var character = input[i]
if character.to_upper() == character and current_word.length() > 0:
words.append(current_word)
current_word = "" + character.to_lower()
elif character == "_":
if current_word.length() > 0:
words.append(current_word)
current_word = ""
else:
current_word += character.to_lower()
if current_word.length() > 0:
words.append(current_word)
var result: String = " ".join(words)
if selected_prefix in prefixes and prefixes[selected_prefix] != "":
result = prefixes[selected_prefix] + result
return result