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)
- 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.
- 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.
- 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 reads | Comes from | Decides |
|---|---|---|
| Where on the bat the ball hit | Your aim (mouse) | The grade, how hard it comes off, how high it tends to go, how much it curves |
| When the bat met the ball | Your timing (press) | The direction: early = left field, on time = centre, late = right field, way off = foul |
| A / D held | Keys or stick | Turns the hit up to 15 degrees toward left / right field, and which way it curves |
| W / S held | Keys or stick | W (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 middle | Aim height | A 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 | |
|---|---|---|
| Quality | 0.75 x timing + 0.25 x sweet spot | From the contact zone only |
| Timing | Most of the grade | The direction only (plus up to 8% power) |
| Exit velocity | Lerp(72, 120, quality) x Power | The zone’s range x Power x five factors (below) |
| Launch | 22 + 10 x timing + under x 10 + LaunchBonus | A range rolled by odds + under x 6 + LaunchBonus |
| GREEN | timing >= 0.80 and sweet >= 0.70 | Perfect zone and timing >= 0.80 |
| Aim display | Aim ring (a circle, radius = reach) | Aim helper (a bat outline) |
| Held keys | None | A/D and W/S |
| Fair / foul | Launch spray only | Where 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)):
| Zone | Distance | Quality (the grade’s 0..1) | Grade |
|---|---|---|---|
| Perfect | within 0.04 m | 0.9 + 0.1 x closeness | PERFECT |
| Nice | 0.04 to 0.11 m | 0.45 + 0.45 x closeness | GREAT from quality 0.7 (within about 0.071 m, derived), else GOOD |
| Sour | past 0.11 m (worst 0.2 m further) | 0.2 + 0.25 x closeness | always 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.
| What | Value | Where 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.6 | SwingResolver.ReplayTiming |
| Foul by timing | spray beyond 45 degrees: held A/D is ignored | HitTables.FoulLineDeg |
| Aim turn (full A/D) | 15 degrees | HitTables.AimSprayDeg |
| Random spread on direction | +/-3 (perfect), 6 (nice), 10 (sour) degrees | HitTables.SpraySpread |
Exit velocity = zone range x Power x timing x side x pitch x height:
| Factor | Value |
|---|---|
| Zone range (mph, by closeness) | Perfect 113 to 121, Nice 103 to 113, Sour 84 to 100 |
| Timing | Lerp(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 height | W 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.
| Zone | Ranges in degrees (odds), Mid trajectory |
|---|---|
| Perfect | 12-18 (0.10), 18-24 (0.25), 24-30 (0.35), 30-36 (0.20), 36-42 (0.10) |
| Nice | 2-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”):
| Batter | Trajectory | Side | Curve |
|---|---|---|---|
| Peapod | Mid | Mid (ALL FIELDS) | 1 |
| Baked Bean | High | Pull | 1 |
| String Bean | Low | Push (OPPO HITTER) | 1.6 (SUPER CURVE from 1.3) |
| Pinto | Mid | Mid | 1 |
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
| File | What it does |
|---|---|
Assets/_Project/Scripts/Batting/HitTables.cs | Every batting number: zones, exit velocity, launch ranges and odds, aim, curve, gravity |
Assets/_Project/Scripts/Batting/SwingResolver.cs | Classify (zone, timing, GREEN, no luck), FromContact (the full hit), EarnsReplay, HitRadius |
Assets/_Project/Scripts/Batting/SwingResult.cs | ContactZone, Grade, TimingLabel, hitAim, curveAccel, gravityScale, contactOffset |
Assets/_Project/Scripts/Batting/SwingInput.cs | ReadHitAim (A/D, W/S, arrows, stick) |
Assets/_Project/Scripts/Batting/AimBat.cs | The aim helper |
Assets/_Project/Scripts/Data/BatterDefinition.cs | trajectory, hitDirection, curve |
Assets/_Project/Editor/BattingModelTest.cs | Tools > 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
HitTablesheader (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.
Related
- Batting Feedback: how the game tells you how you did
- Swing and Timing: input, the bat, reach and contact detection
- GREEN Swings: the top swing
- Hit Physics: the curve, extra gravity and fair or foul
- Swing Log: real swings logged for tuning these numbers
- Batters, Pitch Types, Stats: batter styles, pitch hit power, Power and Contact