Stuff
parent
2a22d3865c
commit
63aa1c0cb5
|
@ -6,6 +6,7 @@ const ACCELERATION: int = 2400
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
|
await get_tree().physics_frame
|
||||||
var movement: Vector2 = Vector2()
|
var movement: Vector2 = Vector2()
|
||||||
|
|
||||||
nav.target_position = get_global_mouse_position()
|
nav.target_position = get_global_mouse_position()
|
||||||
|
|
|
@ -15,6 +15,10 @@ run/main_scene="res://scenes/mario-party/MarioParty.tscn"
|
||||||
config/features=PackedStringArray("4.3", "Forward Plus")
|
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
[display]
|
||||||
|
|
||||||
|
window/stretch/mode="viewport"
|
||||||
|
|
||||||
[input]
|
[input]
|
||||||
|
|
||||||
draw_toggle_polygon={
|
draw_toggle_polygon={
|
||||||
|
|
|
@ -5,17 +5,17 @@ var polygon: PackedVector2Array
|
||||||
var center_node: NavigationNode
|
var center_node: NavigationNode
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
polygon = PackedVector2Array()
|
polygon = PackedVector2Array()
|
||||||
|
|
||||||
func set_polygon(new_polygon: PackedVector2Array) -> PackedVector2Array:
|
func set_polygon(new_polygon: PackedVector2Array) -> PackedVector2Array:
|
||||||
var new_polygon_clone: PackedVector2Array = new_polygon.duplicate()
|
var new_polygon_clone: PackedVector2Array = new_polygon.duplicate()
|
||||||
new_polygon_clone.append(new_polygon_clone[0])
|
new_polygon_clone.append(new_polygon_clone[0])
|
||||||
polygon = new_polygon_clone
|
polygon = new_polygon_clone
|
||||||
return polygon
|
return polygon
|
||||||
|
|
||||||
func center() -> Vector2:
|
func center() -> Vector2:
|
||||||
var center: Vector2 = Vector2()
|
var center: Vector2 = Vector2()
|
||||||
for point in polygon:
|
for point in polygon:
|
||||||
center += Vector2(point.x, point.y)
|
center += Vector2(point.x, point.y)
|
||||||
center /= polygon.size()
|
center /= polygon.size()
|
||||||
return center
|
return center
|
||||||
|
|
|
@ -8,26 +8,26 @@ var frozen: bool = false
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
# check if space is pressed
|
# check if space is pressed
|
||||||
if Input.is_action_just_pressed("ui_select"):
|
if Input.is_action_just_pressed("ui_select"):
|
||||||
frozen = !frozen
|
frozen = !frozen
|
||||||
|
|
||||||
var movement: Vector2 = Vector2()
|
var movement: Vector2 = Vector2()
|
||||||
if frozen:
|
if frozen:
|
||||||
return
|
return
|
||||||
|
|
||||||
var pathfinding_result: PathfindingResult = graph.determine_next_position(position, get_global_mouse_position())
|
var pathfinding_result: PathfindingResult = graph.determine_next_position(position, get_global_mouse_position())
|
||||||
graph.draw_pathfinding_result(pathfinding_result)
|
graph.draw_pathfinding_result(pathfinding_result)
|
||||||
|
|
||||||
movement = pathfinding_result.next_position - global_position
|
movement = pathfinding_result.next_position - global_position
|
||||||
|
|
||||||
if pathfinding_result.is_next_target and movement.length() < 20:
|
if pathfinding_result.is_next_target and movement.length() < 20:
|
||||||
movement = -velocity * 0.2
|
movement = -velocity * 0.2
|
||||||
else:
|
else:
|
||||||
movement = movement.normalized() * ACCELERATION * delta
|
movement = movement.normalized() * ACCELERATION * delta
|
||||||
|
|
||||||
velocity += movement
|
velocity += movement
|
||||||
if velocity.length() > MAX_SPEED:
|
if velocity.length() > MAX_SPEED:
|
||||||
velocity = velocity.normalized() * MAX_SPEED
|
velocity = velocity.normalized() * MAX_SPEED
|
||||||
|
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
|
|
|
@ -5,6 +5,7 @@ extends Node2D
|
||||||
var navigation_nodes: Dictionary = {}
|
var navigation_nodes: Dictionary = {}
|
||||||
# type is Dictionary[CNavigationPolygon, Array[NavigationNode]]
|
# type is Dictionary[CNavigationPolygon, Array[NavigationNode]]
|
||||||
var polygon_nodes: Dictionary = {}
|
var polygon_nodes: Dictionary = {}
|
||||||
|
|
||||||
var latest_navigation_result: PathfindingResult = null
|
var latest_navigation_result: PathfindingResult = null
|
||||||
var draw_polygons: bool = true
|
var draw_polygons: bool = true
|
||||||
var draw_nodes: bool = false
|
var draw_nodes: bool = false
|
||||||
|
@ -12,297 +13,294 @@ var draw_edges: bool = false
|
||||||
|
|
||||||
|
|
||||||
func all_nodes() -> Array[NavigationNode]:
|
func all_nodes() -> Array[NavigationNode]:
|
||||||
# i've had a problem where godot would not allow me to directly return navigation_nodes.keys()
|
# i've had a problem where godot would not allow me to directly return navigation_nodes.keys()
|
||||||
# because it wasn't able to cast the keys to Array[NavigationNode] directly because the type is not explicit
|
# because it wasn't able to cast the keys to Array[NavigationNode] directly because the type is not explicit
|
||||||
# on the dictionary, so i had to do this workaround.
|
# on the dictionary, so i had to do this workaround.
|
||||||
var keys: Array = navigation_nodes.keys()
|
var keys: Array = navigation_nodes.keys()
|
||||||
var nodes: Array[NavigationNode] = []
|
var nodes: Array[NavigationNode] = []
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if key is NavigationNode:
|
if key is NavigationNode:
|
||||||
nodes.append(key)
|
nodes.append(key)
|
||||||
else:
|
else:
|
||||||
push_error("Key is not a NavigationNode: %s" % key)
|
push_error("Key is not a NavigationNode: %s" % key)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
func get_connections(from: NavigationNode) -> Array[NavigationNode]:
|
func get_connections(from: NavigationNode) -> Array[NavigationNode]:
|
||||||
# the same problem as the all_nodes() function
|
# the same problem as the all_nodes() function
|
||||||
var connections: Array = navigation_nodes[from]
|
var connections: Array = navigation_nodes[from]
|
||||||
var nodes: Array[NavigationNode] = []
|
var nodes: Array[NavigationNode] = []
|
||||||
for connection in connections:
|
for connection in connections:
|
||||||
if connection is NavigationNode:
|
if connection is NavigationNode:
|
||||||
nodes.append(connection)
|
nodes.append(connection)
|
||||||
else:
|
else:
|
||||||
push_error("Connection is not a NavigationNode: %s" % connection)
|
push_error("Connection is not a NavigationNode: %s" % connection)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
func add_connection(from: NavigationNode, to: NavigationNode) -> void:
|
func add_connection(from: NavigationNode, to: NavigationNode) -> void:
|
||||||
if all_nodes().has(from):
|
if all_nodes().has(from):
|
||||||
navigation_nodes[from].append(to)
|
navigation_nodes[from].append(to)
|
||||||
else:
|
else:
|
||||||
navigation_nodes[from] = [to]
|
navigation_nodes[from] = [to]
|
||||||
|
|
||||||
|
|
||||||
func add_node(x: float, y: float, merge_threshold: float = -1.0) -> NavigationNode:
|
func add_node(x: float, y: float, merge_threshold: float = -1.0) -> NavigationNode:
|
||||||
if merge_threshold > 0:
|
if merge_threshold > 0:
|
||||||
var closest_node: NavigationNode = find_closest_node_with_threshold(Vector2(x, y), merge_threshold)
|
var closest_node: NavigationNode = find_closest_node_with_threshold(Vector2(x, y), merge_threshold)
|
||||||
if closest_node:
|
if closest_node:
|
||||||
closest_node.was_merged = true
|
closest_node.was_merged = true
|
||||||
return closest_node
|
return closest_node
|
||||||
var node: NavigationNode = NavigationNode.new()
|
var node: NavigationNode = NavigationNode.new()
|
||||||
node.set_position(Vector2(x, y))
|
node.set_position(Vector2(x, y))
|
||||||
navigation_nodes[node] = []
|
navigation_nodes[node] = []
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
func find_closest_node_with_threshold(position: Vector2, threshold: float) -> NavigationNode:
|
func find_closest_node_with_threshold(position: Vector2, threshold: float) -> NavigationNode:
|
||||||
var closest_node: NavigationNode = null
|
var closest_node: NavigationNode = null
|
||||||
var closest_distance: float = threshold
|
var closest_distance: float = threshold
|
||||||
for node in all_nodes():
|
for node in all_nodes():
|
||||||
var distance: float = position.distance_to(node.position)
|
var distance: float = position.distance_to(node.position)
|
||||||
if distance < closest_distance:
|
if distance < closest_distance:
|
||||||
closest_node = node
|
closest_node = node
|
||||||
closest_distance = distance
|
closest_distance = distance
|
||||||
return closest_node
|
return closest_node
|
||||||
|
|
||||||
|
|
||||||
func remove_connection(from: NavigationNode, to: NavigationNode) -> void:
|
func remove_connection(from: NavigationNode, to: NavigationNode) -> void:
|
||||||
if all_nodes().has(from):
|
if all_nodes().has(from):
|
||||||
navigation_nodes[from].erase(to)
|
navigation_nodes[from].erase(to)
|
||||||
|
|
||||||
|
|
||||||
func remove_node(node: NavigationNode) -> void:
|
func remove_node(node: NavigationNode) -> void:
|
||||||
navigation_nodes.erase(node)
|
navigation_nodes.erase(node)
|
||||||
for other_node in all_nodes():
|
for other_node in all_nodes():
|
||||||
if other_node != node:
|
if other_node != node:
|
||||||
remove_connection(other_node, node)
|
remove_connection(other_node, node)
|
||||||
for poly in all_polygons():
|
for poly in all_polygons():
|
||||||
if node in polygon_nodes[poly]:
|
if node in polygon_nodes[poly]:
|
||||||
polygon_nodes[poly].erase(node)
|
polygon_nodes[poly].erase(node)
|
||||||
|
|
||||||
|
|
||||||
func all_polygons() -> Array[CNavigationPolygon]:
|
func all_polygons() -> Array[CNavigationPolygon]:
|
||||||
var keys: Array = polygon_nodes.keys()
|
var keys: Array = polygon_nodes.keys()
|
||||||
var polygons: Array[CNavigationPolygon] = []
|
var polygons: Array[CNavigationPolygon] = []
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if key is CNavigationPolygon:
|
if key is CNavigationPolygon:
|
||||||
polygons.append(key)
|
polygons.append(key)
|
||||||
else:
|
else:
|
||||||
push_error("Key is not a NavigationPolygon: %s" % key)
|
push_error("Key is not a NavigationPolygon: %s" % key)
|
||||||
return polygons
|
return polygons
|
||||||
|
|
||||||
|
|
||||||
func get_nodes_in_polygon(poly: CNavigationPolygon) -> Array[NavigationNode]:
|
func get_nodes_in_polygon(poly: CNavigationPolygon) -> Array[NavigationNode]:
|
||||||
var relevant_nodes: Array = polygon_nodes[poly]
|
var relevant_nodes: Array = polygon_nodes[poly]
|
||||||
var nodes: Array[NavigationNode] = []
|
var nodes: Array[NavigationNode] = []
|
||||||
for node in relevant_nodes:
|
for node in relevant_nodes:
|
||||||
if node is NavigationNode:
|
if node is NavigationNode:
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
else:
|
else:
|
||||||
push_error("Node is not a NavigationNode: %s" % node)
|
push_error("Node is not a NavigationNode: %s" % node)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
func erase_and_create_nodes_from_polygons(new_polys: Array[PackedVector2Array]) -> void:
|
func erase_and_create_nodes_from_polygons(new_polys: Array[PackedVector2Array]) -> void:
|
||||||
navigation_nodes.clear()
|
navigation_nodes.clear()
|
||||||
polygon_nodes.clear()
|
polygon_nodes.clear()
|
||||||
|
|
||||||
for poly in new_polys:
|
for poly in new_polys:
|
||||||
if poly.size() == 0:
|
if poly.size() == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var navpoly: CNavigationPolygon = CNavigationPolygon.new()
|
var navpoly: CNavigationPolygon = CNavigationPolygon.new()
|
||||||
poly = navpoly.set_polygon(poly)
|
poly = navpoly.set_polygon(poly)
|
||||||
polygon_nodes[navpoly] = []
|
polygon_nodes[navpoly] = []
|
||||||
|
|
||||||
polygon_nodes[navpoly] = []
|
# create one in the center of each polygon that is kept no matter what
|
||||||
|
# var poly_center: Vector2 = navpoly.center()
|
||||||
|
# var center_node: NavigationNode = add_node(poly_center.x, poly_center.y, -1)
|
||||||
|
# center_node.was_merged = true
|
||||||
|
# polygon_nodes[navpoly].append(center_node)
|
||||||
|
# navpoly.center_node = center_node
|
||||||
|
|
||||||
# create one in the center of each polygon that is kept no matter what
|
for i in range(len(poly) - 1):
|
||||||
# var poly_center: Vector2 = navpoly.center()
|
var center: Vector2 = (poly[i] + poly[i + 1]) / 2
|
||||||
# var center_node: NavigationNode = add_node(poly_center.x, poly_center.y, -1)
|
polygon_nodes[navpoly].append(add_node(center.x, center.y, 10))
|
||||||
# center_node.was_merged = true
|
var quater: Vector2 = (poly[i] + center) / 2
|
||||||
# polygon_nodes[navpoly].append(center_node)
|
polygon_nodes[navpoly].append(add_node(quater.x, quater.y, 10))
|
||||||
# navpoly.center_node = center_node
|
var three_quater: Vector2 = (center + poly[i + 1]) / 2
|
||||||
|
polygon_nodes[navpoly].append(add_node(three_quater.x, three_quater.y, 10))
|
||||||
|
|
||||||
for i in range(len(poly) - 1):
|
# clear any that were not merged
|
||||||
var center: Vector2 = (poly[i] + poly[i + 1]) / 2
|
for node in all_nodes():
|
||||||
polygon_nodes[navpoly].append(add_node(center.x, center.y, 10))
|
if not node.was_merged:
|
||||||
var quater: Vector2 = (poly[i] + center) / 2
|
remove_node(node)
|
||||||
polygon_nodes[navpoly].append(add_node(quater.x, quater.y, 10))
|
|
||||||
var three_quater: Vector2 = (center + poly[i + 1]) / 2
|
|
||||||
polygon_nodes[navpoly].append(add_node(three_quater.x, three_quater.y, 10))
|
|
||||||
|
|
||||||
# clear any that were not merged
|
# connect all within a polygon
|
||||||
for node in all_nodes():
|
for poly in all_polygons():
|
||||||
if not node.was_merged:
|
var nodes_in_polygon: Array[NavigationNode] = get_nodes_in_polygon(poly)
|
||||||
remove_node(node)
|
connect_all_nodes(nodes_in_polygon)
|
||||||
|
|
||||||
# connect all within a polygon
|
|
||||||
for poly in all_polygons():
|
|
||||||
var nodes_in_polygon: Array[NavigationNode] = get_nodes_in_polygon(poly)
|
|
||||||
connect_all_nodes(nodes_in_polygon)
|
|
||||||
|
|
||||||
|
|
||||||
func connect_all_nodes(nodes: Array[NavigationNode]) -> void:
|
func connect_all_nodes(nodes: Array[NavigationNode]) -> void:
|
||||||
for i in range(len(nodes)):
|
for i in range(len(nodes)):
|
||||||
for j in range(len(nodes)):
|
for j in range(len(nodes)):
|
||||||
if i != j:
|
if i != j:
|
||||||
add_connection(nodes[i], nodes[j])
|
add_connection(nodes[i], nodes[j])
|
||||||
|
|
||||||
|
|
||||||
func _draw() -> void:
|
func _draw() -> void:
|
||||||
if draw_nodes or draw_edges:
|
if draw_nodes or draw_edges:
|
||||||
for from in all_nodes():
|
for from in all_nodes():
|
||||||
if draw_edges:
|
if draw_edges:
|
||||||
for to in get_connections(from):
|
for to in get_connections(from):
|
||||||
draw_line(from.position, to.position, Color.RED, 1, false)
|
draw_line(from.position, to.position, Color.RED, 1, false)
|
||||||
if draw_nodes:
|
if draw_nodes:
|
||||||
draw_circle(from.position, 5, Color.RED)
|
draw_circle(from.position, 5, Color.RED)
|
||||||
|
|
||||||
if draw_polygons:
|
if draw_polygons:
|
||||||
for poly in all_polygons():
|
for poly in all_polygons():
|
||||||
draw_colored_polygon(poly.polygon, Color(0.5, 0.4, 0.9, 0.3))
|
draw_colored_polygon(poly.polygon, Color(0.5, 0.4, 0.9, 0.3))
|
||||||
draw_polyline(poly.polygon, Color.WHITE, 1, true)
|
draw_polyline(poly.polygon, Color.WHITE, 1, true)
|
||||||
|
|
||||||
if latest_navigation_result != null:
|
if latest_navigation_result != null:
|
||||||
if latest_navigation_result.path.size() > 1:
|
if latest_navigation_result.path.size() > 1:
|
||||||
for i in range(latest_navigation_result.path.size() - 1):
|
for i in range(latest_navigation_result.path.size() - 1):
|
||||||
draw_line(latest_navigation_result.path[i].position, latest_navigation_result.path[i + 1].position, Color.GREEN, 1, false)
|
draw_line(latest_navigation_result.path[i].position, latest_navigation_result.path[i + 1].position, Color.GREEN, 1, false)
|
||||||
draw_circle(latest_navigation_result.next_position, 5, Color.GREEN)
|
draw_circle(latest_navigation_result.next_position, 5, Color.GREEN)
|
||||||
|
|
||||||
|
|
||||||
func draw_pathfinding_result(result: PathfindingResult) -> void:
|
func draw_pathfinding_result(result: PathfindingResult) -> void:
|
||||||
if latest_navigation_result and latest_navigation_result.is_identical_to(result):
|
if latest_navigation_result and latest_navigation_result.is_identical_to(result):
|
||||||
return
|
return
|
||||||
latest_navigation_result = result
|
latest_navigation_result = result
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
func determine_next_position(current_position: Vector2, target_position: Vector2) -> PathfindingResult:
|
func determine_next_position(current_position: Vector2, target_position: Vector2) -> PathfindingResult:
|
||||||
var result: PathfindingResult = PathfindingResult.new()
|
var result: PathfindingResult = PathfindingResult.new()
|
||||||
|
|
||||||
# find both polygons containing the current and target positions
|
# find both polygons containing the current and target positions
|
||||||
var current_polygon: CNavigationPolygon = null
|
var current_polygon: CNavigationPolygon = null
|
||||||
var target_polygon: CNavigationPolygon = null
|
var target_polygon: CNavigationPolygon = null
|
||||||
|
|
||||||
for poly in all_polygons():
|
for poly in all_polygons():
|
||||||
if Geometry2D.is_point_in_polygon(current_position, poly.polygon):
|
if Geometry2D.is_point_in_polygon(current_position, poly.polygon):
|
||||||
current_polygon = poly
|
current_polygon = poly
|
||||||
if Geometry2D.is_point_in_polygon(target_position, poly.polygon):
|
if Geometry2D.is_point_in_polygon(target_position, poly.polygon):
|
||||||
target_polygon = poly
|
target_polygon = poly
|
||||||
|
|
||||||
# if the current position is not in any polygon, navigate to the closest node
|
# if the current position is not in any polygon, navigate to the closest node
|
||||||
if not current_polygon:
|
if not current_polygon:
|
||||||
var closest_node: NavigationNode = find_closest_node_with_threshold(current_position, 100000)
|
var closest_node: NavigationNode = find_closest_node_with_threshold(current_position, 100000)
|
||||||
result.next_position = closest_node.position
|
result.next_position = closest_node.position
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# if the target position is not in any polygon, return current position (cannot navigate)
|
# if the target position is not in any polygon, return current position (cannot navigate)
|
||||||
if not target_polygon:
|
if not target_polygon:
|
||||||
result.is_next_target = true
|
result.is_next_target = true
|
||||||
result.next_position = current_position
|
result.next_position = current_position
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# if the current and target positions are in the same polygon, return the target position
|
# if the current and target positions are in the same polygon, return the target position
|
||||||
if current_polygon == target_polygon:
|
if current_polygon == target_polygon:
|
||||||
result.is_next_target = true
|
result.is_next_target = true
|
||||||
result.next_position = target_position
|
result.next_position = target_position
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# we will have to insert the start node into the graph and connect it to the nodes within the polygon,
|
# we will have to insert the start node into the graph and connect it to the nodes within the polygon,
|
||||||
# and remove it later on again
|
# and remove it later on again
|
||||||
var start_node: NavigationNode = add_node(current_position.x, current_position.y, -1)
|
var start_node: NavigationNode = add_node(current_position.x, current_position.y, -1)
|
||||||
var nodes_in_current_polygon: Array[NavigationNode] = get_nodes_in_polygon(current_polygon)
|
var nodes_in_current_polygon: Array[NavigationNode] = get_nodes_in_polygon(current_polygon)
|
||||||
polygon_nodes[current_polygon].append(start_node)
|
polygon_nodes[current_polygon].append(start_node)
|
||||||
for node in nodes_in_current_polygon:
|
for node in nodes_in_current_polygon:
|
||||||
add_connection(start_node, node)
|
add_connection(start_node, node)
|
||||||
|
|
||||||
# the target position is simple, just find the closest node in the polygon to the target position,
|
# the target position is simple, just find the closest node in the polygon to the target position,
|
||||||
# the alternate algorithm for within a polygon above will take care of the rest
|
# the alternate algorithm for within a polygon above will take care of the rest
|
||||||
var end_node: NavigationNode = null
|
var end_node: NavigationNode = null
|
||||||
var min_distance: float = INF
|
var min_distance: float = INF
|
||||||
|
|
||||||
var nodes_in_target_polygon: Array[NavigationNode] = get_nodes_in_polygon(target_polygon)
|
var nodes_in_target_polygon: Array[NavigationNode] = get_nodes_in_polygon(target_polygon)
|
||||||
for node in nodes_in_target_polygon:
|
for node in nodes_in_target_polygon:
|
||||||
var distance: float = target_position.distance_to(node.position)
|
var distance: float = target_position.distance_to(node.position)
|
||||||
if distance < min_distance:
|
if distance < min_distance:
|
||||||
min_distance = distance
|
min_distance = distance
|
||||||
end_node = node
|
end_node = node
|
||||||
|
|
||||||
var path: Array[NavigationNode] = dijkstra(start_node, end_node)
|
var path: Array[NavigationNode] = dijkstra(start_node, end_node)
|
||||||
result.path = path
|
result.path = path
|
||||||
|
|
||||||
# remove the start node again
|
# remove the start node again
|
||||||
remove_node(start_node)
|
remove_node(start_node)
|
||||||
|
|
||||||
# if a path is found, return the position of the next node in the path
|
# if a path is found, return the position of the next node in the path
|
||||||
if path.size() > 1:
|
if path.size() > 1:
|
||||||
# next node in the path
|
# next node in the path
|
||||||
result.next_position = path[1].position
|
result.next_position = path[1].position
|
||||||
return result
|
return result
|
||||||
elif path.size() == 1:
|
elif path.size() == 1:
|
||||||
# directly reachable
|
# directly reachable
|
||||||
result.is_next_target = true
|
result.is_next_target = true
|
||||||
result.next_position = target_position
|
result.next_position = target_position
|
||||||
else:
|
else:
|
||||||
# no path found; return current position
|
# no path found; return current position
|
||||||
result.is_next_target = true
|
result.is_next_target = true
|
||||||
result.next_position = current_position
|
result.next_position = current_position
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
func array_contains_node(arr: Array, node: NavigationNode) -> bool:
|
func array_contains_node(arr: Array, node: NavigationNode) -> bool:
|
||||||
for item in arr:
|
for item in arr:
|
||||||
if item == node:
|
if item == node:
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
func dijkstra(start_node: NavigationNode, end_node: NavigationNode) -> Array[NavigationNode]:
|
func dijkstra(start_node: NavigationNode, end_node: NavigationNode) -> Array[NavigationNode]:
|
||||||
var open_set: Array[NavigationNode] = []
|
var open_set: Array[NavigationNode] = []
|
||||||
var closed_set: Array[NavigationNode] = []
|
var closed_set: Array[NavigationNode] = []
|
||||||
var distances: Dictionary = {}
|
var distances: Dictionary = {}
|
||||||
var previous_nodes: Dictionary = {}
|
var previous_nodes: Dictionary = {}
|
||||||
|
|
||||||
distances[start_node] = 0
|
distances[start_node] = 0
|
||||||
open_set.append(start_node)
|
open_set.append(start_node)
|
||||||
|
|
||||||
while open_set.size() > 0:
|
while open_set.size() > 0:
|
||||||
# Find the node with the smallest tentative distance
|
var current_node: NavigationNode = open_set[0]
|
||||||
var current_node: NavigationNode = open_set[0]
|
var current_distance: float = distances[current_node]
|
||||||
var current_distance: float = distances[current_node]
|
for node in open_set:
|
||||||
for node in open_set:
|
if distances[node] < current_distance:
|
||||||
if distances[node] < current_distance:
|
current_node = node
|
||||||
current_node = node
|
current_distance = distances[node]
|
||||||
current_distance = distances[node]
|
|
||||||
|
|
||||||
# If the end node is reached, reconstruct the path
|
# if the end node is reached, reconstruct path
|
||||||
if current_node == end_node:
|
if current_node == end_node:
|
||||||
var path: Array[NavigationNode] = []
|
var path: Array[NavigationNode] = []
|
||||||
var node: NavigationNode = end_node
|
var node: NavigationNode = end_node
|
||||||
while node != null:
|
while node != null:
|
||||||
path.insert(0, node)
|
path.insert(0, node)
|
||||||
node = previous_nodes.get(node, null)
|
node = previous_nodes.get(node, null)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
open_set.erase(current_node)
|
open_set.erase(current_node)
|
||||||
closed_set.append(current_node)
|
closed_set.append(current_node)
|
||||||
|
|
||||||
# Examine neighbors of the current node
|
# neighbors of the current node
|
||||||
var neighbors = get_connections(current_node)
|
var neighbors = get_connections(current_node)
|
||||||
for neighbor in neighbors:
|
for neighbor in neighbors:
|
||||||
if array_contains_node(closed_set, neighbor):
|
if array_contains_node(closed_set, neighbor):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var tentative_distance: float = distances[current_node] + current_node.position.distance_to(neighbor.position)
|
var tentative_distance: float = distances[current_node] + current_node.position.distance_to(neighbor.position)
|
||||||
|
|
||||||
if not array_contains_node(open_set, neighbor) or tentative_distance < distances.get(neighbor, INF):
|
if not array_contains_node(open_set, neighbor) or tentative_distance < distances.get(neighbor, INF):
|
||||||
distances[neighbor] = tentative_distance
|
distances[neighbor] = tentative_distance
|
||||||
previous_nodes[neighbor] = current_node
|
previous_nodes[neighbor] = current_node
|
||||||
if not array_contains_node(open_set, neighbor):
|
if not array_contains_node(open_set, neighbor):
|
||||||
open_set.append(neighbor)
|
open_set.append(neighbor)
|
||||||
|
|
||||||
# No path found; return an empty array
|
# no path found
|
||||||
return []
|
return []
|
||||||
|
|
|
@ -14,10 +14,10 @@ script = ExtResource("1_tis1c")
|
||||||
navigation_polygon = [NodePath("NavPolygon2D_1"), NodePath("SmallPolygon2D"), NodePath("NavPolygon2D_2")]
|
navigation_polygon = [NodePath("NavPolygon2D_1"), NodePath("SmallPolygon2D"), NodePath("NavPolygon2D_2")]
|
||||||
|
|
||||||
[node name="NavPolygon2D_2" type="Polygon2D" parent="."]
|
[node name="NavPolygon2D_2" type="Polygon2D" parent="."]
|
||||||
polygon = PackedVector2Array(474, 298, 379, 295, 381, 261, 479, 98, 422, 71, 336, 203, 334, 47, 255, 43, 49, 42, 48, 119, 55, 247, 109, 244, 112, 126, 253, 119, 249, 176, 153, 177, 153, 240, 253, 236, 289, 322, 69, 300, 74, 390, 475, 400, 484, 463, 104, 458, 108, 630, 222, 633, 222, 542, 476, 569, 748, 642, 984, 642, 979, 580, 911, 563, 910, 495, 831, 495, 847, 563, 701, 493, 656, 402, 926, 414, 995, 516, 1110, 517, 985, 319, 774, 292, 765, 244, 884, 266, 896, 206, 764, 167, 675, 226, 703, 295, 652, 295, 554, 179, 805, 106, 935, 128, 998, 249, 1105, 257, 934, 19, 616, 61, 476, 167)
|
visible = false
|
||||||
|
polygon = PackedVector2Array(474, 298, 379, 295, 381, 261, 479, 98, 422, 71, 336, 203, 334, 47, 255, 43, 49, 42, 48, 119, 55, 247, 109, 244, 134, 127, 253, 119, 249, 176, 153, 177, 153, 240, 253, 236, 289, 322, 69, 300, 74, 390, 475, 400, 619, 511, 222, 449, 108, 630, 222, 633, 297, 544, 476, 569, 748, 642, 984, 642, 979, 580, 911, 563, 910, 495, 831, 495, 847, 563, 701, 493, 656, 402, 926, 414, 995, 516, 1110, 517, 985, 319, 774, 292, 765, 244, 884, 266, 896, 206, 764, 167, 675, 226, 703, 295, 652, 295, 600, 200, 805, 106, 935, 128, 998, 249, 1105, 257, 934, 19, 616, 61, 476, 167)
|
||||||
|
|
||||||
[node name="NavPolygon2D_1" type="Polygon2D" parent="."]
|
[node name="NavPolygon2D_1" type="Polygon2D" parent="."]
|
||||||
visible = false
|
|
||||||
polygon = PackedVector2Array(164, 56, 379, 23, 603, 53, 684, 152, 759, 255, 572, 293, 598, 166, 422, 106, 344, 158, 509, 312, 438, 454, 489, 504, 610, 342, 734, 323, 834, 199, 786, 85, 958, 43, 1117, 48, 1109, 194, 1137, 565, 916, 550, 887, 386, 965, 359, 962, 473, 1000, 494, 1002, 204, 928, 184, 916, 304, 837, 382, 835, 541, 752, 563, 732, 421, 627, 450, 592, 618, 335, 540, 295, 412, 361, 311, 190, 169, 194, 329, 278, 510, 132, 589, 133, 459, 77, 311, 48, 130)
|
polygon = PackedVector2Array(164, 56, 379, 23, 603, 53, 684, 152, 759, 255, 572, 293, 598, 166, 422, 106, 344, 158, 509, 312, 438, 454, 489, 504, 610, 342, 734, 323, 834, 199, 786, 85, 958, 43, 1117, 48, 1109, 194, 1137, 565, 916, 550, 887, 386, 965, 359, 962, 473, 1000, 494, 1002, 204, 928, 184, 916, 304, 837, 382, 835, 541, 752, 563, 732, 421, 627, 450, 592, 618, 335, 540, 295, 412, 361, 311, 190, 169, 194, 329, 278, 510, 132, 589, 133, 459, 77, 311, 48, 130)
|
||||||
|
|
||||||
[node name="SmallPolygon2D" type="Polygon2D" parent="."]
|
[node name="SmallPolygon2D" type="Polygon2D" parent="."]
|
||||||
|
|
|
@ -9,33 +9,33 @@ var seleted_polygon: int = 0
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
for polygon in navigation_polygon:
|
for polygon in navigation_polygon:
|
||||||
polygon.hide()
|
polygon.hide()
|
||||||
|
|
||||||
update_polygon()
|
update_polygon()
|
||||||
|
|
||||||
|
|
||||||
func update_polygon():
|
func update_polygon():
|
||||||
var polygons: Array[PackedVector2Array] = Geometry2D.decompose_polygon_in_convex(navigation_polygon[seleted_polygon].polygon)
|
var polygons: Array[PackedVector2Array] = Geometry2D.decompose_polygon_in_convex(navigation_polygon[seleted_polygon].polygon)
|
||||||
graph.erase_and_create_nodes_from_polygons(polygons)
|
graph.erase_and_create_nodes_from_polygons(polygons)
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
# check for left and right arrow keys to change the selected polygon (0, len(navigation_polygon)-1)
|
# check for left and right arrow keys to change the selected polygon (0, len(navigation_polygon)-1)
|
||||||
if Input.is_action_just_pressed("ui_right"):
|
if Input.is_action_just_pressed("ui_right"):
|
||||||
seleted_polygon += 1
|
seleted_polygon += 1
|
||||||
if seleted_polygon >= len(navigation_polygon):
|
if seleted_polygon >= len(navigation_polygon):
|
||||||
seleted_polygon = 0
|
seleted_polygon = 0
|
||||||
update_polygon()
|
update_polygon()
|
||||||
|
|
||||||
if Input.is_action_just_pressed("ui_left"):
|
if Input.is_action_just_pressed("ui_left"):
|
||||||
seleted_polygon -= 1
|
seleted_polygon -= 1
|
||||||
if seleted_polygon < 0:
|
if seleted_polygon < 0:
|
||||||
seleted_polygon = len(navigation_polygon) - 1
|
seleted_polygon = len(navigation_polygon) - 1
|
||||||
update_polygon()
|
update_polygon()
|
||||||
|
|
||||||
if Input.is_action_just_pressed("draw_toggle_polygon"):
|
if Input.is_action_just_pressed("draw_toggle_polygon"):
|
||||||
graph.draw_polygons = !graph.draw_polygons
|
graph.draw_polygons = !graph.draw_polygons
|
||||||
if Input.is_action_just_pressed("draw_toggle_nodes") or Input.is_action_just_pressed("draw_toggle_edges"):
|
if Input.is_action_just_pressed("draw_toggle_nodes") or Input.is_action_just_pressed("draw_toggle_edges"):
|
||||||
graph.draw_nodes = !graph.draw_nodes
|
graph.draw_nodes = !graph.draw_nodes
|
||||||
graph.draw_edges = !graph.draw_edges
|
graph.draw_edges = !graph.draw_edges
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -12,187 +12,191 @@ var last_dice_result: int = 0
|
||||||
var global_transform_size: Vector2 = Vector2(0, 0)
|
var global_transform_size: Vector2 = Vector2(0, 0)
|
||||||
#
|
#
|
||||||
const DIRECTIONS: Dictionary = {
|
const DIRECTIONS: Dictionary = {
|
||||||
"up": Vector2(0, -1),
|
"up": Vector2(0, -1),
|
||||||
"down": Vector2(0, 1),
|
"down": Vector2(0, 1),
|
||||||
"left": Vector2(-1, 0),
|
"left": Vector2(-1, 0),
|
||||||
"right": Vector2(1, 0)
|
"right": Vector2(1, 0)
|
||||||
}
|
}
|
||||||
var node_positions: Dictionary = {}
|
var node_positions: Dictionary = {}
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
global_transform_size = Vector2(16, 16) * data_layer.transform.get_scale()
|
global_transform_size = Vector2(16, 16) * data_layer.transform.get_scale()
|
||||||
|
|
||||||
build_graph()
|
build_graph()
|
||||||
|
|
||||||
# place the player at the first node
|
# place the player at the first node
|
||||||
var current_player_position: Vector2 = node_positions.keys()[0]
|
var current_player_position: Vector2 = node_positions.keys()[0]
|
||||||
player.global_position = local_to_world(current_player_position)
|
player.global_position = local_to_world(current_player_position)
|
||||||
|
|
||||||
# bring player to top
|
# bring player to top
|
||||||
player.z_index = 1
|
player.z_index = 1
|
||||||
|
|
||||||
roll_dice()
|
roll_dice()
|
||||||
|
|
||||||
player.finished_movement.connect(roll_dice)
|
player.finished_movement.connect(roll_dice)
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
if Input.is_action_just_pressed("draw_toggle_nodes") or Input.is_action_just_pressed("draw_toggle_edges"):
|
if Input.is_action_just_pressed("draw_toggle_nodes") or Input.is_action_just_pressed("draw_toggle_edges"):
|
||||||
navigation_graph.draw_nodes = !navigation_graph.draw_nodes
|
navigation_graph.draw_nodes = !navigation_graph.draw_nodes
|
||||||
navigation_graph.draw_edges = !navigation_graph.draw_edges
|
navigation_graph.draw_edges = !navigation_graph.draw_edges
|
||||||
navigation_graph.queue_redraw()
|
navigation_graph.queue_redraw()
|
||||||
|
|
||||||
var dice_controls_min: int = 2
|
var dice_controls_min: int = 2
|
||||||
var dice_controls_max: int = 8
|
var dice_controls_max: int = 8
|
||||||
|
|
||||||
|
|
||||||
func roll_dice() -> void:
|
func roll_dice() -> void:
|
||||||
last_dice_result = randi() % (dice_controls_max - dice_controls_min + 1) + dice_controls_min
|
last_dice_result = randi() % (dice_controls_max - dice_controls_min + 1) + dice_controls_min
|
||||||
print("Dice result: ", last_dice_result)
|
print("Dice result: ", last_dice_result)
|
||||||
$ResultLabel.text = "Rolled a " + str(last_dice_result)
|
$ResultLabel.text = "Rolled a " + str(last_dice_result)
|
||||||
|
|
||||||
# Find possible movement options
|
# Find possible movement options
|
||||||
var movement_options: Array[MPNavigationNode] = find_nodes_with_distance(world_to_local(player.global_position), last_dice_result)
|
var movement_options: Array[MPNavigationNode] = find_nodes_with_distance(world_to_local(player.global_position), last_dice_result)
|
||||||
# visualize by spawning an indicator at each possible node, with a click event to move the player
|
# visualize by spawning an indicator at each possible node, with a click event to move the player
|
||||||
for node in movement_options:
|
for node in movement_options:
|
||||||
# res://scenes/mario-party/TileIndicator.tscn
|
# res://scenes/mario-party/TileIndicator.tscn
|
||||||
var indicator_scene: Node2D = preload("res://scenes/mario-party/TileIndicator.tscn").instantiate()
|
var indicator_scene: Node2D = preload("res://scenes/mario-party/TileIndicator.tscn").instantiate()
|
||||||
indicator_scene.scale = Vector2(2, 2)
|
indicator_scene.scale = Vector2(2, 2)
|
||||||
var area_node: Area2D = indicator_scene.get_node("area")
|
var area_node: Area2D = indicator_scene.get_node("area")
|
||||||
area_node.set_data(node.position, "player_movement")
|
area_node.set_data(node.position, "player_movement")
|
||||||
area_node.indicator_clicked.connect(_on_indicator_clicked)
|
area_node.indicator_clicked.connect(_on_indicator_clicked)
|
||||||
indicator_scene.global_position = local_to_world(node.get_position())
|
indicator_scene.global_position = local_to_world(node.get_position())
|
||||||
add_child(indicator_scene)
|
add_child(indicator_scene)
|
||||||
|
|
||||||
|
# await get_tree().create_timer(1.0).timeout
|
||||||
|
# var picked = movement_options[randi() % (len(movement_options))]
|
||||||
|
# _on_indicator_clicked(picked.position, "player_movement")
|
||||||
|
|
||||||
|
|
||||||
func _on_indicator_clicked(pos: Vector2, type: String) -> void:
|
func _on_indicator_clicked(pos: Vector2, type: String) -> void:
|
||||||
print("Indicator clicked: ", type, " ", pos)
|
print("Indicator clicked: ", type, " ", pos)
|
||||||
if type == "player_movement":
|
if type == "player_movement":
|
||||||
player.move_to(pos)
|
player.move_to(pos)
|
||||||
# delete all indicators
|
# delete all indicators
|
||||||
for child in get_children():
|
for child in get_children():
|
||||||
if child is Node2D:
|
if child is Node2D:
|
||||||
var area_node: Area2D = child.get_node("area")
|
var area_node: Area2D = child.get_node("area")
|
||||||
if area_node != null:
|
if area_node != null:
|
||||||
if area_node.get_type() == "player_movement":
|
if area_node.get_type() == "player_movement":
|
||||||
child.queue_free()
|
child.queue_free()
|
||||||
|
|
||||||
|
|
||||||
func local_to_world(location: Vector2) -> Vector2:
|
func local_to_world(location: Vector2) -> Vector2:
|
||||||
return location * global_transform_size + global_transform_size / 2
|
return location * global_transform_size + global_transform_size / 2
|
||||||
|
|
||||||
|
|
||||||
func world_to_local(location: Vector2) -> Vector2:
|
func world_to_local(location: Vector2) -> Vector2:
|
||||||
return (location - global_transform_size / 2) / global_transform_size
|
return (location - global_transform_size / 2) / global_transform_size
|
||||||
|
|
||||||
|
|
||||||
func build_graph() -> void:
|
func build_graph() -> void:
|
||||||
print("Identifying nodes")
|
print("Identifying nodes")
|
||||||
# Step 1: Place nodes at positions where is_tile is true
|
# Step 1: Place nodes at positions where is_tile is true
|
||||||
for position in data_layer.get_used_cells():
|
for position in data_layer.get_used_cells():
|
||||||
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
||||||
var is_tile: bool = tile_data.get_custom_data("is_tile")
|
var is_tile: bool = tile_data.get_custom_data("is_tile")
|
||||||
if is_tile:
|
if is_tile:
|
||||||
var node: MPNavigationNode = navigation_graph.add_node(position.x, position.y)
|
var node: MPNavigationNode = navigation_graph.add_node(position.x, position.y)
|
||||||
node_positions[position] = node
|
node_positions[position] = node
|
||||||
var indicator_scene: Node2D = preload("res://scenes/mario-party/TileIndicator.tscn").instantiate()
|
var indicator_scene: Node2D = preload("res://scenes/mario-party/TileIndicator.tscn").instantiate()
|
||||||
var area_node: Area2D = indicator_scene.get_node("area")
|
var area_node: Area2D = indicator_scene.get_node("area")
|
||||||
area_node.set_display_type("node")
|
area_node.set_display_type("node")
|
||||||
indicator_scene.global_position = local_to_world(position)
|
indicator_scene.global_position = local_to_world(position)
|
||||||
add_child(indicator_scene)
|
add_child(indicator_scene)
|
||||||
|
|
||||||
# Step 2: Connect nodes using flood-fill based on walkable tiles
|
# Step 2: Connect nodes using flood-fill based on walkable tiles
|
||||||
print("Connecting nodes")
|
print("Connecting nodes")
|
||||||
for position in node_positions.keys():
|
for position in node_positions.keys():
|
||||||
connect_node(position)
|
connect_node(position)
|
||||||
|
|
||||||
|
|
||||||
func connect_node(start_position: Vector2) -> void:
|
func connect_node(start_position: Vector2) -> void:
|
||||||
var start_node = node_positions.get(Vector2i(start_position))
|
var start_node = node_positions.get(Vector2i(start_position))
|
||||||
var visited: Dictionary = {}
|
var visited: Dictionary = {}
|
||||||
visited[start_position] = true
|
visited[start_position] = true
|
||||||
# print("Connecting node at ", start_position)
|
# print("Connecting node at ", start_position)
|
||||||
|
|
||||||
# For each direction, perform flood-fill
|
# For each direction, perform flood-fill
|
||||||
for dir_name in DIRECTIONS.keys():
|
for dir_name in DIRECTIONS.keys():
|
||||||
var direction = DIRECTIONS[dir_name]
|
var direction = DIRECTIONS[dir_name]
|
||||||
var next_position = start_position + direction
|
var next_position = start_position + direction
|
||||||
|
|
||||||
# print("Checking direction ", dir_name, " from ", start_position, " to ", next_position)
|
# print("Checking direction ", dir_name, " from ", start_position, " to ", next_position)
|
||||||
|
|
||||||
# Ensure the first tile respects the direction
|
# Ensure the first tile respects the direction
|
||||||
if not is_valid_direction(next_position, dir_name):
|
if not is_valid_direction(next_position, dir_name):
|
||||||
continue
|
continue
|
||||||
# print("Flood-fill in direction ", dir_name, " from ", next_position)
|
# print("Flood-fill in direction ", dir_name, " from ", next_position)
|
||||||
|
|
||||||
# Perform flood-fill from the valid tile
|
# Perform flood-fill from the valid tile
|
||||||
var connected_nodes: Array = flood_fill(next_position, visited)
|
var connected_nodes: Array = flood_fill(next_position, visited)
|
||||||
|
|
||||||
# Add connections between the start node and found nodes
|
# Add connections between the start node and found nodes
|
||||||
for target_position in connected_nodes:
|
for target_position in connected_nodes:
|
||||||
if target_position != start_position:
|
if target_position != start_position:
|
||||||
if node_positions.has(Vector2i(target_position)):
|
if node_positions.has(Vector2i(target_position)):
|
||||||
var target_node = node_positions.get(Vector2i(target_position))
|
var target_node = node_positions.get(Vector2i(target_position))
|
||||||
navigation_graph.add_connection(start_node, target_node)
|
navigation_graph.add_connection(start_node, target_node)
|
||||||
print(start_position, " --> ", target_position)
|
print(start_position, " --> ", target_position)
|
||||||
|
|
||||||
|
|
||||||
func flood_fill(start_position: Vector2, visited: Dictionary) -> Array:
|
func flood_fill(start_position: Vector2, visited: Dictionary) -> Array:
|
||||||
var stack: Array[Vector2] = [start_position]
|
var stack: Array[Vector2] = [start_position]
|
||||||
var connected_nodes: Array[Vector2] = []
|
var connected_nodes: Array[Vector2] = []
|
||||||
|
|
||||||
while stack.size() > 0:
|
while stack.size() > 0:
|
||||||
var current_position = stack.pop_back()
|
var current_position = stack.pop_back()
|
||||||
# print(" - Visiting ", current_position)
|
# print(" - Visiting ", current_position)
|
||||||
|
|
||||||
# Skip if already visited
|
# Skip if already visited
|
||||||
if visited.has(current_position):
|
if visited.has(current_position):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
visited[current_position] = true
|
visited[current_position] = true
|
||||||
|
|
||||||
# Skip if not walkable
|
# Skip if not walkable
|
||||||
var tile_data: TileData = data_layer.get_cell_tile_data(current_position)
|
var tile_data: TileData = data_layer.get_cell_tile_data(current_position)
|
||||||
if tile_data == null:
|
if tile_data == null:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var is_walkable: bool = tile_data.get_custom_data("is_walkable")
|
var is_walkable: bool = tile_data.get_custom_data("is_walkable")
|
||||||
var is_tile: bool = tile_data.get_custom_data("is_tile")
|
var is_tile: bool = tile_data.get_custom_data("is_tile")
|
||||||
if (not is_walkable) and (not is_tile):
|
if (not is_walkable) and (not is_tile):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# If this position is a node, add it to the result
|
# If this position is a node, add it to the result
|
||||||
if is_tile:
|
if is_tile:
|
||||||
# print(" - Found node tile at ", current_position)
|
# print(" - Found node tile at ", current_position)
|
||||||
connected_nodes.append(current_position)
|
connected_nodes.append(current_position)
|
||||||
|
|
||||||
# Add neighboring tiles to the stack if they respect the direction
|
# Add neighboring tiles to the stack if they respect the direction
|
||||||
for dir_name in DIRECTIONS.keys():
|
for dir_name in DIRECTIONS.keys():
|
||||||
var direction = DIRECTIONS[dir_name]
|
var direction = DIRECTIONS[dir_name]
|
||||||
var neighbor_position = current_position + direction
|
var neighbor_position = current_position + direction
|
||||||
|
|
||||||
if not visited.has(neighbor_position):
|
if not visited.has(neighbor_position):
|
||||||
if is_valid_direction(current_position, dir_name):
|
if is_valid_direction(current_position, dir_name):
|
||||||
stack.append(neighbor_position)
|
stack.append(neighbor_position)
|
||||||
|
|
||||||
return connected_nodes
|
return connected_nodes
|
||||||
|
|
||||||
|
|
||||||
func is_valid_direction(position: Vector2, required_dir: String) -> bool:
|
func is_valid_direction(position: Vector2, required_dir: String) -> bool:
|
||||||
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
var tile_data: TileData = data_layer.get_cell_tile_data(position)
|
||||||
if tile_data == null:
|
if tile_data == null:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
var is_walkable: bool = tile_data.get_custom_data("is_walkable")
|
var is_walkable: bool = tile_data.get_custom_data("is_walkable")
|
||||||
var walk_dir: String = tile_data.get_custom_data("walk_dir")
|
var walk_dir: String = tile_data.get_custom_data("walk_dir")
|
||||||
if walk_dir == "":
|
if walk_dir == "":
|
||||||
walk_dir = "any"
|
walk_dir = "any"
|
||||||
|
|
||||||
# print(" L ", position, " ", is_walkable, " ", walk_dir, " ", required_dir)
|
# print(" L ", position, " ", is_walkable, " ", walk_dir, " ", required_dir)
|
||||||
|
|
||||||
# Check if the tile is walkable and allows movement in the required direction
|
# 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")
|
return is_walkable and (walk_dir == required_dir or walk_dir == "any")
|
||||||
|
|
||||||
|
|
||||||
# the first function that evaluates the finished graph
|
# the first function that evaluates the finished graph
|
||||||
|
@ -201,35 +205,35 @@ func is_valid_direction(position: Vector2, required_dir: String) -> bool:
|
||||||
# distance here is the amount of nodes to travel, not the actual distance in pixels
|
# distance here is the amount of nodes to travel, not the actual distance in pixels
|
||||||
# must use the MPNavigationNode class and the navigation_graph.get_connections(node: MPNavigationNode) function
|
# must use the MPNavigationNode class and the navigation_graph.get_connections(node: MPNavigationNode) function
|
||||||
func find_nodes_with_distance(start_position: Vector2, distance: int) -> Array[MPNavigationNode]:
|
func find_nodes_with_distance(start_position: Vector2, distance: int) -> Array[MPNavigationNode]:
|
||||||
var start_node = node_positions.get(Vector2i(start_position))
|
var start_node = node_positions.get(Vector2i(start_position))
|
||||||
if start_node == null:
|
if start_node == null:
|
||||||
print("Error: No node found at starting position ", start_position)
|
print("Error: No node found at starting position ", start_position)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Initialize BFS
|
# Initialize BFS
|
||||||
var queue: Array = [[start_node, 0]] # Each element is [node, current_distance]
|
var queue: Array = [[start_node, 0]] # Each element is [node, current_distance]
|
||||||
var visited: Dictionary = {start_node: true}
|
var visited: Dictionary = {start_node: true}
|
||||||
var result_nodes: Array[MPNavigationNode] = []
|
var result_nodes: Array[MPNavigationNode] = []
|
||||||
|
|
||||||
while queue.size() > 0:
|
while queue.size() > 0:
|
||||||
var current = queue.pop_front()
|
var current = queue.pop_front()
|
||||||
var current_node: MPNavigationNode = current[0]
|
var current_node: MPNavigationNode = current[0]
|
||||||
var current_distance: int = current[1]
|
var current_distance: int = current[1]
|
||||||
|
|
||||||
# If the target distance is reached, add the node to the result
|
# If the target distance is reached, add the node to the result
|
||||||
if current_distance == distance:
|
if current_distance == distance:
|
||||||
result_nodes.append(current_node)
|
result_nodes.append(current_node)
|
||||||
continue # Do not explore further from this node
|
continue # Do not explore further from this node
|
||||||
|
|
||||||
# If the current distance exceeds the target, stop exploring
|
# If the current distance exceeds the target, stop exploring
|
||||||
if current_distance > distance:
|
if current_distance > distance:
|
||||||
break
|
break
|
||||||
|
|
||||||
# Explore neighbors of the current node
|
# Explore neighbors of the current node
|
||||||
var neighbors: Array[MPNavigationNode] = navigation_graph.get_connections(current_node)
|
var neighbors: Array[MPNavigationNode] = navigation_graph.get_connections(current_node)
|
||||||
for neighbor in neighbors:
|
for neighbor in neighbors:
|
||||||
if not visited.has(neighbor):
|
if not visited.has(neighbor):
|
||||||
visited[neighbor] = true
|
visited[neighbor] = true
|
||||||
queue.append([neighbor, current_distance + 1])
|
queue.append([neighbor, current_distance + 1])
|
||||||
|
|
||||||
return result_nodes
|
return result_nodes
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
[gd_resource type="TileSet" load_steps=7 format=3 uid="uid://cm6wi7fjgfk56"]
|
[gd_resource type="TileSet" load_steps=9 format=3 uid="uid://cm6wi7fjgfk56"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bif0n5c12bwrh" path="res://addons/sprout_lands_tilemap/assets/Tilesets/Water.png" id="1_j6mfj"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cmb6k735nhxmy" path="res://scenes/mario-party/img/data_layer.png" id="1_pdyr2"]
|
[ext_resource type="Texture2D" uid="uid://cmb6k735nhxmy" path="res://scenes/mario-party/img/data_layer.png" id="1_pdyr2"]
|
||||||
[ext_resource type="Texture2D" uid="uid://bngfh7nslvij1" path="res://addons/sprout_lands_tilemap/assets/Tilesets/Grass.png" id="2_ev1xx"]
|
[ext_resource type="Texture2D" uid="uid://bngfh7nslvij1" path="res://addons/sprout_lands_tilemap/assets/Tilesets/Grass.png" id="2_ev1xx"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cvemyer4jq6we" path="res://addons/sprout_lands_tilemap/assets/Objects/Paths.png" id="3_eigvu"]
|
[ext_resource type="Texture2D" uid="uid://cvemyer4jq6we" path="res://addons/sprout_lands_tilemap/assets/Objects/Paths.png" id="3_eigvu"]
|
||||||
|
|
||||||
|
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_wcjgp"]
|
||||||
|
texture = ExtResource("1_j6mfj")
|
||||||
|
0:0/0 = 0
|
||||||
|
1:0/0 = 0
|
||||||
|
2:0/0 = 0
|
||||||
|
3:0/0 = 0
|
||||||
|
|
||||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_tp5r2"]
|
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_tp5r2"]
|
||||||
texture = ExtResource("1_pdyr2")
|
texture = ExtResource("1_pdyr2")
|
||||||
0:0/0 = 0
|
0:0/0 = 0
|
||||||
|
@ -120,3 +128,4 @@ custom_data_layer_2/type = 4
|
||||||
sources/1 = SubResource("TileSetAtlasSource_tp5r2")
|
sources/1 = SubResource("TileSetAtlasSource_tp5r2")
|
||||||
sources/2 = SubResource("TileSetAtlasSource_m45pk")
|
sources/2 = SubResource("TileSetAtlasSource_m45pk")
|
||||||
sources/3 = SubResource("TileSetAtlasSource_xi3gi")
|
sources/3 = SubResource("TileSetAtlasSource_xi3gi")
|
||||||
|
sources/0 = SubResource("TileSetAtlasSource_wcjgp")
|
||||||
|
|
|
@ -2,7 +2,6 @@ extends Node2D
|
||||||
|
|
||||||
@onready var floor: NavigationRegion2D = $BaseNavigationRegion2D
|
@onready var floor: NavigationRegion2D = $BaseNavigationRegion2D
|
||||||
@onready var road: NavigationRegion2D = $CheapPathNavigationRegion2D
|
@onready var road: NavigationRegion2D = $CheapPathNavigationRegion2D
|
||||||
# @onready var road_poly: Polygon2D = $CheapPathNavigationRegion2D/CheapPath
|
|
||||||
@onready var road_poly: Polygon2D = $BaseNavigationRegion2D/CheapPath
|
@onready var road_poly: Polygon2D = $BaseNavigationRegion2D/CheapPath
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,3 @@
|
||||||
|
# Godot 4+ specific ignores
|
||||||
|
.godot/
|
||||||
|
/android/
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
After Width: | Height: | Size: 994 B |
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://beggt5ndi6j4p"
|
||||||
|
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://icon.svg"
|
||||||
|
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,15 @@
|
||||||
|
; Engine configuration file.
|
||||||
|
; It's best edited using the editor UI and not directly,
|
||||||
|
; since the parameters that go here are not all obvious.
|
||||||
|
;
|
||||||
|
; Format:
|
||||||
|
; [section] ; section goes between []
|
||||||
|
; param=value ; assign values to parameters
|
||||||
|
|
||||||
|
config_version=5
|
||||||
|
|
||||||
|
[application]
|
||||||
|
|
||||||
|
config/name="state"
|
||||||
|
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||||
|
config/icon="res://icon.svg"
|
|
@ -0,0 +1,18 @@
|
||||||
|
class_name State
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
var state_machine: StateMachine
|
||||||
|
var character: CharacterBody2D
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
func state_enter() -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
func state_process(delta: float) -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
func state_exit() -> void:
|
||||||
|
pass
|
|
@ -0,0 +1,19 @@
|
||||||
|
class_name StateMachine
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
var current_state: State:
|
||||||
|
set(value):
|
||||||
|
if current_state:
|
||||||
|
current_state.state_exit()
|
||||||
|
current_state = value
|
||||||
|
current_state.state_enter()
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
for child in self.get_children():
|
||||||
|
if true:
|
||||||
|
print(child)
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
pass
|
|
@ -0,0 +1,25 @@
|
||||||
|
[gd_scene load_steps=5 format=3 uid="uid://cgdvptekjbpgq"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://beggt5ndi6j4p" path="res://icon.svg" id="1_jgx5y"]
|
||||||
|
[ext_resource type="Script" path="res://scenes/state/StateMachine.gd" id="2_d1xqo"]
|
||||||
|
[ext_resource type="Script" path="res://scenes/state/state_idle.gd" id="3_r1btx"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_tr1gq"]
|
||||||
|
radius = 60.0
|
||||||
|
height = 122.0
|
||||||
|
|
||||||
|
[node name="StateMachineWorld" type="Node2D"]
|
||||||
|
|
||||||
|
[node name="CharacterBody2D" type="CharacterBody2D" parent="."]
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="CharacterBody2D"]
|
||||||
|
texture = ExtResource("1_jgx5y")
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="CharacterBody2D"]
|
||||||
|
shape = SubResource("CapsuleShape2D_tr1gq")
|
||||||
|
|
||||||
|
[node name="StateMachine" type="Node" parent="CharacterBody2D"]
|
||||||
|
script = ExtResource("2_d1xqo")
|
||||||
|
|
||||||
|
[node name="idle" type="Node" parent="CharacterBody2D/StateMachine"]
|
||||||
|
script = ExtResource("3_r1btx")
|
|
@ -0,0 +1,10 @@
|
||||||
|
extends State
|
||||||
|
|
||||||
|
func state_enter():
|
||||||
|
print("idle enter")
|
||||||
|
|
||||||
|
func state_process(delta: float) -> void:
|
||||||
|
print("idle process")
|
||||||
|
|
||||||
|
func state_exit() -> void:
|
||||||
|
print("idle exit")
|
Loading…
Reference in New Issue