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)

Más ejemplos de Navegación