Aller au contenu principal

Session 113 — Phase 2.B : integration runtime IA dans le jeu

Contexte

La phase 2.A (= meme jour, plus tot dans la session) avait livre la machine
a etats IA pure
(AlienAI, AlienBehaviour, AlienDef, AlienRuntimeState,
AlienDefLoader, AiWorld interface), avec 17 tests JUnit passant en
isolation. Restait a la brancher dans le jeu : spawner les aliens depuis
le JSON de niveau, faire tourner l’IA dans la boucle JME, sync les Nodes.

Architecture du wiring

 GameAppState.setupPhysics()
   |
   +-- WorldRaycaster (cree)
   |
   +-- AiWorldAdapter (NEW)         <- implements AiWorld
   |     - camera (lecture position joueur)
   |     - ZoneTracker (zone joueur, brightness)
   |     - WorldRaycaster (test LOS)
   |     - levelScene root
   |
   +-- AlienControlSystem (NEW)     <- extends AbstractAppState
         - charge definitions.json -> AlienDef[20]
         - itere levelScene/items pour typeId=0
         - cree AlienRuntimeState par instance
         - chaque frame : ai.update(state) + sync Node JME

Conversion d’unites

L’IA travaille en unites Amiga (signed 16-bit, ~32 unites = 1 m JME). Le wiring
gere les conversions :

Domaine Unite Conversion vers JME
Position X int Amiga jx = amigaX / 32
Position Z int Amiga jz = -amigaZ / 32
Position Y int Amiga (inv) jy = -amigaY / 32
Angle 0..4095 (12-bit) rad = a * TAU / 4096
Vitesse unites Amiga/tick step = speed * tempFrames
Timer tick 50Hz Amiga seconds = ticks / 50

Frame skip Amiga -> JME

AlienControlSystem accumule le tpf (~16ms a 60 fps JME) et appelle l’IA
tous les 20ms (= 50 Hz Amiga). Si le moteur lague, on cap a 4 frames Amiga par
frame JME pour eviter de geler. Le tempFrames resultant est passe a
AiWorldAdapter.setTempFrames() pour multiplier les vitesses et timeouts.

Fichiers nouveaux

  1. world/AiWorldAdapter.java : implementation runtime de
    core.ai.AiWorld. Lit camera + zoneTracker + raycaster pour fournir les
    queries. Le LOS est un raycast Bullet contre la geometrie du niveau.
    getRandom() est seede a 42L pour la reproductibilite. playPositionalSound
    est un no-op pour l’instant (audio porte plus tard).

  2. world/AlienControlSystem.java : AbstractAppState qui anime tous les
    aliens. initialize() charge defs + spawne les AlienRuntimeState depuis
    les sprites typeId=0. update(tpf) accumule le temps en frames Amiga et
    appelle AlienAI.update() pour chacun. Sync la position du Node JME et
    detache les aliens morts. API publique damageAlien() et
    findNearestAlien() pour les futurs systemes (collision tirs, IA team).

  3. test/.../AlienDefLoaderIntegrationTest.java : 7 tests d’integration
    qui valident le parsing du vrai assets/levels/definitions.json regenere
    par convertLevels. Skippe automatiquement si le fichier n’existe pas
    (utilise @EnabledIf).

Fichiers modifies

  1. app/GameAppState.java :
    – Champ private AlienControlSystem alienControlSystem; apres zoneTracker
    – Dans setupPhysics() : extraction de WorldRaycaster raycaster (etait
    scope local), creation AiWorldAdapter + AlienControlSystem + attach
    via sa.getStateManager().attach(). Path defs : jmeAssets.resolve("levels/definitions.json").
    – Dans cleanup() : detach alienControlSystem (apres physicsBulletSystem).

Verifications

assets/levels/definitions.json est deja a jour (genere lors d’une
execution precedente de convertLevels avec le nouveau code) :
– Red Alien (#0) : reactionTime=5, hitPoints=2, gfxType=0 BITMAP
– Snake Scanner (#1) : isFlying=true, attacksWithGun=true, gfxType=1 VECTOR
– Mantis Boss (#16) : hitPoints=125, girth=2 (large), height=800
– Crab Boss (#18) : hitPoints=125, gfxType=1 VECTOR

Limitations connues (a faire en phase 2.C)

  • Sprites 4-directionnels : ViewpointToDraw pas encore porte. Les sprites
    alien affichent toujours _f<defFrame>.png quel que soit l’angle vs camera.
  • Bullet -> alien : pas de detection de collision tirs joueur sur aliens.
    Il faut etendre BulletUpdateSystem ou creer un AlienBulletCollisionSystem
    qui appelle AlienAI.inflictDamage() au moment du hit.
  • Alien -> joueur : les aliens attacksWithGun()=true ne tirent pas encore
    (TODO dans AlienAI.doResponse). Pour les melee, on charge sans collision
    (le joueur peut traverser).
  • Pathfinding : AiWorldAdapter.controlPoints est une liste vide. Il faut
    exporter les CP depuis LevelSceneBuilder (en userData sur le levelScene)
    et porter GetNextCPt (graphe BFS).
  • Mouvement : AlienAI.moveTowards* est lineaire sans collision murs.
    Il faut brancher WallCollision pour porter MoveObject + Obj_DoCollision.
  • Boss spawn on death : splatType >= NUM_BULLET_DEFS doit spawner des
    aliens plus petits (snake -> alien2, etc.). TODO dans AlienAI.justDied.
  • Audio : AiWorldAdapter.playPositionalSound est un no-op. A brancher
    sur AudioNode JME quand l’AudioManager sera porte.
  • Team coordination : AI_AlienTeamWorkspace_vl (partage position joueur
    entre coequipiers) pas porte. Chaque alien decide en solo.
  • Upper zones : AlienRuntimeState.inUpperZone toujours false (les zones
    doubles sont rares dans le jeu, on traitera plus tard).

Pour valider

# Compile + tests
./gradlew compileJava test

# Regenere assets si besoin
./gradlew convertLevels buildScenes

# Lance le jeu, choisir niveau A pour voir les aliens
./gradlew run

Resultat attendu : sur le niveau A, on doit voir des Red Aliens spawner
au bons emplacements (positions du JSON), passer en mode RESPONSE (anim 1)
quand le joueur entre dans leur ligne de vue, et charger vers lui en ligne
droite. Sans collision player-alien, on peut les traverser. La mort par tir
n’est pas encore active (il faut le BulletAlienCollisionSystem en 2.C).

Statistiques

  • 3 fichiers nouveaux (~600 lignes : adapter + control system + integration tests)
  • 1 fichier modifie (GameAppState.java : ~30 lignes ajoutees)
  • 7 tests d’integration en plus des 17 unitaires de phase 2.A = 24 tests IA

Laisser un commentaire

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