24 KiB
Préparation Code Review - La-Fabrik
Ce document est une fiche de révision pour préparer la code review.
Il est basé sur develop, après le merge récent autour de l'environnement, du repair game, de l'audio, du store Zustand, du hand tracking, de l'éditeur et de la doc intégrée.
Le but n'est pas de réciter le repo. Le but est de savoir :
- expliquer l'architecture générale ;
- naviguer vite entre les bons fichiers ;
- défendre les choix techniques ;
- montrer une démo propre avec Chrome DevTools ;
- reconnaître honnêtement les limites actuelles.
Ce qu'il faut retenir en premier
La-Fabrik est une expérience web 3D en React, Vite, Three.js et React Three Fiber.
Le joueur est dans un monde 3D et avance dans une progression de réparation :
intro -> bike -> pylone -> ferme -> outro
Les trois piliers à connaître pour la review :
RepairGame: boucle de gameplay principale.AudioManager: musique, SFX, dialogues et sous-titres.useGameStore/ Zustand : progression globale du jeu.
Le reste du repo sert à intégrer ces piliers :
Worldmonte la scène 3D ;InteractionManagerrelie les objets interactifs aux inputs ;HandTrackingProvideractive la webcam seulement quand elle est utile ;/editorpermet de modifier map, dialogues, SRT et cinématiques ;/docsrend la documentation Markdown dans l'app.
Pitch en 30 secondes
Phrase simple à savoir dire :
La-Fabrik est une expérience 3D interactive en React Three Fiber. Le joueur progresse dans des missions de réparation. Le coeur du gameplay est un
RepairGameréutilisable pour le vélo, le pylône et la ferme. La progression est centralisée dans Zustand, les interactions passent par un manager commun, les objets manipulables utilisent Rapier, et le joueur garde une collision octree séparée. L'audio est géré par unAudioManageravec volumes par catégorie, dialogues et sous-titres.
Carte mentale du projet
src/main.tsx
-> App.tsx
-> router.tsx
-> / HomePage + Canvas + World + GameUI
-> /editor EditorPage
-> /docs DocsLayout + Markdown pages
Dans la scène jouable :
HomePage
-> HandTrackingProvider
-> Canvas
-> World
-> GameMap
-> GameStageContent
-> RepairGame bike/pylone/ferme
-> GameMusic
-> GameDialogues
-> Player
-> GameUI
Features à connaître
Runtime 3D
Fichiers :
src/pages/page.tsxsrc/world/World.tsxsrc/hooks/world/useWorldSceneLoading.tssrc/world/GameMap.tsxsrc/world/GameMapCollision.tsx
Ce que ça fait :
- charge
public/map.json; - résout les modèles dans
public/models/{name}/model.glboumodel.gltf; - affiche des cubes fallback si un modèle manque ;
- construit l'octree joueur depuis les nodes de collision ;
- attend que map, collision, stage et octree soient prêts avant de spawn le joueur.
Phrase à retenir :
Worldne lance pas tout d'un coup. Il attend que la map, l'octree et le stage gameplay soient prêts avant de monter le joueur, la musique et les dialogues.
Piège à connaître :
Les anciens flags comme noMusic, noMap, noDialogues, noPlayer ne sont plus branchés dans World. Pour la démo, utiliser surtout ?debug.
Player et collision
Fichiers :
src/world/player/PlayerController.tsxsrc/data/input/keybindings.tssrc/data/player/playerConfig.tssrc/world/GameMapCollision.tsx
Ce que ça fait :
- déplacement
ZQSD; - souris en pointer lock ;
- saut avec
Space; - interaction avec
E; - grab avec clic gauche ;
- collision joueur via capsule Three.js + octree.
Phrase à retenir :
Le joueur n'est pas piloté par Rapier. Rapier sert aux objets manipulables, alors que le joueur utilise une capsule et un octree.
Piège à connaître :
useRepairMovementLocked() retourne actuellement false. Le lock de mouvement est prévu dans le code et l'UI, mais il est désactivé sur develop.
Interaction
Fichiers :
src/managers/InteractionManager.tssrc/hooks/interaction/useInteraction.tssrc/components/three/interaction/InteractableObject.tsxsrc/components/three/interaction/TriggerObject.tsxsrc/components/three/interaction/GrabbableObject.tsxsrc/components/ui/InteractPrompt.tsx
Ce que ça fait :
- détecte si un objet est proche ;
- raycast depuis la caméra pour savoir si le joueur vise l'objet ;
- affiche le prompt
Epour les triggers ; - gère les grabs souris et hand tracking ;
- expose un snapshot React via
useSyncExternalStore.
Phrase à retenir :
Les interactions sont séparées du gameplay. Un objet dit juste "je suis trigger" ou "je suis grabbable", puis le player controller déclenche l'action selon le focus courant.
Repair game
Fichiers coeur :
src/components/three/gameplay/RepairGame.tsxsrc/components/three/gameplay/RepairRepairingStep.tsxsrc/components/three/gameplay/RepairScanSequence.tsxsrc/components/three/gameplay/RepairMissionCase.tsxsrc/components/three/gameplay/RepairCaseModel.tsxsrc/components/three/gameplay/RepairCompletionStep.tsx
Fichiers data/state :
src/data/gameplay/repairMissions.tssrc/data/gameplay/repairCaseConfig.tssrc/data/gameplay/repairGameConfig.tssrc/types/gameplay/repairMission.tssrc/managers/stores/useGameStore.ts
Flow :
locked
-> waiting
-> inspected
-> fragmented
-> scanning
-> repairing
-> reassembling
-> done
Ce que ça fait :
- inspecter l'objet de mission ;
- afficher une mallette ;
- ouvrir/interagir avec la mallette ;
- fragmenter le modèle ;
- scanner les pièces cassées ;
- proposer plusieurs pièces de remplacement ;
- attraper et snapper les pièces ;
- vérifier bonne pièce + pièces cassées rangées ;
- réassembler le modèle ;
- valider la mission et passer à la suivante.
Phrase à retenir :
RepairGameest un orchestrateur de steps. Les variations entre vélo, pylône et ferme sont dansrepairMissions.ts, pas hardcodées dans trois composants séparés.
Audio, dialogues, sous-titres
Fichiers :
src/managers/AudioManager.tssrc/world/GameMusic.tsxsrc/utils/dialogues/playDialogue.tssrc/utils/dialogues/loadDialogueManifest.tssrc/managers/stores/useSettingsStore.tssrc/managers/stores/useSubtitleStore.tssrc/components/ui/Subtitles.tsx
Ce que ça fait :
- musique en loop ;
- sons one-shot avec pooling ;
- volumes par catégorie :
music,sfx,dialogue; - dialogues depuis
public/sounds/dialogue/dialogues.json; - SRT par voix et par langue ;
- sous-titres synchronisés avec
audio.currentTime; - queue de dialogues pour éviter les overlaps.
Phrase à retenir :
L'audio est impératif, donc il est dans un manager. React garde les réglages et les sous-titres, mais les vrais
HTMLAudioElementsont gérés parAudioManager.
Zustand
Fichiers :
src/managers/stores/useGameStore.tssrc/managers/stores/useSettingsStore.tssrc/managers/stores/useSubtitleStore.tssrc/components/ui/debug/GameStateDebugPanel.tsx
Ce que ça fait :
useGameStore: progression globale ;useSettingsStore: menu, volumes, sous-titres, runtime repair ;useSubtitleStore: sous-titre actif ;- debug panel : manipule le même store que le vrai gameplay.
Phrase à retenir :
Zustand contient l'état durable. Les valeurs qui changent à chaque frame restent dans des refs, dans R3F ou dans les managers.
Hand tracking
Fichiers :
src/providers/gameplay/HandTrackingProvider.tsxsrc/hooks/handTracking/useRemoteHandTracking.tssrc/hooks/handTracking/useBrowserHandTracking.tssrc/hooks/handTracking/useBothFistsHold.tssrc/components/three/handTracking/HandTrackingGlove.tsxbackend/main.py
Ce que ça fait :
- source
backendavec Python/FastAPI/MediaPipe ; - source
browseravec@mediapipe/tasks-vision; - activation seulement quand utile ;
- gesture deux poings fermés ;
- grab à la main pour certains objets ;
- visualisation gants et panel debug.
Phrase à retenir :
Le hand tracking n'est pas actif tout le temps. Le provider l'active quand le contexte le justifie, par exemple pendant certaines étapes du repair game.
Editor
Fichiers :
src/pages/editor/page.tsxsrc/components/editor/EditorControls.tsxsrc/components/editor/scene/EditorScene.tsxsrc/components/editor/scene/EditorMap.tsxvite.config.ts
Ce que ça fait :
- édite
public/map.json; - affiche et transforme les objets ;
- édite dialogues, SRT et cinématiques ;
- preview audio/cinématique ;
- sauvegarde locale via endpoints Vite.
Phrase à retenir :
L'éditeur partage les mêmes formats que le runtime. Il n'y a pas un format map pour l'éditeur et un autre pour le jeu.
Piège à connaître :
Les endpoints /api/save-* sont des helpers Vite en dev local, pas une API de production.
Bloc principal 1 : RepairGame
Ce qu'il faut comprendre
RepairGame ne fait pas "juste afficher une réparation". Il coordonne :
- le state global Zustand ;
- les étapes de mission ;
- les assets GLTF ;
- les prompts vidéo ;
- la mallette ;
- les interactions
E; - les objets Rapier grabbables ;
- le scan des pièces ;
- la validation gameplay.
Navigation simple
Ouvrir dans cet ordre :
src/world/GameStageContent.tsxsrc/components/three/gameplay/RepairGame.tsxsrc/data/gameplay/repairMissions.tssrc/components/three/gameplay/RepairRepairingStep.tsxsrc/managers/stores/useGameStore.ts
Comment expliquer l'architecture
GameStageContent place les missions dans le monde.
RepairGame reçoit une mission :
<RepairGame mission="bike" position={[8, 0, -6]} />
Puis il vérifie :
- est-ce que
mainStatecorrespond à cette mission ? - quel est le
stepcourant ? - quelle config utiliser ?
- quel sous-composant monter ?
Les variations mission sont dans repairMissions.ts :
- modèle ;
- prompt vidéo ;
- pièces cassées ;
- bonnes pièces ;
- leurres ;
- timings ;
- placeholders.
Pourquoi c'est bien
- Un seul flow réutilisable.
- Moins de duplication entre
bike,pylone,ferme. - Les règles générales restent dans les composants.
- Les variations restent dans la data.
- Le debug panel peut tester les mêmes steps que le vrai jeu.
Compromis
Si une future mission devient très différente, la config ne suffira peut-être plus. Il faudra alors créer des composants spécifiques ou un vrai MissionManager.
Points à dire si on ouvre RepairRepairingStep
Ce fichier gère l'étape la plus gameplay :
- pièces de remplacement ;
- pièces cassées à déposer ;
- snap vers placeholders ;
- feedback vert/rouge/bleu ;
- validation finale.
État local important :
placedPartIdsdepositedBrokenPartIdsshowBlockedInstallFeedback
Phrase simple :
Cette étape garde seulement l'état local de manipulation. La progression globale reste dans Zustand.
Bloc principal 2 : Audio
Ce qu'il faut comprendre
AudioManager est un singleton parce qu'il manipule des objets impératifs du navigateur :
HTMLAudioElementAudioContext- pools audio
- panner stereo
- musique active
Navigation simple
Ouvrir dans cet ordre :
src/managers/AudioManager.tssrc/world/GameMusic.tsxsrc/managers/stores/useSettingsStore.tssrc/utils/dialogues/playDialogue.tssrc/components/ui/Subtitles.tsx
Architecture audio
Catégories :
music
sfx
dialogue
Volumes :
volume effectif = volume de base * volume catégorie
Exemple :
GameMusiclance/sounds/musique/test.mp3avec base0.33.- Si le volume musique settings vaut
0.5, le volume effectif vaut0.165.
Dialogues et sous-titres
Un dialogue référence :
- un
id; - une voix ;
- un fichier audio ;
- un index de cue SRT.
Le SRT est par voix et par langue, pas un fichier SRT par dialogue.
Phrase simple :
Les dialogues utilisent la catégorie audio
dialogue, etplayDialogueByIdsynchronise le sous-titre actif avec le temps courant de l'audio.
Risques à connaître
- Autoplay navigateur : la musique peut attendre un input utilisateur.
- Un seul track musique actif à la fois.
- Les dialogues sont queue-based, pas un vrai système de priorité.
- L'éditeur audio/SRT sauve via Vite local, pas en prod.
Bloc principal 3 : Zustand
Ce qu'il faut comprendre
Zustand contient l'état durable.
Il ne contient pas :
- la velocity joueur ;
- les raycasts ;
- les positions frame par frame ;
- les vecteurs temporaires ;
- l'état interne d'un grab.
Navigation simple
Ouvrir dans cet ordre :
src/managers/stores/useGameStore.tssrc/components/ui/debug/GameStateDebugPanel.tsxsrc/components/three/gameplay/RepairGame.tsxsrc/managers/stores/useSettingsStore.tssrc/managers/stores/useSubtitleStore.ts
Game store
Main states :
intro
bike
pylone
ferme
outro
Mission steps :
locked
waiting
inspected
fragmented
scanning
repairing
reassembling
done
Actions importantes :
setMissionStepcompleteMissionadvanceGameStaterewindGameStateresetGame
Phrase simple :
Le debug panel et le vrai gameplay utilisent la même source de vérité. Ce n'est pas un debug state séparé.
Settings store
Gère :
- menu ouvert/fermé ;
- volumes ;
- sous-titres ;
- langue.
Subtitle store
Gère seulement :
- le sous-titre actif ;
- clear/set.
Phrase simple :
Le store de sous-titres est volontairement petit parce que le timing reste piloté par l'audio.
Les pièges à ne pas rater
Si on te pose une question précise, réponds vrai.
| Sujet | Réponse honnête |
|---|---|
| Lock mouvement réparation | Les étapes repair actives bloquent le déplacement via le hook dédié. |
| Old debug flags | noMusic, noMap, noDialogues, etc. ne sont plus branchés. |
| Player physics | Le joueur n'est pas Rapier, il utilise capsule + octree. |
| Collision map | L'octree vient de nodes dédiés, actuellement terrain. |
| Editor save | Ce sont des endpoints Vite dev, pas une API de prod. |
| Cinematics | GameCinematics est monté seulement pendant outro dans World. |
| Hand tracking depth | Le z MediaPipe est relatif, pas une vraie profondeur monde stable. |
Si l'évaluateur ouvre un fichier au hasard
| Fichier | Ce qu'il faut dire |
|---|---|
World.tsx |
Compositeur de scène. Gère game/debug, loading gates, player spawn. |
useWorldSceneLoading.ts |
Évite de spawn le joueur avant map + collision + stage. |
GameMap.tsx |
Charge map JSON, modèles, fallback cubes, signale quand les nodes sont prêts. |
GameMapCollision.tsx |
Construit l'octree joueur depuis des nodes de collision dédiés. |
PlayerController.tsx |
Inputs, pointer lock, mouvement, jump, interaction, collision capsule. |
InteractionManager.ts |
État courant des interactions, pas dans Zustand car frame-adjacent. |
RepairGame.tsx |
Orchestrateur de steps repair, data-driven par mission. |
RepairRepairingStep.tsx |
Validation gameplay : pièces, snap, dépôt, install target. |
repairMissions.ts |
Config des missions, évite de hardcoder chaque mission dans le composant. |
AudioManager.ts |
Manager impératif pour musique, SFX, dialogue, pooling, volumes. |
playDialogue.ts |
Queue dialogue + synchronisation sous-titre. |
useGameStore.ts |
Source de vérité progression. |
GameStateDebugPanel.tsx |
Outil debug qui manipule le même store que le jeu. |
HandTrackingProvider.tsx |
Active la webcam seulement quand utile. |
GrabbableObject.tsx |
Grab souris/main, Rapier body, snap target. |
EditorControls.tsx |
Panneau éditeur, pas runtime joueur. |
Démo live avec Chrome DevTools
Cette partie correspond à :
Session de test / démo live via les devtools, commentée
Le but n'est pas de tout montrer. Le but est de montrer que tu sais observer l'application proprement.
Préparation
Lancer le front :
npm run dev
Ouvrir :
http://localhost:5173/?debug
Si Vite affiche un autre port, utiliser ce port.
Ouvrir DevTools :
- macOS :
Cmd + Option + I - Windows/Linux :
Ctrl + Shift + I
Disposition conseillée :
- app à gauche ;
- DevTools docké à droite ;
- onglets prêts :
Console,Network,Sources,Application.
Étape 1 : Console
Objectif :
- vérifier que l'app ne crashe pas ;
- surveiller les erreurs quand on interagit.
À faire :
- Ouvrir
Console. - Reload la page.
- Vérifier s'il y a des erreurs rouges.
- Garder la console ouverte pendant la démo.
Phrase simple :
Je commence par la console pour vérifier que la démo ne cache pas une erreur runtime.
Étape 2 : Network
Objectif :
- montrer que les assets sont chargés ;
- vérifier map, modèles, sons, vidéos.
À faire :
- Ouvrir
Network. - Cocher
Disable cache. - Reload.
- Filtrer :
map.jsonmodel.gltf.webm.mp3.srt
Ce que tu peux expliquer :
map.jsondécrit la scène ;model.gltfcharge les assets 3D ;.webmsert aux prompts in-game ;.mp3sert à musique/dialogues/SFX ;.srtsert aux sous-titres.
Phrase simple :
Network me permet de vérifier que la feature n'est pas juste du code React, elle dépend aussi d'assets runtime.
Étape 3 : Sources pour suivre le repair game
Objectif :
- montrer une transition de step ;
- prouver que
RepairGameécrit dans Zustand.
À faire :
- Ouvrir
Sources. Cmd + PouCtrl + P.- Chercher
RepairGame.tsx. - Mettre un breakpoint sur un appel à
setMissionStep. - Chercher
useGameStore.ts. - Mettre un breakpoint dans
setMissionStepoucompleteMission.
Dans l'app :
- Activer
?debug. - Dans lil-gui, mettre
Scene = Physics. - Garder
Camera Mode = Player. - Dans le debug overlay, passer
Main state = Bike. - Mettre le sub-state sur
waitingsi besoin. - Interagir avec l'objet ou avancer les steps via le debug panel.
Quand le breakpoint pause :
- regarder
mission; - regarder
step; - expliquer la transition.
Phrase simple :
Là on voit que le composant ne change pas juste son état local. Il déclenche une transition dans le store global.
Étape 4 : Sources pour suivre l'audio
Objectif :
- montrer qu'un son passe par
AudioManager.
À faire :
- Ouvrir
AudioManager.ts. - Mettre un breakpoint dans
playSound. - Déclencher un son, par exemple ouverture/fermeture de mallette.
- Inspecter :
pathvolumeoptions.category
Ensuite :
- Ouvrir le menu avec
Escape. - Changer le volume SFX.
- Mettre un breakpoint dans
setCategoryVolume. - Vérifier que la catégorie change.
Phrase simple :
Les sliders ne modifient pas directement un audio isolé. Ils mettent à jour un volume de catégorie dans le manager.
Étape 5 : Application
Objectif :
- montrer les données locales du navigateur.
À faire :
- Ouvrir
Application. - Regarder
Local Storage. - Chercher la clé de debug :
la-fabrik-debug-controls
Ce que ça montre :
- certains choix debug peuvent être persistés ;
- ce n'est pas la progression du jeu ;
- c'est juste du confort dev.
Étape 6 : Performance, optionnel
Objectif :
- montrer que tu sais diagnostiquer si ça rame.
À faire seulement si demandé :
- Ouvrir
Performance. - Record 5 secondes.
- Bouger dans la scène.
- Stop.
- Observer FPS, scripting, rendering.
Phrase simple :
Si la scène rame, je ne devine pas. Je regarde si le temps part dans le JS, le rendu, ou le chargement d'assets.
Démo recommandée en 5 minutes
1. Ouvrir la scène debug
http://localhost:5173/?debug
Dans lil-gui :
Scene = PhysicsCamera Mode = PlayerDebug Overlay = true
2. Montrer le store
Dans le debug panel :
- passer
Main state = Bike - passer le step à
waiting - avancer avec
Next step
Expliquer :
Le panel debug écrit dans le même store que le vrai gameplay.
3. Montrer le repair game
Faire défiler les steps :
waiting -> inspected -> fragmented -> scanning -> repairing
Montrer :
- objet de mission ;
- mallette ;
- modèle éclaté ;
- scan ;
- pièces à grab ;
- validation bloquée si mauvaise pièce.
4. Montrer l'audio
Ouvrir DevTools :
- breakpoint dans
AudioManager.playSound; - déclencher un son ;
- montrer
pathetcategory.
5. Montrer Zustand
Breakpoint dans useGameStore.
Expliquer :
Quand la mission avance, ce n'est pas chaque composant qui invente son état. La progression passe par des actions centralisées.
Questions probables et réponses simples
Pourquoi Zustand ?
Parce qu'on a besoin d'une source de vérité partagée entre UI, monde 3D, debug panel et gameplay.
Pourquoi pas tout dans Zustand ?
Parce que certaines valeurs changent trop souvent. Les positions, vitesses, raycasts et animations frame par frame restent dans des refs ou dans les composants R3F.
Pourquoi un AudioManager ?
Parce que l'audio navigateur est impératif. On doit gérer des HTMLAudioElement, des pools, une musique active et des volumes de catégories.
Pourquoi séparer octree et Rapier ?
Pour garder un player controller simple tout en utilisant Rapier pour les objets manipulables.
Pourquoi RepairGame est data-driven ?
Pour réutiliser le même flow sur plusieurs missions et garder les variations dans repairMissions.ts.
Qu'est-ce qui est incomplet ?
- pas de vrai
GameManagerglobal ; - editor save uniquement en dev ;
- hand tracking encore approximatif sur profondeur et smoothing.
Checklist avant la review
Commandes :
npm run format:check
npm run typecheck
npm run lint
npm run build
Pages à ouvrir :
//?debug/editor/docs/docs/code-review
Fichiers à avoir en tête :
src/world/World.tsxsrc/components/three/gameplay/RepairGame.tsxsrc/components/three/gameplay/RepairRepairingStep.tsxsrc/data/gameplay/repairMissions.tssrc/managers/AudioManager.tssrc/managers/stores/useGameStore.tssrc/components/ui/debug/GameStateDebugPanel.tsx
Réponses pièges à réviser :
- lock mouvement repair actif sur les étapes dédiées ;
- player pas Rapier ;
- save editor pas production ;
- old boot flags non branchés.
Mini plan de révision
Session 1 : 20 minutes
Lire :
- cette fiche ;
docs/technical/repair-game.md;docs/technical/zustand.md.
Objectif :
- savoir expliquer le repair game et le store.
Session 2 : 20 minutes
Lire :
docs/technical/audio.md;docs/technical/interaction.md.
Objectif :
- savoir expliquer audio + interaction.
Session 3 : 20 minutes
Faire la démo :
- lancer
npm run dev; - ouvrir
/?debug; - ouvrir DevTools ;
- faire un breakpoint dans
RepairGame; - faire un breakpoint dans
AudioManager.
Objectif :
- ne pas découvrir DevTools le jour de la review.
Version ultra-courte à garder en tête
Si tu paniques, reviens à ça :
World monte la scène.
GameStageContent place les missions.
RepairGame orchestre les steps.
repairMissions fournit la data.
useGameStore garde la progression.
InteractionManager gère focus/trigger/grab.
AudioManager gère sons, musique, dialogues.
DevTools permet de suivre assets, state et appels runtime.