At-Bat Loop
In short
AtBatControllerruns 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 aRoundContextand gets back aRoundResult.
How it works
- Begin.
BeginRound(context, onFinished)builds a seededPitcherAI(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 theRoundLoopcoroutine. - 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. - Pitch (
PitchRoutine). The ball is releasedBatterSetBeforeRelease(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.CreatePitchdecides the pitch;GameEvents.PitchThrownfires. The Phantom’s decoys fly with it (see Special Balls). - Swing window. Every frame until
ArrivalTime + 0.3 s: move the ball alongPitchData.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. - Decide. No press = Take. A press with no contact = Whiff. Contact =
SwingResolver.FromContactwith the held hit aim (see Batting Explained), thenHitPhysics.SimulateandScoreCalculator.Score. The green streak updates,GameEvents.SwingResolvedfires andSwingCompletedfires (co-op streams it). - 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.
- 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. - Between pitches. A skippable wait of the pitcher’s
timeBetweenPitchesplusextraPauseBetweenPitches. After a home run the batter steps back into the box. - 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.RoundFinishedfires and the callback gets theRoundResult.
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
| What | Value | Where to tune |
|---|---|---|
| Windup when there is no pitcher figure | 0.7 s | AtBatController.WindupSeconds |
| Ball released after batter is set | 0.4 s | BatterSetBeforeRelease |
| Settle on the batting view before a windup | 1.2 s | SettleBeforeWindup |
| Swing window ends | ArrivalTime + 0.3 s | PitchRoutine |
| Pre-round wait / end-of-round wait | 1.5 s / 1.5 s (skippable) | RoundLoop |
| Pause between pitches | pitcher timeBetweenPitches (default 1.2 s) + RunConfig.extraPauseBetweenPitches (1 s) | PitcherDefinition, RunConfig |
| Flight playback speed | 1.6x | FlightPlaybackSpeed |
| Roll shown after first landing | up to 3 s | MaxRollShownSeconds |
| Play card before cutting back | 1.2 s from landing (at least 0.3 s after the roll) | PlayCardSeconds, MinPauseAfterRoll |
| Home run unskippable for | 0.5 s of flight | MinHomeRunFlightSeconds |
| Ground out if max height below | 12 ft | GroundBallMaxHeightFt |
| Hit stop | 0.09 s real time (quality >= 0.7), 0.16 s (GREEN), at time scale 0.05 | HitStopSeconds, GreenHitStopSeconds, HitStopTimeScale |
| Hit stop threshold | quality >= 0.7 | SwingResolver.HitStopQuality |
| Replay rule | any GREEN, or PERFECT zone with timing >= 0.6 | SwingResolver.ReplayTiming, EarnsReplay |
| Contact close-up span | 0.35 s before contact to 0.22 s after, at 0.2x speed | InstantReplay constants |
| Close-up camera | 2 m away, FOV 34, orbits -15 to 20 degrees | InstantReplay 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
| File | What it does |
|---|---|
Assets/_Project/Scripts/Batting/AtBatController.cs | BeginRound, RoundLoop, PitchRoutine, PlayOutcome, HitStop, BallInPlay, ResolvePlay, SkippableWait, IsRoundOver, aim helper |
Assets/_Project/Scripts/Batting/AtBatController.HitShow.cs | Counter, tracer, flight camera, WantsBigReplay, BigReplay |
Assets/_Project/Scripts/Batting/AtBatController.Spectate.cs | Co-op spectating: replays another player’s pitches from the SpectateFeed; WantsSkip |
Assets/_Project/Scripts/Batting/AtBatController.Outro.cs | Staging for the end-of-game cutscenes (Cutscenes) |
Assets/_Project/Scripts/Batting/InstantReplay.cs | Records bat poses, plays the contact close-up |
Assets/_Project/Scripts/Batting/RoundTypes.cs | RoundContext (input, incl. walkUpShot), PlayReport (one pitch), RoundResult (output) |
Assets/_Project/Scripts/Run/RunManager.cs | PlayCurrentRound 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.
IsRoundOveruses>=on the goal. In the batting cage the goal isint.MaxValueand outs are 999, so the round never really ends; when it does, the cage starts a new session.LongestFtonly 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.
Related
- Count and Outs: what each result does to the count
- Pitching: how each pitch is chosen
- Swing and Timing: how a press becomes contact
- Batting Explained: how contact becomes a hit
- Batting Feedback: what the player is shown
- Hit Show: counter, tracer, camera, replays
- Hit Physics: the flight that
BallInPlayreplays - Scoring: what a home run is worth
- Run Structure: innings and what happens after
RoundFinished - Batting Cage: the endless practice round
- Co-op Run Flow: turns use
startingScore,isTurnandpreRollSeconds