At-Bat Loop

In short

AtBatController runs one round (an inning, a co-op turn, or the batting cage): pitch, swing, flight, result, then the next pitch. The round ends only between pitches: when the round score reaches the inning goal or no outs are left. It knows nothing about the run, shop or meta progression. The run hands it a RoundContext and gets back a RoundResult.

How it works

  1. Begin. BeginRound(context, onFinished) builds a seeded PitcherAI (see Pitching) and a seeded random for the hit’s rolls (seed ^ 0x6b1d), resets the score, home runs, bonus coins, longest hit, green streak and the count (Count.Reset(outsAllowed)), dresses the batter and bat, and starts the RoundLoop coroutine.
  2. Walk-in. The HUD shows “Now facing pitcher” (not after the match intro, whose walk-up camera follows the walk-in instead; the aim helper and zone stay hidden until 0.3 s before it lands; see Cutscenes). In co-op the first pitch also waits at least preRollSeconds (the “YOUR TURN!” 3-2-1). Then a skippable 1.5 s wait.
  3. Pitch (PitchRoutine). The ball is released BatterSetBeforeRelease (0.4 s) after the batter is set, and never sooner than 1.2 s plus the windup after the camera comes back to the batting view. PitcherAI.CreatePitch decides the pitch; GameEvents.PitchThrown fires. The Phantom’s decoys fly with it (see Special Balls).
  4. Swing window. Every frame until ArrivalTime + 0.3 s: move the ball along PitchData.PositionAt, fade a ghost ball, record the bat pose for the replay, accept one swing press (only after release, never while paused), and sweep the bat against the ball (BatCollision.Sweep). See Swing and Timing.
  5. Decide. No press = Take. A press with no contact = Whiff. Contact = SwingResolver.FromContact with the held hit aim (see Batting Explained), then HitPhysics.Simulate and ScoreCalculator.Score. The green streak updates, GameEvents.SwingResolved fires and SwingCompleted fires (co-op streams it).
  6. Play it out (PlayOutcome):
    • Take or Whiff: update the count (see Count and Outs).
    • Contact: hit stop, then the contact close-up if the swing earns the replay and it is not a big moment, then the ball’s flight (BallInPlay) with the Hit Show, then the big replay if wanted.
  7. Ball in play. The flight camera, tracer and live counter start. The path plays at 1.6x speed (eased to half speed at the wall on a big moment). The play is decided the moment the ball first lands (ResolvePlay): home run, foul or out. Up to 3 s of bounce and roll keep playing under the play card. A home run then waits for its score total to fly into the score panel.
  8. Between pitches. A skippable wait of the pitcher’s timeBetweenPitches plus extraPauseBetweenPitches. After a home run the batter steps back into the box.
  9. Round end. The loop stops when IsRoundOver. The HUD shows “INNING n CLEARED!” (or “ROUND CLEARED!”), “TURN OVER” (co-op turn) or “Out of outs…“. After a skippable 1.5 s, GameEvents.RoundFinished fires and the callback gets the RoundResult.
stateDiagram-v2
    [*] --> WalkIn: BeginRound
    WalkIn --> Pitch: batter set + 0.4 s
    Pitch --> SwingWindow: ball released
    SwingWindow --> Take: no press by arrival + 0.3 s
    SwingWindow --> Whiff: pressed, no contact
    SwingWindow --> Contact: bat sweep hits ball
    Take --> Result: strike or ball
    Whiff --> Result: strike
    Contact --> HitStop: GREAT, PERFECT or GREEN
    Contact --> Flight: otherwise
    HitStop --> CloseUp: earns replay, not a big moment
    HitStop --> Flight
    CloseUp --> Flight
    Flight --> Result: ball first lands
    Result --> BigReplay: big-moment home run
    BigReplay --> Pause
    Result --> RoundOver: score >= goal or no outs left
    Result --> Pause: otherwise
    Pause --> Pitch: time between pitches
    RoundOver --> [*]: RoundFinished

Hit stop

On contact the game freezes for a moment so big hits land with a “crack”. Time.timeScale drops to 0.05 for a fixed real-time span: 0.16 s on a GREEN, 0.09 s on any other swing with quality 0.7 or more (GREAT or PERFECT). It is skipped while paused, and it never overrides a pause that starts during it.

Instant replay

SwingResolver.EarnsReplay: every GREEN, and a PERFECT-zone hit with timing 0.6 or better. The bat and batter pose is recorded every frame during the pitch (the wobbling bat can’t be recomputed afterwards). The ball replays exactly from the pitch path before contact and the hit path after. The camera orbits the contact point from the first-base side at 0.2x speed.

  • Not a big moment: the close-up plays before the flight.
  • A big-moment home run (and Settings REPLAYS on): the close-up plays after the play, followed by the side-on tracer shot. See When the big replay plays (WantsBigReplay).

The HUD letterboxes the screen with “INSTANT REPLAY”, a tag (see Batting Feedback) and “SPACE / CLICK TO SKIP”.

Skipping dead time

Space / click during a wait sets a flag that skips every wait up to the next pitch, not just the current one. During the flight it jumps the ball to where it comes to rest (the play still resolves on the way). A home run can only be skipped after it has flown 0.5 s. A click also hurries a home run’s total into the score panel. A click in a replay skips the whole replay. See Controls.

Numbers

WhatValueWhere to tune
Windup when there is no pitcher figure0.7 sAtBatController.WindupSeconds
Ball released after batter is set0.4 sBatterSetBeforeRelease
Settle on the batting view before a windup1.2 sSettleBeforeWindup
Swing window endsArrivalTime + 0.3 sPitchRoutine
Pre-round wait / end-of-round wait1.5 s / 1.5 s (skippable)RoundLoop
Pause between pitchespitcher timeBetweenPitches (default 1.2 s) + RunConfig.extraPauseBetweenPitches (1 s)PitcherDefinition, RunConfig
Flight playback speed1.6xFlightPlaybackSpeed
Roll shown after first landingup to 3 sMaxRollShownSeconds
Play card before cutting back1.2 s from landing (at least 0.3 s after the roll)PlayCardSeconds, MinPauseAfterRoll
Home run unskippable for0.5 s of flightMinHomeRunFlightSeconds
Ground out if max height below12 ftGroundBallMaxHeightFt
Hit stop0.09 s real time (quality >= 0.7), 0.16 s (GREEN), at time scale 0.05HitStopSeconds, GreenHitStopSeconds, HitStopTimeScale
Hit stop thresholdquality >= 0.7SwingResolver.HitStopQuality
Replay ruleany GREEN, or PERFECT zone with timing >= 0.6SwingResolver.ReplayTiming, EarnsReplay
Contact close-up span0.35 s before contact to 0.22 s after, at 0.2x speedInstantReplay constants
Close-up camera2 m away, FOV 34, orbits -15 to 20 degreesInstantReplay constants

Play headlines (PlayReport.headline): STRIKE LOOKING, STRUCK OUT LOOKING, BALL, BALL FOUR - COUNT RESETS, SWING AND A MISS, STRUCK OUT!, HOME RUN!, FOUL BALL, OFF THE WALL - OUT, GROUND OUT, FLY BALL - OUT.

In the code

FileWhat it does
Assets/_Project/Scripts/Batting/AtBatController.csBeginRound, RoundLoop, PitchRoutine, PlayOutcome, HitStop, BallInPlay, ResolvePlay, SkippableWait, IsRoundOver, aim helper
Assets/_Project/Scripts/Batting/AtBatController.HitShow.csCounter, tracer, flight camera, WantsBigReplay, BigReplay
Assets/_Project/Scripts/Batting/AtBatController.Spectate.csCo-op spectating: replays another player’s pitches from the SpectateFeed; WantsSkip
Assets/_Project/Scripts/Batting/AtBatController.Outro.csStaging for the end-of-game cutscenes (Cutscenes)
Assets/_Project/Scripts/Batting/InstantReplay.csRecords bat poses, plays the contact close-up
Assets/_Project/Scripts/Batting/RoundTypes.csRoundContext (input, incl. walkUpShot), PlayReport (one pitch), RoundResult (output)
Assets/_Project/Scripts/Run/RunManager.csPlayCurrentRound builds the context; OnRoundFinished pays coins

Events raised per pitch, in order: PitchThrown, SwingStarted (if swung), SwingResolved, then HitLanded (contact only), CountChanged, PlayEnded. ScoreboardHit fires when the ball reaches the scoreboard. See Game Events. PlayEnded also feeds the Swing Log.

Gotchas

  • The round only checks for its end between pitches. A dev score boost or a home run that reaches the goal ends the round after the current pitch finishes.
  • A swing pressed before the ball is released is ignored, not queued.
  • IsRoundOver uses >= on the goal. In the batting cage the goal is int.MaxValue and outs are 999, so the round never really ends; when it does, the cage starts a new session.
  • LongestFt only counts home runs.
  • Settings REPLAYS gates only the big replay; the contact close-up before a flight still plays.
  • Spectators never decide anything: each result, including the score, comes from the batter’s machine. Only the batted ball’s flight is simulated locally. See Live Spectating.
  • Dev hooks live here: DevInstaHomeRun, DevForcedHit (TEST HITS), DevAlwaysReplay, DevReplayNext, DevAddScore. See Dev Menu and Shortcuts.