Reference · Updated 2552.10.23.15.05

Third-Person Aim Correction

Turns a third-person body's upper half so the gun in its hands points where its shot actually lands.

Turns a third-person body's upper half so the gun in its hands points where its shot actually lands.

#Why

Hit-scan shots trace from the camera (SLAimTarget), not the gun. The third-person gun hangs below and to the side of that line, and the aim pose only follows AimPitch/AimYaw, so the barrel points parallel to the shot and misses what it hits — worst up close.

#How

  1. ASLCharacterBase::UpdateAimCorrection, from BuildAnimSnapshots every tick, for a pawn whose third-person
  2. mesh was rendered recently and that is not our own pawn in first person:

    • Aim point (FindAimCorrectionTarget): our own pawn traces from the camera
    • (GetPlayerViewPoint); anyone else's from their eyes along the replicated GetBaseAimRotation. One ECC_Visibility trace up to AimCorrectionRange. Nothing new is replicated.

    • Gap: the muzzle socket's forward vector (PrimaryFireMode.MuzzleSocketName on the TP weapon mesh, or the
    • TP sidearm while it is drawn) against the line from the muzzle to the aim point.

    • The muzzle was posed with last frame's correction, so the gap is what is left; the new target is that
    • remainder on top of the current correction, kept in the TP mesh's component space so it doesn't swing as the pawn turns.

    • Capped at AimCorrectionMaxAngle. Past twice that, the gun is taken to be pointing away on purpose
    • (reload, melee, equip) and the correction fades to zero. Also zero when dead.

    • Eased with QInterpTo at AimCorrectionInterpSpeed.
  1. AnimStateSnapshot.AimCorrection (FRotator, component space).
  1. ABP_TP_MasterChief AnimGraph: Transform (Modify) Bone on spine_05, Rotation Mode Add to Existing,
  2. Component Space, between Slot 'DefaultSlot' and the first Control Rig. It sits after the weapon layers (where left-hand IK runs), and turning the chest carries both arms, so the grip stays on the gun.

Tick order matters. Step 1 assumes the pose it measures was built from the correction it wrote last frame. The engine orders a character's mesh after its movement component but not after the actor, so the anim graph could read the previous frame's snapshot, and which it got could change frame to frame. The loop then overshot and the TP body shook (seen on a second machine on a TV, both on your own pawn while jumping and on LAN opponents; fixed 2026-10-23). ASLCharacterBase::AnimateAfterSnapshots makes both meshes tick after the actor.

#Tuning

On the character under **SystemLinkAnimationAim Correction**: bAimCorrection, AimCorrectionMaxAngle

(25°: the AR's pose sits ~18° off its shot), AimCorrectionInterpSpeed (10), AimCorrectionRange (100 m).

#Assumptions to check

  • Each weapon's muzzle socket points +X down the barrel. A socket rotated otherwise makes the correction chase
  • the wrong axis (it then hits the cap and fades out).

#Debugging

sl.AimCorrection.Debug 1 in PIE. For each third-person gun on screen: red is where the barrel points, green is the line to where the shot lands, and the label gives the turn needed and whether it is applied in full (ok), held at the cap (capped), or skipped as a reload or melee (off).

sl.AimCorrection 0 turns the correction off (it eases back to none) without editing the character, so a packaged build can test whether an effect comes from it. 1 restores it; bAimCorrection still gates it per class.

#Testing

SystemLink.Character.AimCorrection covers the math in ASLCharacterBase::CalculateAimCorrection:

  • landing on the shot, including on a turned mesh
  • keeping the correction once on target
  • the cap and the cut-off
  • a correction carried with a negative W, which Slerp can hand back; unguarded it reads as 350° and switches the
  • correction off

Whether the gun lines up on screen is checked in PIE.