Characters and Rigs

In short

The at-bat talks to the batter through IBatterFigure and to the pitcher through IPitcherFigure. Two batter figures exist side by side: HumanBatter (an animated Humanoid, used by every batter today, with the rigged bean models) and BeanBatter (the code-built bean). Cosmetics hang on sockets (CosmeticAnchors) and are built by CosmeticBuilder.

How it works

Which batter figure stands in the box

AtBatController creates both figures at start (HumanBatter.Create and a BeanBatter). SelectFigure(batter) picks:

  • Bean when there is no HumanBatter (library missing or not Humanoid), or CharacterLibrary.useBeans is on, or the batter’s model is BatterModel.Bean.
  • Human otherwise. HumanBatter.SetBatter then loads the batter’s riggedModel (for example Characters/Peapod) and turns it into a Humanoid with BeanRig.MakeHumanoid. If that fails, or the batter has no riggedModel, it uses the library’s batterPrefab.

In DefaultContent all four batters (Peapod, Baked Bean, String Bean, Pinto) are BatterModel.Human with a rigged bean model, so all of them play as animated rigged beans. useBeans is off in Resources/CharacterLibrary.asset.

HumanBatter

  • Plays the batting-stance idle through a PlayableGraph (no Animator Controller asset). In LateUpdate the upper body leans and twists with the swing, the head turns to the pitcher, and two-bone IK puts both hands on the bat handle.
  • Before pitches it plays a walk-in (first pitch of a round) or a step-in, placed to end on the batter’s spot; the bat rides in the animated hands, then is handed back to the stance grip.
  • Rigged beans keep their modelled size; the library human is scaled to batterHeadHeight.
  • On a bean whose rig has both a chest and an upper chest (String Bean since #44), the swing’s twist and lean are shared up the spine (spine 0.3, chest 0.35, upper chest 0.35) and the head turn toward the pitcher is shared too (chest 0.2, upper chest 0.35, head 0.45 of the turn), so the long neck curves instead of hinging at the head and the waist doesn’t pinch. Other figures twist the spine and turn the head as before.
  • A home run of 480 ft or more knocks the hat off (CosmeticWardrobe.KnockOffHat). FlyingHat tumbles it to the grass and flies it back on at the next pitch.

BeanRig

Maps a bean’s bone names to Unity Humanoid bones, by either of two namings: the bean rig from the Blender scripts (hips, spine, chest, head, upper_arm.L, forearm.L, hand.L, thigh.L, shin.L, foot.L and the .R side) or the game-engine style rig String Bean uses since #44 (pelvis, spine_01, spine_02, spine_03, head, clavicle_l, upperarm_l, lowerarm_l, hand_l, thigh_l, calf_l, foot_l and the _r side). Chest, upper chest and the shoulders are optional (shorter beans go hips, spine, head); any other missing bone logs a warning and the bean can’t be animated. It builds an avatar with a T-pose reference (the beans are modelled in an A-pose), so the same Mixamo-style clips play on them. It also places cosmetic sockets from the hat socket (hat_socket or SOCKET_Head), head and foot bones, using riggedEyeHeight and riggedEyeSpacing from the batter definition for glasses.

BeanBatter (code-built)

Built from primitives: a bean body in the batter’s colour (shape from BeanShape: Peapod, Baked, String, Pinto), big eyes, a grin, a jersey band, a default cap and shoes (hidden when cosmetics replace them), white gloves locked to the bat and rubber-hose arms solved with NoodleArm (two-bone IK that stretches when out of reach). Hops into the box, blinks, breathes. Used only when the conditions above pick it.

Pitchers

AtBatController uses HumanPitcher.Create(library) and falls back to BeanPitcher.Create.

  • HumanPitcher, clip mode (CharacterLibrary.pitchingClip set): plays the pitching clip, finds the release frame as the frame where the throwing hand moves fastest, turns the pitcher so that throw points at the plate, and times the clip so release lands on the pitch’s release time. Scripted mode (no clip): idle plus an IK delivery.
  • BeanPitcher: scripted delivery with rubber-hose arms.
  • Either way, ReleasePoint comes from the figure and pitches start there.

Staging for the end-of-game cutscenes

AtBatController.Outro.cs stages the figures for OutroDirector (Cutscenes): StageOutro puts a batter (yours, or a teammate’s batter, outfit and bat in co-op) in the box and the pitcher on the mound with no zone, ring or ball; OutroPose(relaxed) switches between the stance and standing without the bat; TopplePitcher(angle) knocks the pitcher over backwards, pivoting at his feet, while his animation keeps playing (BOSS DOWN); ShowReplayBall flies the winning hit’s ball along its recorded path. EndOutro undoes all of it.

Cosmetics on figures

  • CosmeticAnchor = a bone plus a bone-local pose and a size; CosmeticAnchors holds headTop, eyes, leftFoot, rightFoot.
  • Where anchors come from: rigged beans from BeanRig.Anchors; the library human from its CharacterFitProfile if set, else FitMeasurer (the automatic fit); the code bean from its shape.
  • CosmeticWardrobe builds items on the anchors, tears them down, remembers the hat, and draws sockets as gizmos.
  • CosmeticBuilder.Build makes the code-built styles (TopHat, BallCap, CowboyHat, Sunglasses, NerdGlasses, Monocle, Sneakers, Cleats, ClownShoes) sized from the anchor, or instantiates the item’s prefab (Model). Bats never use a socket: they hang on the bat pivot.
  • CosmeticPop gives a new item a scale bounce (0.5 to 1.12 to 1 in 0.22 s).

Libraries in Resources

AssetHolds
Resources/CharacterLibrary.assetuseBeans (off), batter prefab TT_demo_male_B and pitcher prefab TT_demo_male_A (ToonyTinyPeople), the batter fit profile Content/Characters/TT_demo_male_B_Fit.asset, clips (BaseballIdle, BaseballWalkIn, BaseballStepIn, BaseballPitching, idle m_idle_A), head heights 1.1 / 1.2 m.
Resources/BatModelLibrary.assetBat prefab per Bat-slot upgrade id, a default bat, and the alignment (modelRotation, modelOffset, modelScale). Missing = the primitive bat.
Resources/Characters/The rigged beans: Peapod.fbx, BakedBean.fbx, StringBean.fbx, Pinto.fbx, and the textures peapod_albedo.png, baked_bean_albedo.png, pinto_gradient.png. String Bean has no texture since #44 (flat toon colours; riggedTexture is null).

Blender scripts (Tools/Blender/)

Run with blender --background --factory-startup --python <script> -- <args>.

ScriptMakes
make_bean.py <BeanName> <out_dir> [preview.png]A rigged bean with the shared rig and bone names. Only Pinto is defined; Peapod and Baked Bean come from the team’s own scripts in a separate HomerunDerbyAssets repo. The new String Bean (#44) was modelled in a Blender file outside the repo (the DefaultContent comment says Documents/StringBean), with smoother neck and waist weights and a brighter green.
make_farm_kit.py <out_dir> [lineup.png]Farmyard props (Resources/Props/Farm).
make_moon_kit.py <out_dir> [lineup.png]Moon Stadium props (Resources/Props/Moon). Naming: _Spin, _Yaw, _Bob, materials with Glow.

Numbers

WhatValueWhere to tune
Hat knock-off distance480 ft (both figures)HumanBatter.HatFliesAtFt, BeanBatter.OnHitLanded
Bean head radius (sizing)0.13 mBeanRig.HeadRadius
Bean foot length (sizing)0.28 mBeanRig.FootLength
Default bat scale1.07BatModelLibrary.modelScale
String Bean eye height / spacing1.25 m / 0.109 m (were 1.52 / 0.128 with the old model)DefaultContent (RiggedBean)
Long-spine twist sharesspine 0.3, chest 0.35, upper chest 0.35HumanBatter.SpineTwistShare etc.
Long-neck head-turn shareschest 0.2, upper chest 0.35, head 0.45HumanBatter.ChestShare, UpperChestShare, HeadShare

In the code

FileWhat it does
Scripts/Batting/IBatterFigure.csBatter contract, incl. the Fit Mode members (FitProfile, Anchors, GetBone, RefreshFit, TryMeasureFit).
Scripts/Pitching/IPitcherFigure.csPitcher contract (ReleasePoint, BallInHand, LeadTime, BeginPitch).
Scripts/Batting/HumanBatter.csHumanoid batter.
Scripts/Batting/BeanRig.csRigged bean to Humanoid, either bone naming.
Scripts/Batting/AtBatController.Outro.csFigures staged for the end-of-game cutscenes.
Scripts/Batting/BeanBatter.csCode-built bean.
Scripts/Core/HumanRig.csSpawn (and Toonify), PlayLoop, FitHeadHeight, SolveTwoBone, SolveStretchy.
Scripts/Batting/NoodleArm.csRubber-hose arm IK.
Scripts/Pitching/HumanPitcher.cs, BeanPitcher.csPitcher figures.
Scripts/Batting/CosmeticBuilder.cs, CosmeticWardrobe.cs, CosmeticPop.cs, FlyingHat.csCosmetics on figures.
Scripts/Data/CharacterLibrary.cs, BatModelLibrary.csThe Resources libraries.

Gotchas

  • The CharacterFitProfile is tuned for the library human only. Rigged beans ignore it (_profile = null), so Fit Mode’s SOCKET mode says the body has no fit profile for every current batter (Fit Mode).
  • BeanShape still matters only when useBeans swaps everyone to code-built beans.
  • A batter’s riggedModel that fails to load logs a warning and falls back to the library human.
  • BeanRig’s comment still points at HomerunDerbyAssets/scripts/make_peapod.py for the bean-rig naming; the String Bean model itself has no script in either repo.