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