RAMPAGE gameplay implementation
Implemented 14 September 2026. This document describes the code delivered by the gameplay owner; Unity integration, actual driving runs, controller evaluation and performance measurements belong to the root integration pass. No claim of a completed human driving test is made here.
Files and integration
Runtime/Bike/BikeCommand.cs: shared analogue command for human and AI.Runtime/Bike/BikeTuning.cs: optional ScriptableObject tuning asset; a shared runtime default is created when no asset is assigned.Runtime/Bike/BikeController.cs: rigidbody, contact/suspension, engine/brake/steering, resources, bump damage, recovery, visual state and events.Runtime/Combat/BikeCombat.cs: side kicks with one shared ruleset.Runtime/AI/BikeAI.cs: pursuit driving, bend braking, overtaking, traffic avoidance and attack reservations.Runtime/Racing/RaceProgress.cs: ordered geometric gates, laps, finish, safe recovery and off-course detection.Editor/GameplayVerification.cs: explicitly delegated persistent verification entry point,Rampage.Editor.GameplayVerification.Run, including production road geometry checks.Runtime/QA/InputFlowQA.cs: explicitly delegated opt-in virtual Xbox-layout controller flow, driving-input and isolated-profile checks.Runtime/QA/RaceFlowQA.cs: opt-in Cup/results/retry, time-trial and isolated-profile recovery checks using ordered sample traversal, explicitly not a physics race.Runtime/QA/CombatQA.cs: opt-in physical two-bike strike fixtures initiated through virtual Xbox bumper events.Runtime/App/RuntimeQA.cs: root-owned replay harness, temporarily delegated for per-rider telemetry and honest capture/performance flags, then returned to root ownership.
All classes use namespace Rampage. The agreed APIs in INTEGRATION.md are retained. Additional public controller members are BikeTuning Tuning, RaceProgress Progress, bool CombatEnabled = true, bool BrakeAssist = true, Body, Grounded, IsBoosting, IsProtected, CanReceiveHit, SteerAmount, EngineLoad, EngineRpm, and Gear. Events are Impact(float 0..1), Crashed(BikeController), AttackTelegraphed(int -1/+1), and observational SurfaceImpact(Vector3 worldPoint, Vector3 outwardNormal, float severity01).
Root must place the object before Initialize, assign its progress tracker, disable drive during the countdown, and update progress only during the active race while the bike is not crashed. Both input and AI write the same BikeCommand; AI runs at execution order -50 so its command is available before the bike physics tick. Set CombatEnabled=false for time trial. Set player BrakeAssist from the saved setting. The root retains responsibility for pausing, menu/button release latches, finishing race presentation, audio/haptics and saving.
The shared start/finish line is distance 40m. RaceProgress.StartLineDistance defaults to 40m. The root grid at 35/30/25/20m, two lanes per row, is entirely behind that line. A first forward crossing starts the first lap without awarding one. All subsequent gates and a return to the same line complete a lap. Rear grid positions therefore have the same finish line and travel slightly farther. The world owner confirmed its checker and gantry are at 40m.
Physics choice and tuning
The implementation selects two custom sphere-contact suspension probes, one at each axle, and one capsule/Rigidbody collision body. The initial choice favored explicit, tunable support forces and grip, with radius-0.16m probes providing a finite contact footprint. A subsequent actual bounded WheelCollider-versus-sphere contact/suspension experiment completed 20 isolated editor trials and 77/77 fixture checks. Both corrected fixtures traversed the tested obstacles; flat-ground and side-impulse behavior closely matched. The experiment does not establish overall superiority: obstacle results include unequal observed chassis tilt despite identical configured rotation locks, and no complete WheelCollider steering/rider/slalom prototype was built. The integrated sphere controller remains the production choice; methods, corrected fixture failures, paired measurements and limitations are documented in CONTACT-COMPARISON.md. These separate experiment checks are not additional player-functionality acceptance tests.
The Rigidbody provides unrestricted 3D translation, gravity and collision response. There is no lateral/longitudinal position clamp, spline attachment, velocity direction replacement or race-position catch-up. Steering controls heading and road-normal alignment; locked physical rotation is the explicit arcade balance aid that prevents a side contact spinning or flipping the capsule. The visual rig supplies lean about the tyre contact line. Positive LeanDegrees means a right lean, matching BikeVisual's negative Z roll.
Road probes use layer 8, the collision body uses layer 9, and environment collision uses normal physics. Both axles cast from their mounts toward the road; spring force responds to clearance and point velocity, with bounded force. Ground normal is smoothed. Tyre side grip is acceleration-limited and acts on the actual sideways velocity. Airborne motion retains gravity and momentum, with only gradual landing attitude control. An optional secondary contact on walkable layer-0 shoulder/terrain allows steering back from a road-edge mistake; it has reduced grip/drive, slows high speed progressively and cannot boost or refill boost. Race validation still requires the road corridor. This avoids treating a small shoulder excursion as an uncontrollable airborne state.
FrontSuspensionCompression and RearSuspensionCompression expose the actual accepted sphere-contact clearance as signed metres: RideHeight - (hit.distance + ContactRadius - 0.2m), clamped to suspension travel. Positive means compression; no contact reports full extension (-SuspensionTravel). FrontWheelContact and RearWheelContact distinguish contacts from extension. Before SetPose, the controller passes these measurements to BikeVisual.SetSuspension, offset by 0.65m - RideHeight to retain the rig's physical wheel clearance with custom tuning. LateralSlipMps is the measured signed lateral velocity in the current road frame, and AppliedBrake01 is the final brake value after assistance; both are observational inputs for tire feedback. Reset clears the contact flags and measurements until the next probe. This instrumentation does not change spring, grip or braking forces.
Default controls/tuning:
| Setting | Initial value |
|---|---|
| Mass | 235kg |
| Body reference / nominal tyre contact | road +0.65m |
| Axles | Z -0.75m / +0.75m |
| Suspension travel | 0.38m |
| Spring / damping acceleration coefficients | 105 / 17 |
| Engine acceleration | 16m/s², reducing with speed |
| Normal / boost speed limiter | 210 / 222km/h |
| Maximum manual braking | 27m/s² |
| Corner acceleration authority | 27m/s² |
| Steering response | 0.13s |
| Boost drain / clean recovery | 0.28 / 0.095 per second |
| Stability recovery | 0.12 per second, after 2s without a hit |
| Recovery protection | 2.5s |
Analogue throttle and brake retain full modulation. Opposing throttle/brake prioritizes braking. Boost requires grounded forward motion, throttle and little brake. Exhausting it while holding the button requires release before another activation. Clean close passes grant a small recharge with a cooldown. Display gear/RPM are automatic presentation values, not manually shifted transmission physics.
Optional player brake assistance samples bends across 110m, eases excess throttle and adds at most 48% brake when entry speed substantially exceeds a conservative curve speed. It disengages when facing away from the course or outside the road corridor. It never edits steering or position.
Combat and recovery
A kick has 0.22s windup, a 0.14s active interval and 0.52s recovery. Held buttons do not repeat. The public phase is normalized across the entire 0.88s action; the extension/hit interval is phase approximately 0.25–0.41. One target can be processed only once per action. Local checks require the chosen side, lateral reach 0.35–1.45m, fore/aft separation at most 0.95m, vertical separation at most 1.05m and compatible headings. Reach was tightened after the visual owner confirmed the animated foot reaches approximately 1m from the bike centre; target body width and a small allowance complete the contact. A solid-world linecast blocks attacks through scenery.
A successful kick uses a bounded 3.6m/s sideways velocity impulse and 0.36 stability damage. The victim receives 0.96s hit grace; steering remains available. AI uses exactly the same attack component, dimensions, timings, damage and protection. Bumps use real collision impulse and relative normal speed, apply smaller damage, and have a separate contact cooldown. Glancing contacts do not become high-speed frontal crashes merely because total bike speed is high.
SurfaceImpact supplies visual feedback from actual layer-0/8 collision contacts, independently of damage grace. A new hard contact requires at least 3m/s normal speed. Persistent scraping requires a side-facing surface (absolute normal/up dot below 0.55) and at least 8m/s tangential speed; ordinary upright floor contact does not qualify. The strongest qualifying contact emits once per 0.10s with finite world position, normalized outward normal and severity 0.15–1.0. Bike contacts are excluded. The event adds no forces, damage or synthetic contact positions; the separate visual effect owns particle emission.
Zero stability or a severe physical obstacle/landing impact triggers a short crash state. Being far outside the riding area, airborne for too long or below the road instead requests a separate neutral recovery; a progress rejection cannot trigger a crash. Recovery chooses the last valid progress checkpoint, searches only backward for a road-confirmed empty placement, clears linear/angular velocity and resumes with protection. Protected, crashed and recovering bikes ignore other bike colliders temporarily while retaining world collision. Protection cannot initiate a kick. Calling public ResetToTrack during a race always uses the attached progress tracker's safe distance; caller-supplied forward positions cannot skip race gates.
AI and race validation
AI uses a 158m bend horizon and computes reachable entry speed using its braking allowance. A pursuit target 10–50m ahead follows a chosen lateral offset; the steering command drives the same physical controller as the player. Traffic relative speed limits unsafe following. Scored local lane choices and front obstacle casts provide overtaking/avoidance. Difficulty changes cruise/bend speeds, comfortable braking and attack opportunities. It never changes the shared hitbox or force rules and never applies a rank-dependent speed bonus. A physically stuck bike uses the same safe recovery after 6.5s; this is not a race catch-up teleport.
At most two AI attacks are reserved concurrently. Reservations include the windup so several AI cannot all request attacks before their combat components tick. Lane space, nearby curvature, target protection, compatible heading, predicted relative position and the shared recovery interval constrain opportunities.
After race telemetry showed no opportunistic attacks, deliberate local melee approaches were added. At most two riders select a nearby, similarly paced opponent on a gentle bend, then validate a lane 1.15m beside that opponent. A 1.85m road-edge margin, predicted third-rider clearance and a ban on reciprocal approaches prevent converging groups. Normal steering closes the lateral gap; bounded throttle/brake commands close a small longitudinal gap and match speed. The existing bend-speed and engine limits still apply, and boost is disabled during an approach. The bike holds its line through the shared attack interval, then waits 5–8s before another approach. No transform, velocity or extra force is injected. The full Desert replay below confirms natural attacks and race completion with this addition.
Race checkpoints are spaced at most 45m apart, with at least 24 per lap. Progress requires sequential gate-plane crossings inside the road corridor plus continuous plausible physical movement between observations. The observation origin advances even when progress is rejected. A short excursion can rejoin before the next required gate; a skipped gate remains required until crossed legally. Starting near the finish, reversing over it repeatedly, jumping the first Update to another track section and resetting cannot grant lap credit. NotifyRespawn keeps completed gates and laps while moving current ranking backward to the chosen safe position. SafeDistance is 3m behind the most recently validated gate, or the initial grid location before the first gate.
M0 repair and stage progress — 15 September 2026
BikeController now exposes separate IsRecovering, LastRecoveryReason, RecoveryStarted and RequestRecovery contracts. Actual crashes carry BikeCrashReason. Environmental damage uses both normal contact speed and collision impulse per mass: ordinary support contact below 8m/s effective impact is harmless; hard landings and obstacles remain damaging. No progress flag enters the recovery timer.
The two suspension queries select the nearest valid support in the bike's own PhysicsScene. Spring damping and tire slip use velocity relative to a contacted rigidbody. A crest/mesh join can retain support only within an additional 0.10m for 0.10s, and never while moving away from the surface faster than 2m/s. Real wheel-contact flags remain separate. The asphalt/shoulder grip transition takes roughly 0.14s; bounded lateral force supplies the commanded centripetal acceleration as well as correcting slip. No track-position correction or airborne steering force is added.
Point-to-point progress clamps distances, requires the ordered final gate, and completes one stage regardless of the lap-count argument. CaptureCheckpoint() / RestoreCheckpoint(snapshot) validate stage ID, content revision, route lengths and gate bounds. Restore derives the safe location from the accepted ordinal and ignores the saved arbitrary distance. The application must still select a valid saved stage/checkpoint and exclude resumed best times.
The player's incident recorder keeps five seconds before and two seconds after progress rejection, damage, crash or recovery, including commands, body motion, support colliders/normal/velocity, compression, slip, stability and gate state. Exports occur only on incidents, are capped at 24 per bike instance, and go to persistentDataPath/Diagnostics/Incidents unless QA overrides the directory. OnDisable saves an incomplete post-window explicitly as such.
Actual managed checks: 37/37 passed in m0-rules-verification.json, using unchanged production progress/force helper source with an analytic track fixture. Reproduce with Assets/Rampage/Tests~/run_m0_verification.ps1. This is not native physics. All authored Runtime and Editor source also compiled offline against Unity 6000.3.24f1 without warnings/errors.
Native entry point Rampage.Editor.M0PhysicsVerification.Run invokes the production controller force step in isolated preview PhysicsScenes. It performs no corrections during driving and writes Docs/QA/m0-physics-verification.json. The first actual run (preserved report) passed 23 checks and failed one: the ramp fixture abruptly ended a 20% grade at 162km/h, causing a physically consistent 1.47s flight. The bike entered the plateau at roughly +7.45m/s vertical speed, rose to 3.72m clearance and landed without crash/recovery. The corrected continuity fixture uses smooth entrance/exit easements, with unchanged acceptance thresholds; the original abrupt plateau remains as an eighth explicit launch-and-landing fixture. The actual rerun passed 27/27 checks with exact scene/global-setting restoration. The smoothed ramp retained 100% support across 293.23m; the preserved hard launch still produced 1.47s flight and a safe landing. All eight fixtures recorded zero crashes, recoveries and resets. The production controller was unchanged between these runs.
In that first native run, flat, crest, curb, side-impulse and curved-road fixtures had 100% support. A 3.6m/s sideways impulse left 0.043m/s slip after 0.5s and maximum 0.38m lateral deviation; the curve trial advanced 220.93m with 3.38m maximum deviation under simple lookahead commands. A deliberate vertical impulse produced 0.78s flight and a safe landing. These isolated results do not replace the requested ten production-route repetitions, human drive-feel assessment, real opponent interactions or render/input-gap tests.
Verification performed
All six source files compiled successfully with Unity's bundled Roslyn against the installed Unity 6000.0.45f1 Core/Physics assemblies, using small stubs only for the independently owned world/visual contracts. Authoritative Unity 6.3 compilation and actual runtime verification results are recorded below.
The later measured-suspension, slip/brake observations and surface-impact event addition also compiled successfully against the installed Unity 6000.3.24f1 assemblies, with matching world/visual contract stubs. That standalone compile does not establish the new visual/audio/effect behavior in the player; the 207 recorded runtime checks below precede this observational addition. Existing movement, spring, grip, damage, AI and race-validation logic was retained.
Standalone C# harnesses compiled and executed the actual RaceProgress.cs and BikeCombat.cs source, with minimal math/world mocks (no Unity physics simulation):
- Ten race tests passed: two ordered laps, backward laps rejected, teleport rejected, off-road shortcut rejected, safe recovery followed by a legal finish, repeated start-line oscillation rejected, first-update teleport rejected, rear grid common finish, ranking rewind after recovery, backward placement search followed by a legal finish.
- Eight combat tests passed: telegraph before damage, one hit on the correct side, no held-button repeat through recovery, mirrored left hit, scenery blocking, protected victim, protected attacker, and disabling combat during an action.
Harness sources and compiled outputs are retained at C:/Users/danie/AppData/Local/Temp/RampageGameplayVerification for the root integrator. They are not included in the player build. These tests validate rules; they do not establish vehicle feel, visual quality, actual collider contact, AI lap completion or frame rate. A separate temporary numerical 2D pursuit sanity probe followed the three production spline shapes within road width; this was not a Unity/PhysX acceptance run.
The persistent Editor entry point builds all three actual TrackRuntime courses with scenery disabled and runs 14 cases per track. Its additional checks cover normalized/perpendicular sample frames, projection accuracy, collider agreement at three lateral positions along every course, a legal full-width riding line, corridor rejection and the first physical sample crossing the start line. It writes Docs/QA/gameplay-verification.json and throws if any case fails.
The final recorded Unity 6000.3.24f1 geometry/rules execution in Docs/QA/gameplay-verification.json passed 42/42 checks with zero failures. The first run found two real sample/collider mismatches around 0.31m. The diagnosis was a full-width twisted road quad being represented by only two planar triangles while samples interpolate a banked cross-section. The world owner subdivided the road width into eight strips and corrected banking to use horizontal curvature; the original 0.18m tolerance was retained. Worst distance, lane and collider names are recorded for future regressions.
Root's first valid DX12 Windows physics/rendering run is recorded in Docs/QA/20260914-182510-track0/report.json: Blackpine without scenery, eight riders, 35.52s, 1,524.9m player progress, 208.1km/h peak, grounded at completion, zero player crashes and zero logged errors. It measured 59.78 average FPS, P99 17.02ms and 46.48 FPS for the slowest 1%. Screenshot capture was inside that sample period. This is a partial-course driving proof, not a complete lap, full-scenery performance pass or a human controller acceptance test. Full replay results are owned and recorded by root.
Root's subsequent full-scenery Blackpine run, Docs/QA/20260914-183229-track0/report.json, completed two physical laps (6,800m) in 135.12 race seconds with eight riders, zero rider crashes and zero logged errors. The 1920×1080 DX12 High-quality run without screenshot capture recorded 59.97 average FPS, 16.94ms P99 and 58.33 FPS for the slowest 1%. This report predates explicit camera-render coverage instrumentation; subsequent reports distinguish camera renders from player-loop timing. A paused Desert run was invalid and is not evidence of a completed race or performance acceptance.
The completed full-scenery Desert run with deliberate AI melee, Docs/QA/20260914-185540-track1/report.json, finished two physical laps (7,900m) in 154.34s. It recorded seven natural attacks and seven accepted kicks, zero crashes across all eight riders and zero logged errors. At 1920×1080 DX12 High without captures, all 8,777 measured frames received the main-camera render callback (100% coverage): 59.98 average FPS, 16.82ms P99, 59.03 FPS slowest 1%. Three riders had finished when the player's result appeared; the others were 25–87m short of the finish, with no off-course or wrong-way flags. The player received three kicks and recovered stability by race end. A final stability of 1.0 alone therefore cannot determine whether combat occurred.
The completed full-scenery City run, Docs/QA/20260914-185832-track2/report.json, finished two physical laps (6,200m) in 133.26s, with one natural attack and one accepted kick against the player. All eight riders had zero crashes, off-course or wrong-way flags; there were zero logged errors. At 1920×1080 DX12 High without captures, all 7,513 measured frames rendered (100% coverage): 59.98 average FPS, 16.80ms P99 and 59.20 FPS slowest 1%. The remaining riders were 36–188m short of the finish when results appeared.
Opt-in runtime QA
Run virtual controller checks with -rampageInputQA -rampageTest input -rampageNoScenery -rampageInputReport <directory>. The harness requires the isolated-profile flag, queues real Input System GamepadState events, and does not invoke menu callbacks to bypass navigation. It checks focus restoration, A/B/menu and track navigation, held-A launch/resume latches, analogue RT/LT with actual speed changes, steering, attack sides, camera/look, boost consumption, frozen pause, input-map separation and device removal/reconnection. It saves/reloads only qa-profile.json, restores its original contents and checks that production rider.json is unchanged. Results go to input-flow-report.json. This validates virtual Xbox-layout inputs, not a physical Xbox connection or haptic hardware. Root owns actual execution.
The final post-fix Unity execution in Docs/QA/20260914-190926-input/input-flow-report.json passed 36/36 virtual controller checks with zero failures, zero logged errors and the production profile unchanged.
Run application-flow checks with -rampageFlowQA -rampageTest flow -rampageNoScenery -rampageFlowReport <directory>. This deliberately traverses ordered gates through RaceProgress.Update while bodies are frozen; it verifies application result transitions, Cup point awards and retry rollback, same-frame repeated Retry/NextRace calls, final Cup win accounting, time-trial solo/combat state and saved best time. It also corrupts and recovers only the isolated QA profile, then restores the original QA main/backup/temp file bytes. It writes race-flow-report.json with physicsDrivingTest=false. The initial run passed 90/90. The final expanded actual run, Docs/QA/20260914-190137-flow/race-flow-report.json, passed 112/112, including saved Cup place, eight scores and UTC timestamp through save/reload, retries, Home and time trial. Zero logged errors, exact QA file restoration, and production profile unchanged.
Run physical strike checks with -rampageCombatQA -rampageTest combat -rampageNoScenery -rampageCombatReport <directory>, optionally -rampageNoCapture, or use Tools/combat-test.ps1 (180s bound). Two genuine rigidbodies are positioned only between independent fixtures and then accelerate under matching analogue commands. Virtual RB/LB presses must produce one accepted moving hit, visible stability damage and continued physical motion; a fresh respawn must prevent damage. No target placement occurs after a strike begins. The harness writes combat-report.json with physicsCombatTest=true and physicsLapTest=false, restores exact isolated-profile files and verifies production profile integrity. The final post-fix Unity execution Docs/QA/20260914-191527-combat/combat-report.json passed 17/17: three telegraphs, one moving hit from each side at 24.3km/h, stability 1.00→0.64, and the protected strike blocked. Zero failures, zero logged errors, exact QA file restoration and production profile unchanged. Run profile-writing harnesses serially.
The replay harness accepts -rampageNoCapture to suppress every screenshot, HUD-toggle capture and associated wait. Use -rampageSeconds 180 -rampageNoCapture -rampageRequireFinish for full-race acceptance runs. -rampageRequireFinish makes an unfinished timed run fail. Reports explicitly state quality, scenery, capture, graphics API, progress validity, performance sample validity, timeout/stall state and requested/race duration. Zero logged errors is insufficient when there is no meaningful progress; 20s without new validated player progress while racing aborts as stalled. Per-rider JSONL snapshots are written at the start, every 10s and at the end, including gates/laps, physical versus validated distance, safe position, ground/contact state, lateral offset, finish status and all rider crash counts. This makes stranded AI and repeated recovery cycles inspectable without changing gameplay.
Replay input maps are disabled once automated driving owns the player. Any unexpected pause invalidates the continuous run and is reported without silently resuming. Measured frame intervals are counted separately from distinct frames receiving RenderPipelineManager.endCameraRendering for the actual main camera. At least 98% render coverage is required for a valid performance sample and successful replay exit; these are CPU frame intervals, not GPU timer queries. Telegraph, general impact and accepted-kick counters are separate, so bumps cannot be mistaken for successful attacks. The controller's SuccessfulKicks and KicksReceived are cumulative observational counters and do not alter combat behavior.
The initial opportunistic AI's formation lanes were 3.4m apart and its passing score discouraged separation below 1.8m, while predicted kick selection required at most 1.33m lateral and 0.80m fore/aft separation. Root measured zero attacks in the initial race runs, motivating the deliberate approach described above. Per-rider telemetry now includes meleeApproaching and meleeTarget, so subsequent results can distinguish no approach, a missed opportunity and a landed strike.
The final bounded application review found two player-facing issues, both corrected by root: human attack commands now pass the held bumper state to the combat component's own rising-edge latch, preventing a render-only frame from dropping a one-frame attack pulse; the HUD now displays the progress tracker's wrong-way and off-course conditions. Cup rollback, Home track selection, pause/settings transitions, device removal handling, camera and recorded-engine wiring produced no further actionable findings in that read-only pass. The final controller and physical-combat reruns after these integration changes passed 36/36 and 17/17 respectively, as recorded above.