Grafo AStar2D

Quarenta pontos ligados por distância, com o AStar2D traçando rota entre dois deles. Nada aqui é uma grade, o grafo é o que você conectar.

O editor acima está rodando este projeto. Mude uma linha e ele recarrega.

Classes do Godot usadas

AStar2DLine2DPolygon2D

O 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)

Mais exemplos de Navegação