Pitching
In short
PitcherAI.CreatePitchdecides everything about a pitch the moment the windup starts: type, strike or ball, plate location, speed, wobble, loop direction, special balls and decoys. All rolls come from the round’s seed, so the same seed throws the same pitches. See Seeds and Determinism.PitchData.PositionAt(time)is the one source of the ball’s path. The bat, timing ring, replay and spectators all read it.
How it works
Choosing a pitch (CreatePitch)
- Type. A weighted pick from the pitcher’s
pitcheslist (WeightedPitch.weight). If the park’s or the pitcher’s special balls ride only some pitch types, 80% of picks come from those types (FavouredShare), so you actually see the special balls. The same type is never thrown more than twice in a row, or three times for a favoured type (if the pitcher has another). - Strike or ball. A strike with chance
strikeRate. A strike lands anywhere inside the zone. A ball lands just off one edge (left, right, low or high, picked at random) by 0.08 to 0.25 m. - Speed.
type.speedMph x pitcher.speedMultiplier x random(0.97, 1.03). - Special balls. Two slots, each rolled on the separate ball stream: the pitcher’s own ball (
bossEffect), then the park’s ball (effect). They stack. A Super Fast ball in either slot multiplies the speed by 1.15. See Special Balls. - Flight time.
releasePoint.z / (mph x 0.44704) / speedScale.speedScaleslows real pitches down to something playable with a mouse. - Wobble.
type.wobble x random(-1, 1)(knuckleball flutter). - In zone.
StrikeZone.Contains(location), decided now. - Loop direction. Curveballs and changeups (
arcSideways) loop any way from left (-90) through up (0) to right (+90), rolled on their own stream; other pitches loop up. - Decoys. A pitcher with
decoys(The Phantom) throws that many fake balls with a pitch, with chancedecoyChance x ballChanceScale, on their own stream.
The release point comes from the pitcher figure (human or bean) when there is one, otherwise PitcherAI.DefaultReleasePoint (-0.3, 1.8, 17) m.
The path (PitchData.PositionAt)
Let t = share of the flight time (0 at release, 1 at the plate).
- Before release: the release point. In play, the ball sits in the pitcher’s hand until then.
- During flight:
Lerp(release, plate - break, t) + break x t^2, pluswobble x sin(3 pi t)on x, plus a loop ofarc x 4t(1 - t)bowing towardarcAngle. Break grows with t squared, so the pitch bends late. Wobble and loop are zero at both ends, so the pitch still crosses the plate exactly atplateLocation. - After the plate: keeps going toward the catcher at its final speed.
Breakis(horizontalBreak, verticalBreak, 0)from the pitch type. Negative vertical break is a drop.- Special balls change the path here: Anti-Gravity flips the drop (and the loop) upside down, Sticky / Headwind / Tailwind change the pace late, Wiffle swerves, Ghost fades. See Special Balls.
Decoys
Each decoy ends 0.25 to 0.42 m from the real ball at the plate, spread around it and kept near the zone (x +/-0.55 m, height 0.2 to 1.4 m). It follows the real path until 30% of the flight, then eases onto its own, and vanishes at 97%. Decoys can’t be hit and cast no shadow: the shadow is the tell. See Pitchers and Bosses.
The strike zone
A box on the plate plane (z = 0): x from -0.22 to 0.22 m, height 0.5 to 1.1 m. Contains adds the ball radius (0.037 m) on every side, so a pitch that clips the edge is a strike. The zone frame is drawn as a white outline at the plate.
Hit power
Each pitch type also sets how hard it comes off the bat by contact zone (hitPowerPerfect, hitPowerNice, hitPowerSour, default 1). Values per type: Numbers and Pitch Types.
Numbers
| What | Value | Where to tune |
|---|---|---|
| Same pitch in a row, max | 2 (3 for a favoured special-ball pitch) | PitcherAI.MaxSamePitchInARow, MaxFavouredInARow |
| Favoured share | 0.8 (comment: every pitcher lands about 67 to 72% overall) | PitcherAI.FavouredShare |
| Speed jitter | x0.97 to x1.03 | PitcherAI.CreatePitch |
| Default release point | (-0.3, 1.8, 17) m | PitcherAI.DefaultReleasePoint |
| Pitch speed scale (all runs and the cage) | 0.6 | RunConfig.pitchSpeedScale |
| Difficulty pitch speed | x1 Normal, x1.08 Medium, x1.15 Hard (multiplies the scale) | Difficulties.PitchSpeed |
| Loop height | fastball 0.12, sinker 0.15, slider 0.2, changeup 0.35, knuckleball 0.35, curveball 0.6 m | PitchTypeDefinition.arc (DefaultContent) |
| Decoy distance at the plate | 0.25 to 0.42 m | PitcherAI.DecoyMinOffset, DecoyMaxOffset |
| Decoy split / vanish | 0.3 / 0.97 of the flight | PitchData.DecoySplit, DecoyFade |
| Strike zone | half width 0.22 m, bottom 0.5 m, top 1.1 m | StrikeZone |
| Ball miss distance | 0.08 to 0.25 m off an edge | StrikeZone.RandomOutside |
| Pitcher defaults | speedMultiplier 1, strikeRate 0.75, timeBetweenPitches 1.2 s, decoyChance 1 | PitcherDefinition |
| Pitch type defaults | 90 mph, spin 2200 rpm (visual only), hit power 1 | PitchTypeDefinition |
| Random streams | balls 0x0BA11, decoys 0xDEC0, loop 0xA2C | PitcherAI |
| Batting cage machine | strike rate 0.85, 0.8 s between pitches | BattingCage |
Worked example (default release point, Normal): a 90 mph pitch takes 17 / (90 x 0.44704) / 0.6 = about 0.70 s.
Per-type speeds and breaks: Pitch Types. Each pitcher’s mix, strike rate, speed, own balls and decoys: Pitchers and Bosses.
In the code
| File | What it does |
|---|---|
Assets/_Project/Scripts/Pitching/PitcherAI.cs | CreatePitch, ChoosePitchType, FavouredPitches, Roll, MakeDecoys; dev overrides DevForcedPitch, DevForcedEffect |
Assets/_Project/Scripts/Pitching/PitchData.cs | One pitch: PositionAt, ArrivalTime, Break, OpacityAt, WiffleOffset, Has, Primary, DecoyPositionAt |
Assets/_Project/Scripts/Pitching/StrikeZone.cs | Zone size, Contains, RandomInside, RandomOutside |
Assets/_Project/Scripts/Data/PitchTypeDefinition.cs | Speed, breaks, wobble, arc, arcSideways, spin, hit power, trail colour |
Assets/_Project/Scripts/Data/PitcherDefinition.cs | Pitch mix, strike rate, speed, pace, boss batter modifiers, own balls, decoys, DescribeBalls |
Assets/_Project/Scripts/Batting/DecoyBalls.cs | Draws the decoys |
Gotchas
- The mph shown on the HUD is
speedMph(type, pitcher and Super Fast). It does not includepitchSpeedScaleor the difficulty speed, which only shorten the flight time. - The order of random draws on the main stream matters for determinism: the favoured roll (when there are favoured pitches) and type, strike roll, location, speed jitter, wobble. Adding a draw in the middle changes every later pitch for a given seed. Special balls, decoys and loops have their own streams.
DevForcedEffectreplaces only the park slot; a boss’s own ball can still roll. See Dev Menu and Shortcuts.- The dev overrides still make the normal rolls, so turning them off leaves the rest of the sequence unchanged.
PitcherAIhas no game-state smarts yet (no pitching around the count). Its class comment says that behaviour belongs here.- The trail is tinted by
PitchTypeDefinition.colorso breaking balls are readable, or by the special ball’s colour.
Related
- Pitch Types: every pitch type’s numbers
- Pitchers and Bosses: who throws what
- Special Balls: effects that change the path
- Swing and Timing: hitting what comes in
- Batting Explained: how each pitch’s hit power is used
- Difficulty: faster pitches and more special balls
- At-Bat Loop: when a pitch is created