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
-
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). -
world/AlienControlSystem.java:AbstractAppStatequi anime tous les
aliens.initialize()charge defs + spawne lesAlienRuntimeStatedepuis
les sprites typeId=0.update(tpf)accumule le temps en frames Amiga et
appelleAlienAI.update()pour chacun. Sync la position du Node JME et
detache les aliens morts. API publiquedamageAlien()et
findNearestAlien()pour les futurs systemes (collision tirs, IA team). -
test/.../AlienDefLoaderIntegrationTest.java: 7 tests d’integration
qui valident le parsing du vraiassets/levels/definitions.jsonregenere
par convertLevels. Skippe automatiquement si le fichier n’existe pas
(utilise@EnabledIf).
Fichiers modifies
app/GameAppState.java:
– Champprivate AlienControlSystem alienControlSystem;apreszoneTracker
– DanssetupPhysics(): extraction deWorldRaycaster raycaster(etait
scope local), creationAiWorldAdapter+AlienControlSystem+ attach
viasa.getStateManager().attach(). Path defs :jmeAssets.resolve("levels/definitions.json").
– Danscleanup(): detachalienControlSystem(apresphysicsBulletSystem).
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>.pngquel que soit l’angle vs camera. - Bullet -> alien : pas de detection de collision tirs joueur sur aliens.
Il faut etendreBulletUpdateSystemou creer unAlienBulletCollisionSystem
qui appelleAlienAI.inflictDamage()au moment du hit. - Alien -> joueur : les aliens
attacksWithGun()=truene tirent pas encore
(TODO dansAlienAI.doResponse). Pour les melee, on charge sans collision
(le joueur peut traverser). - Pathfinding :
AiWorldAdapter.controlPointsest une liste vide. Il faut
exporter les CP depuisLevelSceneBuilder(en userData sur le levelScene)
et porterGetNextCPt(graphe BFS). - Mouvement :
AlienAI.moveTowards*est lineaire sans collision murs.
Il faut brancherWallCollisionpour porterMoveObject + Obj_DoCollision. - Boss spawn on death :
splatType >= NUM_BULLET_DEFSdoit spawner des
aliens plus petits (snake -> alien2, etc.). TODO dansAlienAI.justDied. - Audio :
AiWorldAdapter.playPositionalSoundest 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.inUpperZonetoujours 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