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_wn’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)activeAmplitudelerpee versMAX_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 » (
duckedtoujours false). A relier a
PlayerState.duckedquand 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
BobControllerselon le ressenti.
Fichiers modifies
core/BobController.java(nouveau) : phase, amplitude, lissage, accesseursapp/GameAppState.java:- import
BobController - champ
private final BobController bobController = new BobController() weaponView.setBobController(bobController)apres instantiationupdate():walkIntensity+ appelbobController.update(...)updateCameraFromPlayer(): application bob Y + bob X a la position cameraweapon/WeaponViewAppState.java:- import
BobController - champ
private BobController bobController - methode
setBobController(BobController) update(): ajoutup.mult(bobYExtra)aweaponPos