Agente 2D

Uma região de navegação gerada por código e um agente percorrendo quatro cantos por ela. Clique em qualquer lugar para mandá-lo para outro ponto.

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

Classes do Godot usadas

NavigationRegion2DNavigationAgent2DNavigationPolygon

O código

scripts/main.gd
extends Node

const SPEED: float = 300.0
const BODY_RADIUS: float = 16.0
const MARGIN: float = 60.0

var walls: Array[Rect2] = []
var targets: PackedVector2Array = PackedVector2Array()
var target_index: int = 0

var body: Node2D
var agent: NavigationAgent2D
var path_line: Line2D
var marker: Polygon2D


func _ready() -> void:
	_add_background()

	var view: Vector2 = get_viewport().get_visible_rect().size
	var bounds: Rect2 = Rect2(MARGIN, MARGIN, view.x - MARGIN * 2.0, view.y - MARGIN * 2.0)

	walls = [
		Rect2(view.x * 0.26, MARGIN, 60, view.y * 0.62),
		Rect2(view.x * 0.49, view.y * 0.34, 60, view.y - view.y * 0.34 - MARGIN),
		Rect2(view.x * 0.72, MARGIN, 60, view.y * 0.62),
	]

	targets = PackedVector2Array(
		[
			Vector2(view.x - 110, view.y - 100),
			Vector2(view.x - 110, 120),
			Vector2(110, view.y - 100),
			Vector2(110, 120),
		]
	)

	var region: NavigationRegion2D = NavigationRegion2D.new()
	region.navigation_polygon = _bake(bounds)
	add_child(region)

	_add_region_visual(region.navigation_polygon)

	for wall in walls:
		var block: Polygon2D = _make_rect(wall.size, Color('#2a2f3d'))
		block.position = wall.position + wall.size * 0.5
		add_child(block)

	path_line = Line2D.new()
	path_line.width = 5.0
	path_line.default_color = Color('#8eef97')
	add_child(path_line)

	marker = _make_circle(12.0, Color('#f3d48d'))
	marker.position = targets[0]
	add_child(marker)

	body = Node2D.new()
	body.position = Vector2(110, 120)
	body.add_child(_make_circle(BODY_RADIUS, Color('#8da5f3')))
	add_child(body)

	agent = NavigationAgent2D.new()
	agent.radius = BODY_RADIUS
	agent.path_desired_distance = 12.0
	agent.target_desired_distance = 12.0
	body.add_child(agent)

	print('the agent walks a loop of four corners, click anywhere to send it somewhere else')


func _physics_process(delta: float) -> void:
	var next: Vector2 = agent.get_next_path_position()
	var path: PackedVector2Array = agent.get_current_navigation_path()

	# the navigation map only syncs at the end of a physics frame, so the first queries come back empty
	if path.is_empty():
		_send_to(targets[target_index])
		return

	path_line.points = path

	if agent.is_navigation_finished():
		target_index = (target_index + 1) % targets.size()
		_send_to(targets[target_index])
		return

	body.position = body.position.move_toward(next, SPEED * delta)


func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
		_send_to(event.position)


func _send_to(where: Vector2) -> void:
	agent.target_position = where
	marker.position = where


func _bake(bounds: Rect2) -> NavigationPolygon:
	var poly: NavigationPolygon = NavigationPolygon.new()
	poly.agent_radius = BODY_RADIUS + 8.0

	var source: NavigationMeshSourceGeometryData2D = NavigationMeshSourceGeometryData2D.new()
	source.add_traversable_outline(_rect_points(bounds))

	for wall in walls:
		source.add_obstruction_outline(_rect_points(wall))

	NavigationServer2D.bake_from_source_geometry_data(poly, source)

	return poly


func _add_region_visual(poly: NavigationPolygon) -> void:
	var vertices: PackedVector2Array = poly.get_vertices()

	for i in poly.get_polygon_count():
		var points: PackedVector2Array = PackedVector2Array()

		for index in poly.get_polygon(i):
			points.append(vertices[index])

		var face: Polygon2D = Polygon2D.new()
		face.polygon = points
		face.color = Color(0.55, 0.65, 0.95, 0.12)
		add_child(face)

		var edge: Line2D = Line2D.new()
		edge.points = points
		edge.closed = true
		edge.width = 2.0
		edge.default_color = Color(0.55, 0.65, 0.95, 0.4)
		add_child(edge)


func _rect_points(rect: Rect2) -> PackedVector2Array:
	return PackedVector2Array([rect.position, Vector2(rect.end.x, rect.position.y), rect.end, Vector2(rect.position.x, rect.end.y)])


func _make_rect(size: Vector2, color: Color) -> Polygon2D:
	var rect: Polygon2D = Polygon2D.new()
	rect.polygon = PackedVector2Array([-size * 0.5, Vector2(size.x, -size.y) * 0.5, size * 0.5, Vector2(-size.x, size.y) * 0.5])
	rect.color = color

	return rect


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