Grafo AStar2D
Cuarenta puntos enlazados por distancia, con AStar2D trazando ruta entre dos de ellos. Aquí nada es una rejilla, el grafo es lo que tú conectes.
El editor de arriba está ejecutando este proyecto. Cambia una línea y se recarga.
Clases de Godot usadas
AStar2DLine2DPolygon2D
El código
scripts/main.gd
extends Node
const COLS: int = 8
const ROWS: int = 5
const LINK_RANGE: float = 250.0
const HEAVY_WEIGHT: float = 7.0
const WALKER_SPEED: float = 300.0
var astar: AStar2D = AStar2D.new()
var start_id: int = 0
var goal_id: int = 0
var links: Node2D
var path_line: Line2D
var dots: Dictionary = {}
var walker: Polygon2D
var walk_path: PackedVector2Array = PackedVector2Array()
var travelled: float = 0.0
var info: Label
func _ready() -> void:
_add_background()
var rng: RandomNumberGenerator = RandomNumberGenerator.new()
rng.seed = 8814
var view: Vector2 = get_viewport().get_visible_rect().size
var step: Vector2 = Vector2(view.x - 220.0, view.y - 250.0) / Vector2(COLS - 1, ROWS - 1)
for y in ROWS:
for x in COLS:
var id: int = y * COLS + x
var at: Vector2 = Vector2(110, 150) + Vector2(x, y) * step + Vector2(rng.randf_range(-34, 34), rng.randf_range(-34, 34))
astar.add_point(id, at, HEAVY_WEIGHT if rng.randf() < 0.22 else 1.0)
links = Node2D.new()
add_child(links)
for id in astar.get_point_ids():
for other in astar.get_point_ids():
if other <= id:
continue
if astar.get_point_position(id).distance_to(astar.get_point_position(other)) > LINK_RANGE:
continue
astar.connect_points(id, other)
links.add_child(_make_link(astar.get_point_position(id), astar.get_point_position(other), 2.0, Color(0.55, 0.65, 0.95, 0.28)))
path_line = Line2D.new()
path_line.width = 6.0
path_line.default_color = Color('#8eef97')
add_child(path_line)
for id in astar.get_point_ids():
var dot: Polygon2D = _make_circle(10.0, Color('#f3d48d') if astar.get_point_weight_scale(id) > 1.0 else Color('#8da5f3'))
dot.position = astar.get_point_position(id)
add_child(dot)
dots[id] = dot
start_id = 2 * COLS
goal_id = 2 * COLS + COLS - 1
walker = _make_circle(12.0, Color('#fc7f7f'))
add_child(walker)
info = Label.new()
info.position = Vector2(110, 46)
info.add_theme_font_size_override('font_size', 20)
info.add_theme_color_override('font_color', Color('#f3d48d'))
add_child(info)
_repath()
print('%d points linked by distance, the yellow ones cost %d to cross, click near a point to route there' % [astar.get_point_count(), int(HEAVY_WEIGHT)])
func _process(delta: float) -> void:
if walk_path.size() < 2:
return
travelled += WALKER_SPEED * delta
var remaining: float = travelled
for i in range(walk_path.size() - 1):
var span: float = walk_path[i].distance_to(walk_path[i + 1])
if remaining <= span:
walker.position = walk_path[i].lerp(walk_path[i + 1], remaining / span)
return
remaining -= span
travelled = 0.0
walker.position = walk_path[0]
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
var picked: int = astar.get_closest_point(event.position)
if picked != start_id:
goal_id = picked
_repath()
func _repath() -> void:
for id in dots:
dots[id].scale = Vector2.ONE
dots[id].color = Color('#f3d48d') if astar.get_point_weight_scale(id) > 1.0 else Color('#8da5f3')
walk_path = astar.get_point_path(start_id, goal_id)
path_line.points = walk_path
travelled = 0.0
var cost: float = 0.0
for id in astar.get_id_path(start_id, goal_id):
cost += astar.get_point_weight_scale(id)
dots[id].scale = Vector2(1.5, 1.5)
dots[start_id].scale = Vector2(2.1, 2.1)
dots[start_id].color = Color.WHITE
dots[goal_id].scale = Vector2(2.1, 2.1)
dots[goal_id].color = Color('#8eef97')
info.text = 'white to green in %d hops, weight sum %d' % [walk_path.size(), int(cost)]
func _make_link(from: Vector2, to: Vector2, width: float, color: Color) -> Line2D:
var line: Line2D = Line2D.new()
line.points = PackedVector2Array([from, to])
line.width = width
line.default_color = color
return line
func _make_circle(radius: float, color: Color) -> Polygon2D:
var points: PackedVector2Array = PackedVector2Array()
for i in 20:
points.append(Vector2(cos(TAU * i / 20.0), sin(TAU * i / 20.0)) * radius)
var circle: Polygon2D = Polygon2D.new()
circle.polygon = points
circle.color = color
return circle
func _add_background() -> void:
var background: ColorRect = ColorRect.new()
background.color = Color('#12141a')
add_child(background)
# ColorRect defaults to MOUSE_FILTER_STOP and would eat the click before _unhandled_input
background.mouse_filter = Control.MOUSE_FILTER_IGNORE
background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)