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