doc 1 of 6

Steering & Flocking — movement that looks alive, and agents that stop walking through each other

Your enemies walk in straight lines toward the player and stop dead on arrival. Twenty of them end up stacked on the same pixel. This shelf replaces that with one equation applied over and over: steering is the difference between the velocity you want and the velocity you have, capped and applied as a force. Seek, flee, arrive, and all three flocking rules are that same equation with different answers to "what do I want". By the end you have a flock that holds formation, curves around walls, refuses to interpenetrate, and still runs at a thousand members — with the frame times measured rather than assumed.

What this shelf teaches#

PartYou buildThe idea
01Seek, flee, and arrive — three behaviours from the same eight lines, one value swapped.Steering is desired_velocity - velocity, capped on turn rate and top speed.
02Sixty boids flocking as one body, retuned live with three exported weights.Cohesion, alignment, separation are Part 01's equation aimed at different targets.
03The flock crossing a room of pillars with a speed-scaled look-ahead probe.Collision reacts to a mistake; avoidance is a force cast ahead of it.
04The same flock on NavigationAgent2D RVO avoidance, toggled against your hand-rolled separation.RVO reads neighbour velocities as well as positions — it buys non-interpenetration, costs control.
05A timing harness measuring the neighbour search, and a spatial hash replacing it.Every-agent-checks-every-agent is quadratic; a grid makes the loop smaller, not faster.

Part 01's chase carries a visible turning arc, and arrive slows to a stop instead of buzzing across the target — every behaviour built afterward only changes what desired_velocity means. Part 02's sixty boids turn together, keep spacing, and reform after you scatter them; a flock is the weighted sum of those three rules, and each one looks broken in isolation in a way that explains exactly what it contributes. Part 03 ships a runtime toggle back to plain collision so you can see the difference in one keypress, and the avoidance force is applied sideways rather than backwards. Part 04's agents predict each other and slip past instead of colliding, and a key toggles between your hand-rolled separation and the built-in solver so you can judge both in the same scene. Part 05 records a before-and-after table at 100, 400, and 1000 agents from your own machine — the number that actually justifies the grid.

Where reasonable people differ#

Four forks on this shelf have no correct answer. Each one has a question that decides it for your project.

Hand-rolled steering forces, or the engine's RVO solver. Forces are yours: you can weight them, print them, exaggerate them, and make a swarm feel panicked or lazy by moving a slider. RVO is a solver: it predicts collisions from both agents' velocities and produces velocities that genuinely do not intersect, but it hands you a corrected vector with no explanation and few dials. Deciding question: is non-interpenetration a hard requirement — units in a strategy game, a crowd that must not clip — or is the look the requirement? The first is RVO's job. The second is yours.

Steering forces, or pathfinding. A look-ahead probe handles convex obstacles and short detours with no map data and no precomputation. It also oscillates forever in a concave pocket, because it has no memory and no plan. Pathfinding costs you a navigation mesh, a rebuild step when the world changes, and a path to follow. Deciding question: can an agent in your world get into a dead end it cannot see out of? If yes, no weight tuning saves you; the missing piece is a path.

Screen wrap, or a turn-back force at the boundary. Wrapping keeps the flock's shape intact and costs two lines. A turn-back force produces the milling, folding behaviour real schools show when they hit a wall, and costs a tuned margin plus a force that fights your other forces. Deciding question: is the boundary part of the fiction, or is it just the edge of the demo? An arena wall the player can see should push back. An invisible edge should wrap.

A spatial grid, or a smaller flock. A grid turns the quadratic neighbour search into something closer to linear, but it costs a rebuild every frame and a cell size you have to justify. Below some agent count the rebuild costs more than the search it replaces. Deciding question: have you measured? Part 05 makes you produce the crossover number for your machine, and shipping a grid for forty boids is a loss you can then prove rather than argue about.

Prerequisites#

  • Godot 4.7, 2D, Godot Physics. Everything here runs in a single scene with no addons.
  • Comfort with CharacterBody2D, _physics_process, and move_and_slide(). If those are new, build something that moves under keyboard input first.
  • Typed GDScript. Every snippet declares its types, including -> void on functions that return nothing. If typed declarations are unfamiliar, read Typed GDScript before Part 01.
  • Vector2 arithmetic: addition, subtraction, length(), normalized(), dot(). You do not need to remember the formulas, but you should recognise the names.
  • Part 05 uses Dictionary and Array and leans on the fact that both are reference types. The reference-semantics trap is explained where it bites, not assumed.

Parts run in order. Part 02 rebuilds nothing from Part 01 — it calls the same equation three times — and Parts 03 through 05 all extend the Part 02 scene.

Where this shows up in the projects#

Enemies that need to hold a loose formation without stacking are a state-machine problem and a steering problem at the same time: the machine decides chase or retreat, and the steering decides what that looks like. The handoff point is covered in When you need a state machine — a state that sets desired_velocity and nothing else composes cleanly with everything on this shelf.


Next: Part 01 — Seek, flee, arrive. Start there even if you already know the three flocking rules — Part 02 assumes the force equation and the two caps are already in your fingers.