Architectural Thinking: The "Validation-Chain" Pattern
A Master implementation treats Time Trials as a State-Validated Sequence. Recording a time is easy; ensuring the player didn't cheat via shortcuts requires a strictly ordered CheckpointManager.
Core Responsibilities
- TimeTrialManager: The central clock. Validates checkpoint order and handles "Best Lap" logic.
- GhostRecorder: Captures high-frequency transform data. Uses delta-time timestamps for frame-independent playback.
- Checkpoint: Spatial triggers that notify the Manager.
Expert Code Patterns
1. Robust Checkpoint Validation
Prevent "Shortcut Cheating" by requiring checkpoints to be cleared in numerical order.
Wire Area body_entered (physics) → TimeTrialManager.pass_checkpoint(index). The manager owns usec timing — see script.
2. Space-Efficient Ghosting
Sample at a fixed rate (e.g. 10 Hz). Lerp position; slerp Quaternion rotation (ghost_recorder.gd / ghost_replayer.gd). Never Euler-lerp ghost heading.
Master Decision Matrix: Data Storage
| Format |
Best For |
Implementation |
| Dictionary Array |
Prototyping |
Simple [{t: 0.1, p: pos}, ...] |
| Typed Array |
Performance |
PackedVector3Array for positions. |
| JSON/Binary |
Saving |
FileAccess.get_var() to save ghost files. |
NEVER Do
- NEVER use OS.get_ticks_msec() for ultra-precise race timing — Millisecond resolution is too coarse for high-end racing games. Use
Time.get_ticks_usec() for microsecond precision.
- NEVER rely exclusively on _process() for finish line triggers — Visual frames can skip during lag. Always evaluate physical overlaps in
_physics_process() to guarantee detection within the fixed physics step.
- NEVER evaluate Area3D overlaps immediately after instantiation — The physics server requires at least one physics frame to synchronize.
await get_tree().physics_frame before checking for players.
- NEVER scale a CollisionShape3D on a checkpoint non-uniformly — This breaks the underlying SAT collision math. Always scale the internal shape resource (e.g.,
BoxShape3D.size) instead.
- NEVER use TCP (reliable) for syncing positions in multiplayer racing — Congestion algorithms cause huge spikes. Use
ENetMultiplayerPeer with TRANSFER_MODE_UNRELIABLE for high-frequency position updates.
- NEVER trust client-side finish line/lap crossing — Always validate triggers on the authoritative server using
multiplayer.is_server() to prevent cheating.
- NEVER use standard float equality (==) for record lap times — Use
is_equal_approx() to account for precision loss in accumulated time variables.
- NEVER hardcode input checks without flushing the buffer — For frame-perfect boost/stop responses, call
Input.flush_buffered_events() to ensure the engine has processed the latest raw input.
- NEVER allocate new Vector3 arrays inside fast path-following loops — This triggers the garbage collector. Use
PackedVector3Array to maintain a contiguous memory block.
- NEVER use dynamic string paths ($"../Checkpoint") in tight loops — Lookups are slow. Use
@onready to cache node references during initialization.
- NEVER record the whole player object for ghosts — Only record core transforms (position/rotation). Recording the whole object is memory-intensive and unnecessary for visual ghosts.
- NEVER give the ghost collision — It should be a purely visual indicator (e.g., semi-transparent) to avoid disrupting the player's line.
- NEVER neglect checkpoint sequencing — Don't just check if the player hit the finish line. Verify they passed every intermediate checkpoint in the correct order.
- NEVER use Area3D without monitoring optimization — Checkpoints should only look for the
Player physics layer to minimize the number of physics overlap calculations.
- NEVER use standard lerp for ghost rotation — Use
slerp() or Quaternion.slerp() to avoid gimbal lock and ensure smooth rotation interpolation.
Available Scripts
MANDATORY: Follow the golden path order. Read each listed script before coding that stage.
Golden path (MANDATORY)
time_trial_manager.gd — microsecond (Time.get_ticks_usec) or physics-frame clock; pass_checkpoint only from physics overlaps / Area signals
- Checkpoint Areas — ordered indices into the manager (physics frame, not
_process)
ghost_recorder.gd — samples {t, p, q} with Quaternion rotation
ghost_replayer.gd — position lerp + Quaternion slerp (never Euler lerp)
time_trial_leaderboard_bridge.gd — integer usec/msec → UI strings
Script index
time_trial_patterns.gd
10 Expert patterns: Microsecond timing, server-authoritative validation, rubber-banding AI, and frame-perfect input flushing.
time_trial_manager.gd
Central clock. Accumulates Time.get_ticks_usec() (or physics-frame counts). Finish checks must come from physics overlaps.
ghost_recorder.gd
Captures transform samples with Quaternion "q" fields for slerp-safe playback.
ghost_replayer.gd
MANDATORY with recorder. Replays samples via position lerp + Quaternion.slerp.
time_trial_playback_buffer.gd
Jitter-buffer for smooth ghost playback during network streaming.
time_trial_leaderboard_bridge.gd
Formatting utility for converting raw time data to human-readable strings.
Expert Time Trial Patterns
1. Delta-Compression for Ghosts
Store a keyframe only when position/rotation changes beyond a threshold. Persist with FileAccess.open_compressed() + ZSTD; prefer binary floats over JSON.
2. The Leaderboard Bridge
Store records as int usec/msec. Format with %02d:%02d.%03d for stable UI (e.g. 01:24.450).
Expert knowledge (on demand)
LLM-ignorance rule: If a general agent would not know it before reading, load the reference — never delete expert deltas.
- time-trial-expert-patterns.md — restored baseline pedagogy (architecture, WHY, implementation depth)
Reference
Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
Official Documentation
- Time —
get_ticks_usec() for microsecond lap clocks when OS.get_ticks_msec() is too coarse for race records.
- Idle and Physics Processing — why finish-line and checkpoint overlap must run in
_physics_process, not visual _process frames that can skip under load.
- Area3D — monitoring, collision masks, and
body_entered for ordered checkpoint gates without scanning every physics body.
- Collision shapes (3D) — scale shape resources (
BoxShape3D.size) instead of non-uniform CollisionShape3D scale so SAT stays valid on gates.
- Using transforms — global position/basis capture for ghost samples and why Euler-only storage needs careful replay conversion.
- Quaternion —
slerp() between keyframes so ghost heading avoids gimbal lock from naive Euler lerp.
- Transform3D —
interpolate_with() for jitter-buffered network ghost playback between ordered frames.
- Saving games —
FileAccess / store_var patterns for persisting ghost runs and best-time dictionaries without float display round-trips.
- High-level multiplayer — server-authoritative RPC validation so clients cannot fake lap/finish crossings.
- MultiplayerPeer —
TRANSFER_MODE_UNRELIABLE for high-frequency racer transforms where TCP-style reliability spikes latency.
- Input —
flush_buffered_events() when frame-perfect boost/stop must see the latest raw input before the physics step.
- Engine —
physics_ticks_per_second / get_physics_frames() for integer frame-count timing bridges into MM:SS.mmm UI.
Related Skills
Prerequisites
- godot-project-foundations — scene tree, autoloads, and resource layout before wiring a
TimeTrialManager and checkpoint Areas into a track scene.
- godot-physics-3d — Area3D/CollisionShape3D layers, RigidBody/CharacterBody vehicles, and physics-frame overlap rules that make checkpoint sequencing trustworthy.
- godot-signal-architecture — typed lap/split/finish signals between gates, manager, HUD, and ghost systems without brittle node-path coupling.
- godot-gdscript-mastery — typed arrays, Packed* buffers,
await physics_frame, and RPC annotations used in timing and authority patterns.
Complements
- godot-input-handling — action maps and buffered boost/steer input that time-trial NEVER rules require to flush before physics.
- godot-save-load-systems — compressed binary ghost files and best-time persistence beyond in-memory sample arrays.
- godot-multiplayer-networking — ENet peers, authority, and unreliable transform sync for live races and streamed ghost frames.
- godot-adapt-single-to-multiplayer — lag compensation, snapshots, and interest patterns when elevating a solo time trial into online racing.
- godot-navigation-pathfinding — NavigationServer3D agent max-speed for rubber-band AI that paces against the player without cheating collision.
- godot-monte-carlo-balancer — simulate rubber-band factors, checkpoint difficulty, and target clear times before shipping trial parameters.
- godot-camera-systems — chase/replay cameras that must track live cars and non-colliding ghost visuals during playback.
Downstream / consumers
- godot-genre-racing — full racing genre stack that consumes checkpoint clocks, ghosts, and leaderboard formatting as core loop primitives.
- godot-game-loop-collection — meta inventory/collection loops that can gate unlocks on validated best times from this skill.
Master
- godot-master — library router and mirrored module entry for cross-skill discovery.
1---2name: godot-game-loop-time-trial3description: Expert patterns for racing mechanics, checkpoint tracking, and ghost recording/playback in Godot 4. Use when building racing games, speed-run platformers, or arcade trials.4---5
6## Architectural Thinking: The "Validation-Chain" Pattern
7
8A Master implementation treats Time Trials as a **State-Validated Sequence**. Recording a time is easy; ensuring the player didn't cheat via shortcuts requires a strictly ordered `CheckpointManager`.
9
10### Core Responsibilities
11- **TimeTrialManager**: The central clock. Validates checkpoint order and handles "Best Lap" logic.
12- **GhostRecorder**: Captures high-frequency transform data. Uses delta-time timestamps for frame-independent playback.
13- **Checkpoint**: Spatial triggers that notify the Manager.
14
15## Expert Code Patterns
16
17### 1. Robust Checkpoint Validation
18Prevent "Shortcut Cheating" by requiring checkpoints to be cleared in numerical order.
19
20Wire Area `body_entered` (physics) → `TimeTrialManager.pass_checkpoint(index)`. The manager owns usec timing — see script.
21
22### 2. Space-Efficient Ghosting
23Sample at a fixed rate (e.g. 10 Hz). Lerp **position**; **slerp Quaternion** rotation (`ghost_recorder.gd` / `ghost_replayer.gd`). Never Euler-lerp ghost heading.
24
25## Master Decision Matrix: Data Storage
26
27| Format | Best For | Implementation |
28| :--- | :--- | :--- |
29| **Dictionary Array** | Prototyping | Simple `[{t: 0.1, p: pos}, ...]` |
30| **Typed Array** | Performance | `PackedVector3Array` for positions. |
31| **JSON/Binary** | Saving | `FileAccess.get_var()` to save ghost files. |
32
33## NEVER Do
34
35- **NEVER use OS.get_ticks_msec() for ultra-precise race timing** — Millisecond resolution is too coarse for high-end racing games. Use `Time.get_ticks_usec()` for microsecond precision.
36- **NEVER rely exclusively on _process() for finish line triggers** — Visual frames can skip during lag. Always evaluate physical overlaps in `_physics_process()` to guarantee detection within the fixed physics step.
37- **NEVER evaluate Area3D overlaps immediately after instantiation** — The physics server requires at least one physics frame to synchronize. `await get_tree().physics_frame` before checking for players.
38- **NEVER scale a CollisionShape3D on a checkpoint non-uniformly** — This breaks the underlying SAT collision math. Always scale the internal shape resource (e.g., `BoxShape3D.size`) instead.
39- **NEVER use TCP (reliable) for syncing positions in multiplayer racing** — Congestion algorithms cause huge spikes. Use `ENetMultiplayerPeer` with `TRANSFER_MODE_UNRELIABLE` for high-frequency position updates.
40- **NEVER trust client-side finish line/lap crossing** — Always validate triggers on the authoritative server using `multiplayer.is_server()` to prevent cheating.
41- **NEVER use standard float equality (==) for record lap times** — Use `is_equal_approx()` to account for precision loss in accumulated time variables.
42- **NEVER hardcode input checks without flushing the buffer** — For frame-perfect boost/stop responses, call `Input.flush_buffered_events()` to ensure the engine has processed the latest raw input.
43- **NEVER allocate new Vector3 arrays inside fast path-following loops** — This triggers the garbage collector. Use `PackedVector3Array` to maintain a contiguous memory block.
44- **NEVER use dynamic string paths ($"../Checkpoint") in tight loops** — Lookups are slow. Use `@onready` to cache node references during initialization.
45- **NEVER record the whole player object for ghosts** — Only record core transforms (position/rotation). Recording the whole object is memory-intensive and unnecessary for visual ghosts.
46- **NEVER give the ghost collision** — It should be a purely visual indicator (e.g., semi-transparent) to avoid disrupting the player's line.
47- **NEVER neglect checkpoint sequencing** — Don't just check if the player hit the finish line. Verify they passed every intermediate checkpoint in the correct order.
48- **NEVER use Area3D without monitoring optimization** — Checkpoints should only look for the `Player` physics layer to minimize the number of physics overlap calculations.
49- **NEVER use standard lerp for ghost rotation** — Use `slerp()` or `Quaternion.slerp()` to avoid gimbal lock and ensure smooth rotation interpolation.
50
51---
52
53## Available Scripts
54
55> **MANDATORY**: Follow the golden path order. Read each listed script before coding that stage.
56
57### Golden path (MANDATORY)
581. `time_trial_manager.gd` — microsecond (`Time.get_ticks_usec`) or physics-frame clock; `pass_checkpoint` only from physics overlaps / Area signals
592. Checkpoint Areas — ordered indices into the manager (physics frame, not `_process`)
603. `ghost_recorder.gd` — samples `{t, p, q}` with **Quaternion** rotation
614. `ghost_replayer.gd` — position `lerp` + Quaternion `slerp` (never Euler lerp)
625. `time_trial_leaderboard_bridge.gd` — integer usec/msec → UI strings
63
64### Script index
65### [time_trial_patterns.gd](scripts/time_trial_patterns.gd)
6610 Expert patterns: Microsecond timing, server-authoritative validation, rubber-banding AI, and frame-perfect input flushing.
67
68### [time_trial_manager.gd](scripts/time_trial_manager.gd)
69Central clock. Accumulates `Time.get_ticks_usec()` (or physics-frame counts). Finish checks must come from physics overlaps.
70
71### [ghost_recorder.gd](scripts/ghost_recorder.gd)
72Captures transform samples with Quaternion `"q"` fields for slerp-safe playback.
73
74### [ghost_replayer.gd](scripts/ghost_replayer.gd)
75**MANDATORY** with recorder. Replays samples via position lerp + `Quaternion.slerp`.
76
77### [time_trial_playback_buffer.gd](scripts/time_trial_playback_buffer.gd)
78Jitter-buffer for smooth ghost playback during network streaming.
79
80### [time_trial_leaderboard_bridge.gd](scripts/time_trial_leaderboard_bridge.gd)
81Formatting utility for converting raw time data to human-readable strings.
82
83---
84
85## Expert Time Trial Patterns
86
87### 1. Delta-Compression for Ghosts
88Store a keyframe only when position/rotation changes beyond a threshold. Persist with `FileAccess.open_compressed()` + ZSTD; prefer binary floats over JSON.
89
90### 2. The Leaderboard Bridge
91Store records as `int` usec/msec. Format with `%02d:%02d.%03d` for stable UI (e.g. `01:24.450`).
92
93## Expert knowledge (on demand)
94
95> **LLM-ignorance rule:** If a general agent would not know it before reading, load the reference — never delete expert deltas.
96
97- [time-trial-expert-patterns.md](references/time-trial-expert-patterns.md) — restored baseline pedagogy (architecture, WHY, implementation depth)
98
99## Reference
100
101> Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
102
103### Official Documentation
104- [Time](https://docs.godotengine.org/en/stable/classes/class_time.html) — `get_ticks_usec()` for microsecond lap clocks when `OS.get_ticks_msec()` is too coarse for race records.
105- [Idle and Physics Processing](https://docs.godotengine.org/en/stable/tutorials/scripting/idle_and_physics_processing.html) — why finish-line and checkpoint overlap must run in `_physics_process`, not visual `_process` frames that can skip under load.
106- [Area3D](https://docs.godotengine.org/en/stable/classes/class_area3d.html) — monitoring, collision masks, and `body_entered` for ordered checkpoint gates without scanning every physics body.
107- [Collision shapes (3D)](https://docs.godotengine.org/en/stable/tutorials/physics/collision_shapes_3d.html) — scale shape resources (`BoxShape3D.size`) instead of non-uniform `CollisionShape3D` scale so SAT stays valid on gates.
108- [Using transforms](https://docs.godotengine.org/en/stable/tutorials/3d/using_transforms.html) — global position/basis capture for ghost samples and why Euler-only storage needs careful replay conversion.
109- [Quaternion](https://docs.godotengine.org/en/stable/classes/class_quaternion.html) — `slerp()` between keyframes so ghost heading avoids gimbal lock from naive Euler lerp.
110- [Transform3D](https://docs.godotengine.org/en/stable/classes/class_transform3d.html) — `interpolate_with()` for jitter-buffered network ghost playback between ordered frames.
111- [Saving games](https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html) — `FileAccess` / `store_var` patterns for persisting ghost runs and best-time dictionaries without float display round-trips.
112- [High-level multiplayer](https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html) — server-authoritative RPC validation so clients cannot fake lap/finish crossings.
113- [MultiplayerPeer](https://docs.godotengine.org/en/stable/classes/class_multiplayerpeer.html) — `TRANSFER_MODE_UNRELIABLE` for high-frequency racer transforms where TCP-style reliability spikes latency.
114- [Input](https://docs.godotengine.org/en/stable/classes/class_input.html) — `flush_buffered_events()` when frame-perfect boost/stop must see the latest raw input before the physics step.
115- [Engine](https://docs.godotengine.org/en/stable/classes/class_engine.html) — `physics_ticks_per_second` / `get_physics_frames()` for integer frame-count timing bridges into MM:SS.mmm UI.
116
117### Related Skills
118
119#### Prerequisites
120- [godot-project-foundations](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md) — scene tree, autoloads, and resource layout before wiring a `TimeTrialManager` and checkpoint Areas into a track scene.
121- [godot-physics-3d](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-physics-3d/SKILL.md) — Area3D/CollisionShape3D layers, RigidBody/CharacterBody vehicles, and physics-frame overlap rules that make checkpoint sequencing trustworthy.
122- [godot-signal-architecture](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md) — typed lap/split/finish signals between gates, manager, HUD, and ghost systems without brittle node-path coupling.
123- [godot-gdscript-mastery](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md) — typed arrays, Packed* buffers, `await physics_frame`, and RPC annotations used in timing and authority patterns.
124
125#### Complements
126- [godot-input-handling](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-input-handling/SKILL.md) — action maps and buffered boost/steer input that time-trial NEVER rules require to flush before physics.
127- [godot-save-load-systems](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-save-load-systems/SKILL.md) — compressed binary ghost files and best-time persistence beyond in-memory sample arrays.
128- [godot-multiplayer-networking](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-multiplayer-networking/SKILL.md) — ENet peers, authority, and unreliable transform sync for live races and streamed ghost frames.
129- [godot-adapt-single-to-multiplayer](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-adapt-single-to-multiplayer/SKILL.md) — lag compensation, snapshots, and interest patterns when elevating a solo time trial into online racing.
130- [godot-navigation-pathfinding](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-navigation-pathfinding/SKILL.md) — NavigationServer3D agent max-speed for rubber-band AI that paces against the player without cheating collision.
131- [godot-monte-carlo-balancer](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-monte-carlo-balancer/SKILL.md) — simulate rubber-band factors, checkpoint difficulty, and target clear times before shipping trial parameters.
132- [godot-camera-systems](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-camera-systems/SKILL.md) — chase/replay cameras that must track live cars and non-colliding ghost visuals during playback.
133
134#### Downstream / consumers
135- [godot-genre-racing](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-genre-racing/SKILL.md) — full racing genre stack that consumes checkpoint clocks, ghosts, and leaderboard formatting as core loop primitives.
136- [godot-game-loop-collection](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-game-loop-collection/SKILL.md) — meta inventory/collection loops that can gate unlocks on validated best times from this skill.
137
138#### Master
139- [godot-master](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-master/SKILL.md) — library router and mirrored module entry for cross-skill discovery.