AStar2D 图
四十个点按距离相连,AStar2D 在其中两点之间寻路。这里没有网格,图就是你连出来的样子。
上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。
用到的 Godot 类
AStar2DLine2DPolygon2D
代码
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)