Seek and flee

Seek and flee are the same two lines with one sign flipped. The drawn vectors are the desired velocity, the current one and the difference between them.

The editor above is running this project. Change a line and it reloads.

Godot classes used

Node2DPolygon2DLine2D

The code

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

More Navigation examples