126 lines
4.9 KiB
GDScript
126 lines
4.9 KiB
GDScript
|
extends Node2D
|
||
|
|
||
|
@onready var ground_layer: TileMapLayer = $GroundLayer
|
||
|
@onready var path_layer: TileMapLayer = $PathLayer
|
||
|
@onready var data_layer: TileMapLayer = $TestDataLayer
|
||
|
@onready var navigation_graph: MPNavigationGraph = $NavigationGraph
|
||
|
const DIRECTIONS: Dictionary = {
|
||
|
"up": Vector2(0, -1),
|
||
|
"down": Vector2(0, 1),
|
||
|
"left": Vector2(-1, 0),
|
||
|
"right": Vector2(1, 0)
|
||
|
}
|
||
|
var node_positions: Dictionary = {}
|
||
|
|
||
|
|
||
|
func _ready() -> void:
|
||
|
build_graph()
|
||
|
|
||
|
|
||
|
func build_graph() -> void:
|
||
|
print("Identifying nodes")
|
||
|
# Step 1: Place nodes at positions where is_tile is true
|
||
|
for position in data_layer.get_used_cells():
|
||
|
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
||
|
var is_tile: bool = tile_data.get_custom_data("is_tile")
|
||
|
if is_tile:
|
||
|
var node: NavigationNode = navigation_graph.add_node(position.x, position.y)
|
||
|
node_positions[position] = node
|
||
|
print(Vector2(position))
|
||
|
|
||
|
# Step 2: Connect nodes using flood-fill based on walkable tiles
|
||
|
print("Connecting nodes")
|
||
|
for position in node_positions.keys():
|
||
|
connect_node(position)
|
||
|
|
||
|
|
||
|
func connect_node(start_position: Vector2) -> void:
|
||
|
var start_node = node_positions.get(Vector2i(start_position))
|
||
|
var visited: Dictionary = {}
|
||
|
visited[start_position] = true
|
||
|
print("Connecting node at ", start_position)
|
||
|
|
||
|
# For each direction, perform flood-fill
|
||
|
for dir_name in DIRECTIONS.keys():
|
||
|
var direction = DIRECTIONS[dir_name]
|
||
|
var next_position = start_position + direction
|
||
|
|
||
|
print("Checking direction ", dir_name, " from ", start_position, " to ", next_position)
|
||
|
|
||
|
# Ensure the first tile respects the direction
|
||
|
if not is_valid_direction(next_position, dir_name):
|
||
|
continue
|
||
|
print("Flood-fill in direction ", dir_name, " from ", next_position)
|
||
|
|
||
|
# Perform flood-fill from the valid tile
|
||
|
var connected_nodes: Array = flood_fill(next_position, visited)
|
||
|
|
||
|
# Add connections between the start node and found nodes
|
||
|
for target_position in connected_nodes:
|
||
|
if target_position != start_position:
|
||
|
if node_positions.has(Vector2i(target_position)):
|
||
|
var target_node = node_positions.get(Vector2i(target_position))
|
||
|
navigation_graph.add_connection(start_node, target_node)
|
||
|
print(start_position, " --> ", target_position)
|
||
|
|
||
|
|
||
|
func flood_fill(start_position: Vector2, visited: Dictionary) -> Array:
|
||
|
var stack: Array[Vector2] = [start_position]
|
||
|
var connected_nodes: Array[Vector2] = []
|
||
|
|
||
|
while stack.size() > 0:
|
||
|
var current_position = stack.pop_back()
|
||
|
print(" - Visiting ", current_position)
|
||
|
|
||
|
# Skip if already visited
|
||
|
if visited.has(current_position):
|
||
|
continue
|
||
|
|
||
|
visited[current_position] = true
|
||
|
|
||
|
# Skip if not walkable
|
||
|
var tile_data: TileData = data_layer.get_cell_tile_data(current_position)
|
||
|
if tile_data == null:
|
||
|
continue
|
||
|
|
||
|
var is_walkable: bool = tile_data.get_custom_data("is_walkable")
|
||
|
var is_tile: bool = tile_data.get_custom_data("is_tile")
|
||
|
if (not is_walkable) and (not is_tile):
|
||
|
continue
|
||
|
|
||
|
# If this position is a node, add it to the result
|
||
|
if is_tile:
|
||
|
print(" - Found node tile at ", current_position)
|
||
|
connected_nodes.append(current_position)
|
||
|
else:
|
||
|
print(" - Found walkable tile at ", current_position)
|
||
|
|
||
|
# Add neighboring tiles to the stack if they respect the direction
|
||
|
for dir_name in DIRECTIONS.keys():
|
||
|
var direction = DIRECTIONS[dir_name]
|
||
|
var neighbor_position = current_position + direction
|
||
|
|
||
|
if not visited.has(neighbor_position):
|
||
|
if is_valid_direction(current_position, dir_name):
|
||
|
stack.append(neighbor_position)
|
||
|
|
||
|
return connected_nodes
|
||
|
|
||
|
|
||
|
func is_valid_direction(position: Vector2, required_dir: String) -> bool:
|
||
|
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
||
|
if tile_data == null:
|
||
|
print(" L Cancelled due to null tile data")
|
||
|
return false
|
||
|
|
||
|
var is_walkable: bool = tile_data.get_custom_data("is_walkable")
|
||
|
var walk_dir: String = tile_data.get_custom_data("walk_dir")
|
||
|
# If walk_dir is empty, treat it as "any" direction
|
||
|
if walk_dir == "":
|
||
|
walk_dir = "any"
|
||
|
|
||
|
print(" L ", position, " ", is_walkable, " ", walk_dir, " ", required_dir)
|
||
|
|
||
|
# Check if the tile is walkable and allows movement in the required direction
|
||
|
return is_walkable and (walk_dir == required_dir or walk_dir == "any")
|