On this page
  1. Related
  2. Games
  3. References
  4. Sources

A racing game with jumps has to decide what a landing at the wrong angle costs. Excitebike 64 compares how far each wheel is from the ground when the first touches down and sorts the landing into five bands: clean, rough on either side of it, then a crash on either side of those1. A rough landing raises the bike’s drag to at least 7.0 for about a second2.

  • GradedExcitebike 64 (2000). The gap between the wheels' distances to the ground at touchdown sorts a landing into clean, rough (more drag for about a second), or a crash.1,2,3

Games

GameYearHowNote
Excitebike 642000Gradedthe gap between the wheels' distances to the ground at touchdown sorts a landing into clean, rough (more drag for about a second), or a crash
Every claim about this mechanic, by game (3)
Excitebike 64 2000In Excitebike 64's US Rev 1 build, bike_handle_landing runs when a wheel touches down. After 41 or more updates in the air (airFrames), it takes the front wheel's groundDist, the signed offset along the bike's up axis from the wheel's point on the bike to the ground plus 0.5, which grows as the ground comes nearer, less the rear wheel's, so the difference is negative when the rear wheel is nearer the ground. Below −2.0 the rider crashes at once (ANIM_CRASH_BACK); from −2.0 to under −0.8 that crash is queued (pendingCrash); from −0.8 to under −0.3 the landing is rough (roughLanding); from −0.3 to under 0.3 it's clean, and the bike's turning is stopped; from 0.3 to under 0.6 it's rough; from 0.6 up a crash is queued (ANIM_CRASH_FRONT). A queued crash becomes a rough landing when the impact, the bike's velocity along the two wheels' ground normals added together, is above −10. Whatever the wheels, a rider still in a trick animation after 11 or more updates in the air crashes. By the atlas's reading the wheel difference measures the bike's pitch against the ground it lands on.Code
In Excitebike 64's US Rev 1 build, a rough landing (roughLanding, commented "frames since a rough landing (more drag)") raises the bike's drag coefficient, the one multiplied by the square of its speed and applied against its velocity, to at least 7.0; otherwise it's 0.21 in the air and, on the ground, 0.21 times the rear wheel's surface drag, softened during a wheelie (wheelieTimer not 0) to (drag − 1) × 0.25 + 1. bike_update_jump adds each update's ticks to the counter, which starts at 1, and clears it at 60, so the extra drag lasts 59 updates, about a second (computed). On a surface whose drag is 1, that's about 33 times the usual coefficient (computed).Code
In Excitebike 64's US Rev 1 build, bike_update_jump sets landingAssist (commented "the bike will land straight: pitched to the ground") on a falling bike more than 30 updates into a jump and not in a wheelie, once its predicted landing (landingTime) is under 0.25 seconds away: always for a CPU rider, and for a human when the pitch part of the misalignment between the bike's up axis and the landing point's ground normal, the dot product of their cross product with the bike's right axis, is smaller in size than 10 times the rider's landing stat. While it's set, bike_align_to_ground sets the bike's turning to a pitch toward the ground's slope at twice the angle over the time left plus 0.1 seconds, at most 2 radians a second; it returns without setting anything when the landing point's ground normal has a y below 0.5 or the angle's sine is 0. Either way, while the flag is set the player's air pitch input isn't read. For unit axes that dot product is at most 1, and the six profiles of floats at the head of sRiderStatsTable set landing from 0.25 to 5, so the human test passes for every one (computed); the code reads the stat nowhere else.Code

References

  1. ^ abIn Excitebike 64's US Rev 1 build, bike_handle_landing runs when a wheel touches down. After 41 or more updates in the air (airFrames), it takes the front wheel's groundDist, the signed offset along the bike's up axis from the wheel's point on the bike to the ground plus 0.5, which grows as the ground comes nearer, less the rear wheel's, so the difference is negative when the rear wheel is nearer the ground. Below −2.0 the rider crashes at once (ANIM_CRASH_BACK); from −2.0 to under −0.8 that crash is queued (pendingCrash); from −0.8 to under −0.3 the landing is rough (roughLanding); from −0.3 to under 0.3 it's clean, and the bike's turning is stopped; from 0.3 to under 0.6 it's rough; from 0.6 up a crash is queued (ANIM_CRASH_FRONT). A queued crash becomes a rough landing when the impact, the bike's velocity along the two wheels' ground normals added together, is above −10. Whatever the wheels, a rider still in a trick animation after 11 or more updates in the air crashes. By the atlas's reading the wheel difference measures the bike's pitch against the ground it lands on.Codechasmlol/excitebike64
    How we know
    1968/* Lands the bike: cleanly, roughly or in a crash, from the wheels' heights and the impact. */
    1969void bike_handle_landing(Rider *arg)
    ⋮
    1976    impactVel = vec3_dot(vel, &rider->rearWheel.normal.x) + vec3_dot(vel, &rider->frontWheel.normal.x);
    1977    if ((u8)rider->anim < ANIM_TRICK_COUNT && rider->airFrames >= 0xB)
    1978        bike_crash_begin(rider, ANIM_CRASH_FRONT);
    ⋮
    1986    if (rider->airFrames < 0x29)
    1987        goto record_impact;
    1988    wheelDiff = rider->frontWheel.groundDist - rider->rearWheel.groundDist;
    ⋮
    1990    if (wheelDiff < -2.0f) {
    1991        bike_crash_begin(rider, ANIM_CRASH_BACK);
    1992        goto soften;
    ⋮
    1994    if (wheelDiff < -0.8f) {
    ⋮
    
    src/game/bikenew.c lines 1968–2017 at 9cdb194 · upstream ↗
    2018    if (rider->landingAssist != 0) {
    2019        f32 zf = 0.0f;
    2020        rider->bike.angVel.z = zf;
    2021        rider->bike.angVel.y = zf;
    2022        rider->bike.angVel.x = zf;
    
    src/game/bikenew.c lines 2018–2024 at 9cdb194 · upstream ↗
    2462        if (rider->airFrames > 0) {
    2463            bike_handle_landing(rider);
    
    src/game/bikenew.c lines 2462–2463 at 9cdb194 · upstream ↗
    471    axisPos[1] = rider->bike.pos.y + rider->bike.fwd.y * fwdOffset;
    ⋮
    476    pointY = axisPos[1] + dist * rider->bike.up.y;
    ⋮
    481    diff = groundY - pointY;
    482    tolerance = 0.002f;
    483    slope = -1.0f;
    ⋮
    489        dist -= slope * diff;
    ⋮
    495        pointY = axisPos[1] + dist * rider->bike.up.y;
    ⋮
    498        diff = groundY - pointY;
    ⋮
    508                slope = -1.0f;
    ⋮
    514    dist += 0.5f;
    ⋮
    533    wheel->groundDist = dist;
    
    src/game/bikenew.c lines 470–533 at 9cdb194 · upstream ↗
    252    /* 0x28 */ f32 groundDist;      /* along the bike's up axis */
    
    include/structs/rider.h line 252 at 9cdb194 · upstream ↗
    47#define ANIM_NONE                   (-1)
    
    include/structs/rider.h line 47 at 9cdb194 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-25.

    Checked by a second AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-25.

    In the claim ledger

  2. ^ abIn Excitebike 64's US Rev 1 build, a rough landing (roughLanding, commented "frames since a rough landing (more drag)") raises the bike's drag coefficient, the one multiplied by the square of its speed and applied against its velocity, to at least 7.0; otherwise it's 0.21 in the air and, on the ground, 0.21 times the rear wheel's surface drag, softened during a wheelie (wheelieTimer not 0) to (drag − 1) × 0.25 + 1. bike_update_jump adds each update's ticks to the counter, which starts at 1, and clears it at 60, so the extra drag lasts 59 updates, about a second (computed). On a surface whose drag is 1, that's about 33 times the usual coefficient (computed).Codechasmlol/excitebike64revised
    How we know
    1546    if (rider->speed != 0.0f) {
    ⋮
    1551        minDrag = 0.0f;
    1552        if (rider->roughLanding != 0) {
    1553            minDrag = 7.0f;
    ⋮
    1555        drag = 0.21000001f;
    1556        if (rider->airFrames <= 0) {
    1557            drag = (rider->wheelieTimer != 0 ? (rider->rearWheel.surface->drag - 1.0f) * 0.25f + 1.0f : rider->rearWheel.surface->drag) * 0.21000001f;
    ⋮
    1559        if (drag < minDrag) {
    1560            drag = minDrag;
    ⋮
    1562        drag = drag * rider->speed * rider->speed;
    
    src/game/bikenew.c lines 1546–1562 at 9cdb194 · upstream ↗
    1581        rate = drag / rider->speed;
    1582        force.x -= rider->bike.vel.x * rate;
    1583        force.y -= rider->bike.vel.y * rate;
    1584        force.z -= rider->bike.vel.z * rate;
    
    src/game/bikenew.c lines 1581–1584 at 9cdb194 · upstream ↗
    2226    if (rider->roughLanding != 0) {
    2227        rider->roughLanding += ((u8 *)&gPhysTicks)[3]; /* its low byte */
    2228        if (rider->roughLanding >= 60) {
    2229            rider->roughLanding = 0;
    
    src/game/bikenew.c lines 2226–2231 at 9cdb194 · upstream ↗
    401    /* 0x5CD */ u8 roughLanding;    /* frames since a rough landing (more drag), 0 none */
    
    include/structs/rider.h line 401 at 9cdb194 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

  3. ^In Excitebike 64's US Rev 1 build, bike_update_jump sets landingAssist (commented "the bike will land straight: pitched to the ground") on a falling bike more than 30 updates into a jump and not in a wheelie, once its predicted landing (landingTime) is under 0.25 seconds away: always for a CPU rider, and for a human when the pitch part of the misalignment between the bike's up axis and the landing point's ground normal, the dot product of their cross product with the bike's right axis, is smaller in size than 10 times the rider's landing stat. While it's set, bike_align_to_ground sets the bike's turning to a pitch toward the ground's slope at twice the angle over the time left plus 0.1 seconds, at most 2 radians a second; it returns without setting anything when the landing point's ground normal has a y below 0.5 or the angle's sine is 0. Either way, while the flag is set the player's air pitch input isn't read. For unit axes that dot product is at most 1, and the six profiles of floats at the head of sRiderStatsTable set landing from 0.25 to 5, so the human test passes for every one (computed); the code reads the stat nowhere else.Codechasmlol/excitebike64revised
    How we know
    2429        if (rider->landingAssist != 0) {
    2430            bike_align_to_ground(rider);
    2431        } else if (rider->landingTime < 0.25f && rider->bike.vel.y < 0.0f && rider->airFrames > FRAMES(30) && rider->wheelieTimer == 0) {
    2432            if (rider->isCpu == 0) {
    ⋮
    2442                height_get_ground_normal(&vec.x);
    2443                axis.x = rider->bike.up.y * vec.z - vec.y * rider->bike.up.z;
    2444                axis.y = rider->bike.up.z * vec.x - vec.z * rider->bike.up.x;
    2445                axis.z = rider->bike.up.x * vec.y - vec.x * rider->bike.up.y;
    2446                tilt = vec3_dot(&axis.x, &rider->bike.right.x);
    2447                limit = rider->landing * 10.0f;
    2448                if (tilt > 0.0f ? tilt < limit : -tilt < limit) {
    2449                    rider->landingAssist = 1;
    ⋮
    
    src/game/bikenew.c lines 2429–2453 at 9cdb194 · upstream ↗
    2088/* In the air: pitches the bike towards the slope of the ground where it will land. */
    ⋮
    2110    if (normal.y < 0.5f) {
    2111        return;
    ⋮
    2116    sinAngle = vec3_dot(&axis.x, &rider->bike.right.x);
    ⋮
    2120    if (sinAngle == 0.0f) {
    2121        return;
    ⋮
    2123    angleWord = math_asini((s32)(sinAngle * 16384.0f));
    2124    angle = (f32)(s16)angleWord * 3.1415927f * 3.0517578e-05f;
    2125    time = rider->landingTime;
    2126    time += 0.1f;
    2127    rate = (angle + angle) / time;
    2128    if (rate > 2.0f) {
    ⋮
    
    src/game/bikenew.c lines 2088–2136 at 9cdb194 · upstream ↗
    1641        if (rider->landingAssist == 0) {
    ⋮
    1647                pitchError = rider->airPitchInput;
    ⋮
    1653            localTorque.x += ((f32)(pitchError / 4) * 3.1415927f * 3.0517578e-05f - rider->localAngVel.x * 0.0625f) * (250.0f * FRAME_RATE);
    
    src/game/bikenew.c lines 1641–1653 at 9cdb194 · upstream ↗
    585/* The unit normal of the ground triangle found last. */
    586void height_get_ground_normal(f32 *out)
    
    src/game/height.c lines 585–586 at 9cdb194 · upstream ↗
    400    /* 0x5CC */ u8 landingAssist;   /* the bike will land straight: pitched to the ground (bike_align_to_ground) */
    
    include/structs/rider.h line 400 at 9cdb194 · upstream ↗
    333void bike_apply_rider_stats(Rider *rider, s32 profile)
    ⋮
    335    if (rider->isCpu == 0) {
    336        rider->bike.mass = 250.0f;
    337        rider->landing = sRiderStatsTable[profile].landing;
    
    src/game/bikenew.c lines 333–337 at 9cdb194 · upstream ↗
    3725u32 sRiderStatsTable[0x50] = {
    
    src/data/game_800C2280.c lines 3725–3740 at 9cdb194 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

Sources

Code