Apex F1
A top-down racing game where braking is the skill.
Context & The Problem
Many racing games rely on complex controls; this project explores whether a single well-timed input can create satisfying driving skill.
A top-down point-A-to-point-B sprint with tap-to-brake controls, cornering consequences, crashes, and race HUD feedback.
My Contribution & Ownership
Building the racing mechanics, car handling, track presentation, and on-screen race feedback.
Architecture & System Flow
A Flutter mobile application using Flame for game simulation and rendering.
Input-driven speed and cornering behavior centered on when the player presses and releases the brake.
A top-down route with boundaries, off-line movement, and crash consequences.
Compact visual feedback for race state and the player's driving performance.
Turning braking into a racing mechanic
Apex F1 uses a fixed 60Hz physics timestep accumulator to ensure car dynamics remain consistent regardless of render frame rates. Braking decelerates the vehicle to keep lateral tire forces within grip limits; missing braking points overloads tire adhesion, triggering spin-outs and recovery cooldowns.
1234567891011121314151617181920212223242526272829303132333435363738void update(double frameDelta, {bool? isBraking}) {
if (!frameDelta.isFinite || frameDelta < 0) {
throw ArgumentError.value(frameDelta, 'frameDelta', 'Must be finite and non-negative.');
}
if (isBraking != null) {
state.isBraking = isBraking;
}
final acceptedDelta = math.min(frameDelta, maxFrameDelta);
droppedTime += frameDelta - acceptedDelta;
_accumulator += acceptedDelta;
var steps = 0;
while (_accumulator + 1e-12 >= fixedTimeStep && steps < maxStepsPerFrame) {
if (state.isSpinningOut) {
state.recoveryTimer = math.max(0, state.recoveryTimer - fixedTimeStep);
if (state.recoveryTimer == 0) {
state.isSpinningOut = false;
state.isCrashed = false;
state.speed = math.max(drivingConfig.minimumSpeed, corneringConfig.recoverySpeed);
}
_accumulator -= fixedTimeStep;
steps++;
continue;
}
if (state.isBraking) {
state.speed = math.max(
drivingConfig.minimumSpeed,
state.speed - drivingConfig.brakingDeceleration * fixedTimeStep,
);
} else {
state.speed = math.min(drivingConfig.maxSpeed, state.speed + drivingConfig.acceleration * fixedTimeStep);
}
// ...
}
}Engineering Decisions & Trade-offs
One primary control
Traditional racing controls can overwhelm a short-session mobile game.
Make brake timing the main player skill instead of relying on full steering controls.
Sprint instead of lap racing
Repeating circuit laps does not fit the intended quick, forward-moving challenge.
Use an open point-to-point route where crashes have meaningful consequences.
Limitations & What I'd Improve
- In active development; gameplay, balancing, and visual polish may change.
- No public release link is currently listed.
Schoola Learn
A practice-first learning experience for secondary school students.