↓
iaen.es Devlog Leer en español

Gotic · 3 min read

A buoy time-trial regatta in Godot 4: detecting when you pass between two buoys

Eleven buoy gates in Caleta's lagoon and around the sandbar island, rowed against the clock. How to check you cross them in order and the right way, and how to set medal times without players.

#races#minigames#mods#godot#testing

A buoy time-trial regatta in Godot 4: detecting when you pass between two buoys

With the boat in the water, the lagoon at Caleta Salobre was asking for a race. The Regata de Caleta is eleven buoy gates, 761 m of rowing between the shore, the sandbar island and the open water, against the clock.

You don't need to talk to anyone: there's no "use" button in the boat, so the clock starts when you cross the SALIDA (start) on board and stops at the META (finish). You have to pass between the two buoys of each gate, in order. The current gate's flag is gold, the next one white and the rest red. Get off the boat and the attempt is over. Gold is 200 s, silver 235, with a 300 s limit; gold also earns a pennant on the boat. A sign on the shore by the pier explains the rules and shows your record.

How it's built

Each gate is a point in the world plus a direction, the average of where you're coming from and where you're heading. To know whether the boat's last bit of movement crossed the gate, all it takes is a dot product on each side and an intersection point:

static func crosses(i: int, a: Vector2, b: Vector2) -> bool:
	var center: Vector2 = GATES[i]
	var axis := direction(i)
	var da := (a - center).dot(axis)
	var db := (b - center).dot(axis)
	if da > 0.0 or db < 0.0 or is_equal_approx(da, db):
		return false
	var hit := a.lerp(b, -da / (db - da))
	return hit.distance_to(center) < HALF

If the boat was before the gate (negative da) and is now past it (positive db), it works out where it crossed the line, and that counts if it was within 6 m of the centre: between the buoys, which are 12 m apart. Crossing backwards or outside doesn't count.

I measured the course outside the game. I wanted it to go all the way round the island, but on the southwest side a sandbank joins it to the shore and you can't circle it. So the outward leg hugs the island and the return leg runs further out to sea. Every gate and every straight, with 3 m either side, has over 1 m of water (the boat needs 0.6), and a test checks it again.

The medal times don't come from players. An autopilot using the boat's own rowing code completes the lap in 175.4 s without touching the bottom (172.9 s with the engine's frames). Gold is set at 200 s, about 25 s above that.

What went wrong

No game bugs were recorded for the regatta, but one test cost far too much. The full lap moved the whole world frame by frame and took 56 s. Calling the boat's mover directly, which is the same code, it takes 7 s. Lesson for the rest: a test that waits on frames with the whole island loaded is slow.

The mutations the suite had to catch (and did): crossing without being in the boat, getting off without ending the attempt, the clock running with the menu open, and any route counting. With the regatta, the suite reached 1,049 passing checks.

Still unverified

Not tested on a phone. The medal times come from an autopilot, not players: gold could be easy or impossible rowing with a thumb. The bell at each gate was generated and measured, but nobody has listened to it.

More from Gotic →