↓
iaen.es Devlog Leer en español

Gotic · 4 min read

A sky-ring time trial in Godot 4, and how to tell you flew through

Ten rings in the air, a broom and a clock. How I detect that you pass through each ring the right way, why the rings are opaque and where the medal times come from.

#races#minigames#mods#godot#bugs

A sky-ring time trial in Godot 4, and how to tell you flew through

The broom was the best-liked part of the game, and it needed something to do. That's how Aros del cielo ("sky rings") came about: next to the starting beach there's a sign; talk to it while carrying the broom, take off, and the START ring lights up. The clock starts as you fly through it and stops at the FINISH, ten rings later, after a loop over Brumacosta and back along the coast. You have to pass them in order and through the middle, and landing or getting off the broom ends the attempt.

The next ring glows gold, the one after it white, and the ones you've cleared disappear. Gold at 80 s, silver at 95, time limit 120. Your record is saved and, with your first gold, the broom starts leaving a golden trail, the one in the cover image.

How it's built

The interesting problem is deciding whether you went through a ring. Being near its centre isn't enough: you have to pass through it, inside the hoop and in the direction of the course, so that dipping in and out the same side or skimming past the outside doesn't count. So each physics step is a segment from the previous position to the current one, and I check whether that segment crosses the ring's plane, front to back, less than 5 m from its centre:

static func crosses(i: int, a: Vector3, b: Vector3) -> bool:
	var center: Vector3 = RINGS[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   # solo de delante hacia detrás, en el sentido del recorrido
	var hit := a.lerp(b, -da / (db - da))
	return hit.distance_to(center) < RADIUS

(The comment says: only front to back, in the direction of the course.) Each ring's direction is the average of the line coming in from the previous ring and the line going out to the next, and the ring is oriented along it, so you fly through it head-on even where the course bends. A jump of more than 12 m in one step doesn't count, so a teleport can't sneak in as a pass.

Ring positions are constants. I measured their heights outside the game against the terrain: every ring and the straight line to the next stay above the ground, the sea and the treetops (14 m above ground over forest), and a test checks it.

The medal times aren't guesses. A test flies the broom with its real physics, steering with the camera the way a player would, and completes the loop (about 950 m) in 64.6 s. Gold went at 80 s from there. This mod was built by one of two Claude sessions working in parallel that round, each with its own mods and its own copy of the project.

What went wrong

  • The sea ate the rings. The first version was transparent. The sea is transparent too, gets drawn afterwards, and hid the bottom half of a ring sitting 3 m above the water. They're opaque now: anything that has to show over water can't be transparent.
  • A black ball behind the broom. The trail used camera-facing particles (BILLBOARD_PARTICLES). In the Compatibility renderer, particles that hadn't spawned yet were all drawn bunched up at the emitter, with zero vertex colour. I found it by isolating it in a separate scene. The trail is now small spheres with no billboard.
  • A test that tested nothing. I made the clock keep running with the menu open, to see whether any test would catch it, and none did: the test looked before the START ring, when the clock wasn't running yet. Now it checks mid-lap.

That's 21 checks in all (19 s, almost all of it the physics lap). The cover screenshot cost 447 draw calls; over Brumacosta at 1600×720 it reached 583.

Still unverified

It hasn't been played on a phone: no FPS, no flying through rings with real fingers. The medal times come from an autopilot, not players. And the ring sound (a whoosh and two bell notes that rise in pitch with each ring) is generated in code and doesn't clip, but I haven't heard it.

More from Gotic →