Aller au contenu principal

Session 109 — Bob de marche (head bob) camera + arme

Probleme

Quand le joueur marche, la camera reste parfaitement plate et l’arme tenue ne
bouge pas. Il manque le « head bob » classique des FPS — oscillation verticale
de la vue + de l’arme synchronisee avec les pas. Sans ça, le mouvement
parait artificiel (le joueur glisse au lieu de marcher).

Diagnostic ASM

hires.s ligne 2842-2867 (fonction Plr1_Control) :

phase = plr1_TmpBobble_w           ; angle 16-bit (oscillator)
sin = SinCosTable[phase]            ; -32768..+32767
bobbleY = (|sin| + 16384) >> 4     ; toujours positif
if (!Ducked && !Squished) bobbleY *= 2
plr1_BobbleY_l = bobbleY

; Application :
;  - camera : Plr1_TmpYOff_l += BobbleY        (1.0x)
;  - arme   : weaponY        += BobbleY * 1.5  (1.5x)
;  - xwobble = sin(phase) >> 6  (lateral signed)

Points cles :

  • |sin(phase)| produit 2 oscillations descendantes par cycle (1 cycle =
    2 pas, gauche + droit). C’est le head-bob classique.
  • BobbleY est toujours positif : la camera ne descend jamais en-dessous de
    sa hauteur « debout neutre », elle pulse vers le haut a chaque pas.
  • L’arme oscille a 1.5x la camera, ce qui se traduit visuellement par un
    decalage relatif (l’arme bouge a l’ecran).
  • L’incrementation de Plr1_Bobble_w n’est pas dans le source ASM dispo
    (variable jamais ecrite). On reconstruit la mecanique cote Java.

Architecture Java

Nouvelle classe core/BobController.java :

  • phase (float, radians, wrap a 2π) avance proportionnellement a
    l’intensite de marche (WALK_CYCLES_PER_SEC = 1.5f)
  • activeAmplitude lerpee vers MAX_AMP_CAMERA * walkIntensity (lissage
    start/stop, transition en ~0.125 sec)
  • getBobYCamera() = |sin(phase)| * activeAmplitude (>= 0)
  • getBobYWeaponExtra() = getBobYCamera() * 0.5 (= 0.5x extra pour
    atteindre 1.5x au total avec la camera qui porte l’arme)
  • getBobX() = sin(phase) * activeAmplitude * 0.3 (signe, lateral)

Valeurs par defaut : MAX_AMP_CAMERA = 0.04f JME (~4 cm), subtil mais
perceptible.

Modifications GameAppState

Dans update(), calcul de l’intensite de marche AVANT normalisation :

float walkIntensity = walk.length();  // 0..√2 (clamp dans BobController)
if (walk.lengthSquared() > 0) walk.normalizeLocal();
player.setWalkDirection(walk.multLocal(MOVE_SPEED * tpf));
bobController.update(walkIntensity, false /*ducked*/, tpf);

Dans updateCameraFromPlayer(), la position de la camera integre les bob Y
+ X :

float bobYCam = bobController.getBobYCamera();
float bobXCam = bobController.getBobX();
Vector3f camPos = player.getPhysicsLocation().add(
    sy * bobXCam,                  // wobble lateral (axe right monde)
    EYE_HEIGHT + bobYCam,          // pulse vertical au pas
    -cy * bobXCam);
sa.getCamera().setLocation(camPos);

Le bobController est connecte au WeaponViewAppState via
weaponView.setBobController(bobController) apres l’instantiation.

Modifications WeaponViewAppState

Dans update(), on ajoute un offset Y supplementaire le long de
cam.getUp() proportionnel a getBobYWeaponExtra() :

if (bobController != null) {
    float bobYExtra = bobController.getBobYWeaponExtra();
    if (bobYExtra > 0f) {
        weaponPos.addLocal(up.mult(bobYExtra));
    }
}

Resultat : la camera porte l’arme (= 1x bob), et l’arme s’eleve de 0.5x extra,
totalisant 1.5x comme dans l’ASM. A l’ecran, l’arme bouge a chaque pas
(la difference 0.5x est visible en relatif a la camera).

Pour tester

./gradlew run
  • A l’arret : camera plate, arme stable
  • En marchant (Z/Q/S/D ou fleches) : camera oscille legerement vers le haut a
    chaque pas (~2-3 pas/sec), l’arme oscille un peu plus visiblement
  • En diagonale : meme effet (intensite ~√2 clampee a 1)
  • Stop apres marche : transition douce en ~0.125 sec, pas de snap

Limitations / TODO

  • Pas de detection « accroupi » (ducked toujours false). A relier a
    PlayerState.ducked quand le systeme de squat sera implemente.
  • Le wobble X est applique a la camera mais pas a l’arme (l’ASM ne l’applique
    pas non plus a l’arme dans le monde — il est implicite via la position de
    la camera). Suffisant en l’etat.
  • Constantes (MAX_AMP_CAMERA, WALK_CYCLES_PER_SEC) ajustables dans
    BobController selon le ressenti.

Fichiers modifies

  • core/BobController.java (nouveau) : phase, amplitude, lissage, accesseurs
  • app/GameAppState.java :
  • import BobController
  • champ private final BobController bobController = new BobController()
  • weaponView.setBobController(bobController) apres instantiation
  • update() : walkIntensity + appel bobController.update(...)
  • updateCameraFromPlayer() : application bob Y + bob X a la position camera
  • weapon/WeaponViewAppState.java :
  • import BobController
  • champ private BobController bobController
  • methode setBobController(BobController)
  • update() : ajout up.mult(bobYExtra) a weaponPos

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *