靠近与远离

靠近和远离就是同样两行代码翻转一个符号。画出来的向量分别是期望速度、当前速度和两者之差。

上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。

用到的 Godot 类

Node2DPolygon2DLine2D

代码

scripts/main.gd
extends Node

const ORBIT_RADIUS: float = 185.0
const ORBIT_SPEED: float = 1.1
const PADDING: float = 70.0

var panels: Array[Rect2] = []
var agents: Array[Node2D] = []
var markers: Array[Polygon2D] = []
var elapsed: float = 0.0


func _ready() -> void:
	_add_background()

	var view: Vector2 = get_viewport().get_visible_rect().size
	var half: float = view.x * 0.5

	panels = [
		Rect2(PADDING, PADDING + 40.0, half - PADDING * 1.5, view.y - PADDING * 2.0 - 40.0),
		Rect2(half + PADDING * 0.5, PADDING + 40.0, half - PADDING * 1.5, view.y - PADDING * 2.0 - 40.0),
	]

	var divider: Line2D = Line2D.new()
	divider.points = PackedVector2Array([Vector2(half, PADDING), Vector2(half, view.y - PADDING)])
	divider.width = 2.0
	divider.default_color = Color(0.55, 0.65, 0.95, 0.25)
	add_child(divider)

	for i in 2:
		var frame: Polygon2D = Polygon2D.new()
		frame.polygon = PackedVector2Array([panels[i].position, Vector2(panels[i].end.x, panels[i].position.y), panels[i].end, Vector2(panels[i].position.x, panels[i].end.y)])
		frame.color = Color(0.55, 0.65, 0.95, 0.05)
		add_child(frame)

		var title: Label = Label.new()
		title.text = 'seek' if i == 0 else 'flee'
		title.position = Vector2(panels[i].position.x, PADDING - 10.0)
		title.add_theme_font_size_override('font_size', 26)
		title.add_theme_color_override('font_color', Color('#8eef97') if i == 0 else Color('#fc7f7f'))
		add_child(title)

		var marker: Polygon2D = _make_circle(14.0, Color('#f3d48d'))
		add_child(marker)
		markers.append(marker)

		var agent: Node2D = Node2D.new()
		agent.set_script(Get.script('agent'))
		agent.flee = i == 1
		agent.bounds = panels[i].grow(-80.0)
		agent.position = panels[i].get_center() + Vector2(0, 120)
		add_child(agent)
		agents.append(agent)

	var legend: Label = Label.new()
	legend.text = 'yellow: desired velocity     blue: current velocity     green: desired minus current'
	legend.position = Vector2(PADDING, view.y - PADDING + 14.0)
	legend.add_theme_font_size_override('font_size', 18)
	legend.add_theme_color_override('font_color', Color('#f3d48d'))
	add_child(legend)

	print('same two lines of math with one sign flipped, the left agent closes on the dot and the right one keeps away from it')


func _process(delta: float) -> void:
	elapsed += delta

	var angle: float = elapsed * ORBIT_SPEED

	markers[0].position = panels[0].get_center() + Vector2(cos(angle), sin(angle * 0.7)) * ORBIT_RADIUS
	agents[0].target = markers[0].position

	markers[1].position = panels[1].get_center() + Vector2(cos(angle), sin(angle)) * panels[1].size * 0.44
	agents[1].target = markers[1].position


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)
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
scripts/agent.gd
extends Node2D

const MAX_SPEED: float = 260.0
const MAX_FORCE: float = 230.0
const DRAW_SCALE: float = 0.45

var flee: bool = false
var bounds: Rect2 = Rect2()
var target: Vector2 = Vector2.ZERO
var velocity: Vector2 = Vector2(MAX_SPEED, 0)

var hull: Polygon2D
var desired_line: Line2D
var velocity_line: Line2D
var steering_line: Line2D


func _ready() -> void:
	desired_line = _make_line(Color('#f3d48d'))
	velocity_line = _make_line(Color('#8da5f3'))
	steering_line = _make_line(Color('#8eef97'))

	hull = Polygon2D.new()
	hull.polygon = PackedVector2Array([Vector2(26, 0), Vector2(-18, 15), Vector2(-18, -15)])
	hull.color = Color('#fc7f7f') if flee else Color('#8eef97')
	add_child(hull)


func _process(delta: float) -> void:
	var to_target: Vector2 = target - position
	var desired: Vector2 = (-to_target if flee else to_target).normalized() * MAX_SPEED
	var steering: Vector2 = (desired - velocity).limit_length(MAX_FORCE)

	velocity = (velocity + steering * delta).limit_length(MAX_SPEED)
	position += velocity * delta

	_stay_inside()

	hull.rotation = velocity.angle()
	desired_line.points = PackedVector2Array([Vector2.ZERO, desired * DRAW_SCALE])
	velocity_line.points = PackedVector2Array([Vector2.ZERO, velocity * DRAW_SCALE])
	steering_line.points = PackedVector2Array([velocity * DRAW_SCALE, desired * DRAW_SCALE])


func _stay_inside() -> void:
	if (position.x < bounds.position.x and velocity.x < 0.0) or (position.x > bounds.end.x and velocity.x > 0.0):
		velocity.x = -velocity.x

	if (position.y < bounds.position.y and velocity.y < 0.0) or (position.y > bounds.end.y and velocity.y > 0.0):
		velocity.y = -velocity.y

	position = position.clamp(bounds.position, bounds.end)


func _make_line(color: Color) -> Line2D:
	var line: Line2D = Line2D.new()
	line.width = 4.0
	line.default_color = color
	add_child(line)

	return line

更多寻路示例