Batting Explained

In short

You do three things: put the aim helper (the gold bat outline) on the ball with the mouse, press swing at the right moment, and optionally hold A/D and W/S as you hit. Where on the bat the ball meets it decides how good the hit is (WEAK / GOOD / GREAT / PERFECT). When you swing decides where it goes: early pulls it to left field, late pushes it to right, way off is foul. A GREEN is a sweet-spot hit that is also on time. How the game tells you all this: Batting Feedback.

How it works

For players (no code)

  1. Aim. The mouse moves the aim helper: two gold curves at the ends of a bat’s barrel. The middle between them is the bat’s sweet spot. Put it on the ball as it arrives.
  2. Swing. Space, left click or gamepad A. The bat takes 0.2 s to swing through. The game uses the exact moment you pressed, not the frame.
  3. Hold a direction (optional). At the moment of contact the game reads A/D (or left/right arrows, or the gamepad stick) and W/S (or up/down arrows). The HUD box bottom-left shows what you are holding.

What the game reads from that one swing:

What it readsComes fromDecides
Where on the bat the ball hitYour aim (mouse)The grade, how hard it comes off, how high it tends to go, how much it curves
When the bat met the ballYour timing (press)The direction: early = left field, on time = centre, late = right field, way off = foul
A / D heldKeys or stickTurns the hit up to 15 degrees toward left / right field, and which way it curves
W / S heldKeys or stickW (LIFT): more chance of a high launch, 6% less power. S (DRIVE): more chance of a low launch, 5% more power
Ball above or below the bat’s middleAim heightA small launch nudge, up to 6 degrees

A good swing = sweet spot on the ball (PERFECT or GREAT) and on time (up the middle). A bad swing = the ball on the bat’s handle or tip (WEAK: a grounder or a pop-up), or so early or late it goes foul, or no contact at all. A miss that says SWUNG ON TIME means your timing was fine but the aim helper wasn’t on the ball.

flowchart TD
    P[Pitch thrown] --> A[Mouse: aim helper on the ball]
    A --> S[Press swing]
    S --> C{Bat reaches the ball?}
    C -- no --> W[Whiff: a strike]
    C -- yes --> Z[Where on the bat: sour / nice / perfect]
    C -- yes --> T[When: early pull / on time / late push / foul]
    C -- yes --> H[Held A/D and W/S]
    Z --> G[Grade, exit velocity, launch odds, curve]
    T --> D[Direction]
    H --> D
    H --> G
    G --> F[Ball flight]
    D --> F
    F --> R{Result}
    R --> HR[HOME RUN!]
    R --> FO[FOUL BALL]
    R --> O[Out]

”After Mario Superstar Baseball” in this code

The code comments name Mario Superstar Baseball as the model. In practice that means, and only this:

  • Contact zones along the bat (sour / nice / perfect) set the quality, not timing.
  • Timing sets the direction (early pulls, late pushes).
  • The stick held at contact aims the hit (A/D) and moves the launch odds (W/S).
  • Launch angle is rolled from five ranges per zone, each with odds. It is not a formula of timing.
  • Batter styles: a trajectory (Low / Mid / High), a side they hit hardest to (Pull / Mid / Push, “Mario’s table”) and a curve strength.
  • Each pitch type has its own hit power per zone.
  • The ball curves in flight (hook or slice), and sour contact falls sooner (extra gravity).

The old model, for comparison

Old (before PR #46)Now
Quality0.75 x timing + 0.25 x sweet spotFrom the contact zone only
TimingMost of the gradeThe direction only (plus up to 8% power)
Exit velocityLerp(72, 120, quality) x PowerThe zone’s range x Power x five factors (below)
Launch22 + 10 x timing + under x 10 + LaunchBonusA range rolled by odds + under x 6 + LaunchBonus
GREENtiming >= 0.80 and sweet >= 0.70Perfect zone and timing >= 0.80
Aim displayAim ring (a circle, radius = reach)Aim helper (a bat outline)
Held keysNoneA/D and W/S
Fair / foulLaunch spray onlyWhere the ball actually goes (it can hook foul)

Numbers

All in Batting/HitTables unless noted. Contact stat = 1 unless noted.

Contact zones (distance along the bat from the sweet spot, 0.6 m from the knob, either way; both scale with max(0.2, Contact)):

ZoneDistanceQuality (the grade’s 0..1)Grade
Perfectwithin 0.04 m0.9 + 0.1 x closenessPERFECT
Nice0.04 to 0.11 m0.45 + 0.45 x closenessGREAT from quality 0.7 (within about 0.071 m, derived), else GOOD
Sourpast 0.11 m (worst 0.2 m further)0.2 + 0.25 x closenessalways WEAK

closeness is 0..1 toward the next better zone (perfect: toward the dead centre). Grades: PERFECT >= 0.9, GREAT >= 0.7, GOOD >= 0.45, WEAK below (SwingResult.Grade).

Timing (SwingResolver.Classify): spray = the bat face’s direction at contact, clamped to +/-80 degrees. timing = 1 - |spray| / 45, clamped 0..1. Negative spray = left field = early.

WhatValueWhere to tune
GREEN timing>= 0.80 (spray within 9 degrees); comment: about 14 ms on a middle fastball (0.85 = 10 ms, 0.75 = 19 ms)SwingResolver.GreenTiming
Replay timing (perfect zone, not green)>= 0.6SwingResolver.ReplayTiming
Foul by timingspray beyond 45 degrees: held A/D is ignoredHitTables.FoulLineDeg
Aim turn (full A/D)15 degreesHitTables.AimSprayDeg
Random spread on direction+/-3 (perfect), 6 (nice), 10 (sour) degreesHitTables.SpraySpread

Exit velocity = zone range x Power x timing x side x pitch x height:

FactorValue
Zone range (mph, by closeness)Perfect 113 to 121, Nice 103 to 113, Sour 84 to 100
TimingLerp(0.92, 1, timing)
Batter side (Pull batter, from left line to right line at -45, -22.5, 0, 22.5, 45 degrees)1.02, 1.05, 1, 0.95, 0.85 (Push batter: mirrored; Mid: 1)
Pitch (perfect / nice / sour)fastball 1.05 / 1 / 0.95, sinker 1 / 0.97 / 0.85, changeup 0.98 / 0.95 / 0.8, curveball 1 / 0.97 / 0.9, slider 1.02 / 0.98 / 0.9, knuckleball 1 / 0.95 / 0.8 (DefaultContent)
Held heightW x0.94 (LiftPower), S x1.05 (DrivePower)

Launch angle: pick a range by its odds, then an angle evenly inside it; add under x 6 and LaunchBonus; clamp -15 to 75 degrees.

ZoneRanges in degrees (odds), Mid trajectory
Perfect12-18 (0.10), 18-24 (0.25), 24-30 (0.35), 30-36 (0.20), 36-42 (0.10)
Nice2-10 (0.15), 10-18 (0.25), 18-26 (0.30), 26-34 (0.20), 34-44 (0.10)
Sour-12-0 (0.30), 0-8 (0.20), 8-18 (0.15), 45-58 (0.20), 58-70 (0.15): grounders or pop-ups

High trajectory shifts every range +4, Low -4. W gives the lowest range’s odds to the highest; S gives the two highest ranges’ odds to the lowest. Keys are all or nothing; a stick has a 0.3 deadzone and is full at 0.8 tilt.

Curve and gravity in flight: sideways pull 2.4 m/s^2 x zone (1 / 0.6 / 0.3) x lift fade x side x batter curve. The lift fade goes from full at 33.75 degrees launch to none at 78.75. Side = the held A/D direction at full strength (beyond 0.3), else it bends away from a neutral line 8.4 degrees to the pull side. A quarter of the curve also pulls the ball back toward the plate (HitPhysics). Gravity x1 / x1.05 / x1.15 by zone until first landing.

Batter styles (DefaultContent; the title screen shows them, e.g. “HIGH TRAJECTORY - PULL HITTER”):

BatterTrajectorySideCurve
PeapodMidMid (ALL FIELDS)1
Baked BeanHighPull1
String BeanLowPush (OPPO HITTER)1.6 (SUPER CURVE from 1.3)
PintoMidMid1

Reach (unchanged): HitRadius = (0.037 + 0.033 + 0.12 x Contact) x sqrt(max(0.1, TimingWindow / 0.11)), 0.19 m at base stats. See Swing and Timing.

Balance sim (comment in HitTables): a good player (20 ms, 4 cm error) homers on about 67% of contacts left alone, about 76% holding W; a pro (12 ms, 2.5 cm) about 84% / 92%; a casual (35 ms, 7 cm) about 38% / 44%.

In the code

FileWhat it does
Assets/_Project/Scripts/Batting/HitTables.csEvery batting number: zones, exit velocity, launch ranges and odds, aim, curve, gravity
Assets/_Project/Scripts/Batting/SwingResolver.csClassify (zone, timing, GREEN, no luck), FromContact (the full hit), EarnsReplay, HitRadius
Assets/_Project/Scripts/Batting/SwingResult.csContactZone, Grade, TimingLabel, hitAim, curveAccel, gravityScale, contactOffset
Assets/_Project/Scripts/Batting/SwingInput.csReadHitAim (A/D, W/S, arrows, stick)
Assets/_Project/Scripts/Batting/AimBat.csThe aim helper
Assets/_Project/Scripts/Data/BatterDefinition.cstrajectory, hitDirection, curve
Assets/_Project/Editor/BattingModelTest.csTools > Home Run Derby > Run Batting Model Test

Gotchas

  • Timing no longer changes the grade. A perfectly timed swing off the handle is still WEAK.
  • The sour zone covers both ends: off the hands and off the tip.
  • A/D only aims hits whose timing kept them fair (spray within 45 degrees). For a way-early or way-late swing, A/D is ignored (direction and curve), so you can’t aim it back fair; only the random spread or the natural curve could.
  • A ball can start fair and hook foul in the air; fair or foul is where it lands or crosses the wall. See Hit Physics.
  • Every batter is right-handed, so PULL always means left field and OPPO right field.
  • The balance figures in the HitTables header (about 76% holding W) were written before W got its x0.94 power cost (commit 01c0ecc); the later comment says lifting now pays in short parks and costs in deep ones.
  • Contact makes both the reach and the zones bigger. The aim helper’s height shows the reach only; the zones are not drawn.
  • The Dev menu’s insta-home-run bat and TEST HITS skip all of this. See Dev Menu and Shortcuts.