TP — XR Engineering & Debugging (VR) — Solar System Workbench
Développer une application VR structurée, instrumentée et déboguable, en appliquant des bonnes pratiques d’ingénierie Unity/XR.
Objectif
Ce TP (~7h) a pour objectif de vous apprendre à développer et maintenir une application VR avec :
- une architecture claire (séparation des responsabilités, dépendances explicites),
- une chaîne d’itération efficace (tester un maximum sans casque, builds debug),
- une observabilité minimale (overlay VR, logs structurés),
- des pratiques de debugging XR,
- une introduction au profiling VR (optionnel selon le temps restant).
L’application développée est un atelier VR de visualisation du système solaire : un modèle “tabletop” manipulable, une UI en world-space, des modes de vue, et des contrôles temporels.
Pré-requis
Compétences
- Bases C# / Unity (scènes, prefabs, scripts)
- Notions Git (commit, tag, push)
- Bases VR (confort, world-space UI) utiles mais pas indispensables
Outils
- Unity LTS (2022.3 ou 2023 LTS)
- Packages Unity :
- OpenXR
- XR Interaction Toolkit (XRI)
- Input System
- Un casque VR compatible OpenXR
Ressources
- PlanetData.cs (fournit les données et fonctions de calcul orbital)
Règles d’ingénierie (à respecter)
Dans ce TP, vous devez explicitement appliquer les règles suivantes :
Aucune dépendance implicite
- Pas de
FindObjectOfType, pas deGameObject.Find, pas de dépendance “magique”. - Les références nécessaires sont injectées via l’inspecteur ou via un bootstrapper.
- Pas de
Pas de logique métier dans
Update()par défautUpdate()est réservé aux besoins continus réellement nécessaires.- Préférer événements, actions, timers contrôlés.
Séparation des responsabilités
- Modèle de données / services (calculs, temps, conversion) ≠ vues (GameObjects) ≠ contrôleurs.
Debuggabilité
- Tout comportement critique doit produire des logs exploitables.
- Mise en place d’un DebugOverlay visible dans le casque.
Performance (optionnel)
- Interdiction d’allocations évitables par frame (strings/LINQ).
- Profiling obligatoire (preuve avant/après sur un point).
Scénario : Solar System VR Workbench
Vous développerez une application VR contenant :
- un “tabletop” du système solaire (échelle manipulable),
- des planètes positionnées à un instant t (les fonctions de calcul sont déjà fournies par
PlanetData), - un contrôle du temps (date + vitesse),
- des trajectoires activables/désactivables,
- un mode “focus planète” (centrage/inspection),
- une UI VR (world-space) pour piloter l’application,
- un overlay debug (FPS, état, derniers événements, warnings).
Organisation du TP
Le TP est structuré en 5 étapes principales et 2 extensions optionnelles.
Obligatoire :
1 — Boot XR
2 — Architecture
3 — Événements / simulation
4 — Interactions VR
5 — UI + Debug overlay
Optionnel (si temps restant) :
6 — Debugging XR guidé
7 — Optimisation de performance
Organisation du rendu (Git)
Vous devez livrer :
- un repo Git (lien),
- des tags Git à chaque étape (v1.0, v1.1, … v2.0),
- un court compte-rendu (README ou PDF) :
- captures d’écran (dans le casque ou miroir),
- fonctionnalités implémentées,
- schéma d’architecture (simple, lisible),
- une “debug story” (si vous avez traité l’étape 6),
- une “perf story” (si vous avez traité l’étape 7).
Rappel tags
À chaque étape :
git add .git commit -m "…"git tag -a v1.x -m "…"git push origin maingit push origin --tags
Concepts utiles pour ce TP
Avant de commencer le TP, voici quelques notions importantes que nous allons utiliser pendant toute la séance. Elles sont très courantes dans les projets Unity et encore plus importantes en XR.
1 — Logique métier
La logique métier correspond aux règles qui définissent comment fonctionne votre application, indépendamment de l’affichage.
Exemples dans ce TP :
Logique métier :
- calculer la position d’une planète à une date donnée
- changer la vitesse du temps
- activer ou désactiver les trajectoires
- changer l’échelle du système solaire
Affichage (pas logique métier) :
- déplacer un GameObject
- changer un texte dans l’UI
- afficher un panneau
Mauvais exemple
void Update()
{
planet.transform.position = PlanetData.ComputePosition(DateTime.Now);
}
Le calcul et l’affichage sont mélangés.
Bon exemple
DateTime currentTime;
void OnTimeChanged(DateTime t)
{
currentTime = t;
UpdateView();
}
La logique décide quand on met à jour, la vue décide comment on affiche.
2 — Séparation des responsabilités
Dans un projet Unity, il est très facile de mettre beaucoup de choses dans un seul script. Cela fonctionne pour de petits projets, mais devient rapidement difficile à maintenir.
On sépare donc les rôles.
Structure simple utilisée dans ce TP
Data / Model ←→ Services ←→ Controllers ←→ Views
Model (données)
Contient uniquement des données.
Exemple :
TimeModel
- currentTime
- timeSpeed
Un modèle ne dépend normalement pas de Unity.
Services
Contiennent des calculs ou des règles.
Exemple :
PlanetEphemerisService
- computePosition()
Controllers
Coordonnent l’application.
Ils reçoivent des événements et mettent à jour les vues.
Exemple :
SolarSystemController
Views
Objets Unity visibles.
Exemples :
- PlanetView
- OrbitRenderer
- UI panels
Résumé
- Model → stocker l’état
- Service → calculer
- Controller → décider quoi faire
- View → afficher
3 — Bootstrapper
Le problème
Dans beaucoup de projets Unity on trouve :
- FindObjectOfType
- GameObject.Find
- des Singletons utilisés partout
Ces méthodes fonctionnent mais rendent le projet fragile.
Solution simple
Créer un objet qui initialise l’application.
C’est ce qu’on appelle un Bootstrapper.
Exemple :
AppBootstrapper
Son rôle :
- créer les services
- connecter les objets
- initialiser les données
Exemple simple
public class AppBootstrapper : MonoBehaviour
{
public SolarSystemController controller;
void Start()
{
var timeModel = new TimeModel();
controller.Init(timeModel);
}
}
Les dépendances deviennent visibles et contrôlées.
4 — Événements
Les événements permettent à différents objets de communiquer sans être fortement couplés.
Sans événements :
UI → Manager → Controller → Camera
Tout dépend de tout.
Avec événements :
UI → Event → Objets intéressés
Chaque système reste indépendant.
Exemple
public event Action<DateTime> OnTimeChanged;
Quand la date change :
OnTimeChanged?.Invoke(newTime);
Un autre script peut écouter cet événement :
void Start()
{
planetManager.OnTimeChanged += UpdatePosition;
}
5 — Actions
Une Action est simplement une fonction stockée dans une variable.
Exemple :
Action<DateTime> callback;
Dans Unity on les utilise pour :
- événements
- callbacks
- interactions UI
6 — Gestion de l’état de la simulation
Dans cette application, la variable centrale n’est pas vraiment un “timer” technique. Ce qui compte est simplement la date de la simulation.
Autrement dit, on manipule un état global :
Date simulée → positions des planètes → affichage
Lorsque cette date change :
- les positions des planètes doivent être recalculées
- certaines visualisations doivent être mises à jour
- l’interface peut afficher la nouvelle valeur
Plutôt que de recalculer en permanence dans Update(), on déclenche un événement uniquement quand l’état change.
Mauvaise approche
void Update()
{
UpdatePlanets(currentDate);
}
Chaque objet vérifie en permanence si quelque chose a changé.
Approche recommandée
On utilise un événement qui signale un changement d’état.
public event Action<DateTime> OnTimeChanged;
Quand la date change :
UI → PlanetManager → événement → objets intéressés
Exemple :
void Start()
{
PlanetManager.current.OnTimeChanged += UpdatePosition;
}
Cela permet :
- moins de calculs inutiles
- un code plus lisible
- un débogage plus simple
- de meilleures performances en VR
7 — Pourquoi c’est encore plus important en XR
En VR et XR :
- les builds sont plus longs
- les bugs sont plus difficiles à diagnostiquer
- les performances sont critiques
Une architecture claire permet :
- de tester certaines parties sans casque
- de comprendre rapidement un bug
- de modifier une interaction sans casser tout le projet
8 - ScriptableObject
Un ScriptableObject est un asset Unity utilisé pour stocker des paramètres éditables.
Contrairement à un MonoBehaviour, il n’est pas attaché à un GameObject.
Exemple dans ce TP :
[CreateAssetMenu(menuName = "XR/Solar System Config")]
public class SolarSystemConfig : ScriptableObject
{
public float distanceScale;
public float planetSizeScale;
public bool showOrbits;
}
On crée ensuite l’asset dans Unity :
Assets → Create → XR → Solar System Config
Puis on l’assigne dans l’inspecteur.
Intérêt :
- configuration centralisée
- modifiable sans changer le code
- partageable entre scènes.
Ce que vous devez retenir
Pendant ce TP, essayez de toujours vous poser ces questions :
- Est-ce que ce script fait trop de choses ?
- Est-ce que ce code dépend inutilement d’un autre objet ?
- Est-ce que je peux comprendre ce système rapidement ?
- Est-ce que je pourrais déboguer ça facilement sur un casque VR ?
Si la réponse est “non”, il faut probablement simplifier ou mieux séparer les responsabilités.
Étape 1 — Boot XR + scène minimale (Tag : v1.0) — ~45 min
Objectifs
- Avoir une scène VR qui tourne et un pipeline de build déboguable.
À faire
- Créer le projet Unity.
- Installer et activer :
- OpenXR
- XR Interaction Toolkit
- Vous pouvez ajouter les Starter Assets (pour avoir un XR Rig complet), et le XR Simulator (pour tester sans casque) depuis les samples du package.
- Input System
- Configurer le projet :
- Activer les plugin-provider nécessaires pour votre casque (OpenXR, Oculus, etc.) dans Player Settings / XR Plug-in Management.
- Ajouter un rig VR (XRI) :
- XR Origin
- XR Interaction Manager
- Créer une scène
Boot:- sol + repère (axes X/Y/Z par exemple pour voir l’origine du monde dans la scène)
- un cube interactable (grab) pour valider XRI
- Configurer le build debug :
- Development Build
- Script Debugging
- (si possible) Autoconnect Profiler
Rendu attendu
- Capture (mode miroir dans le casque ou XR simulator) montrant le cube grab en VR
- Repo taggé
v1.0-boot(n’noubliez pas le .gitignore)
Étape 2 — Architecture : modèles, services, contrôleurs
Tag : v1.1-architecture — ~1h15
Objectif
Avant d’ajouter de la VR, des interactions et de l’UI, nous allons structurer correctement l’application.
Dans les projets XR, les problèmes viennent souvent de :
- scripts qui se référencent dans tous les sens
- logique mélangée avec l’affichage
- dépendances invisibles
- code impossible à tester sans casque.
Nous allons donc construire une architecture simple :
Bootstrapper
↓
Models / Config
↓
Services
↓
Controllers
↓
Views (Unity)
1 — Organisation des dossiers
Créer les dossiers suivants :
Scripts
├── Bootstrap
├── Models
├── Services
├── Controllers
├── Views
└── Config
But :
| Dossier | Rôle |
|---|---|
| Bootstrap | point d’entrée |
| Models | état de la simulation |
| Services | calculs |
| Controllers | coordination |
| Views | GameObjects Unity |
| Config | paramètres éditables |
2 — Le modèle du temps
Dans notre simulation, la variable centrale est le temps simulé.
Créer :
Scripts/Models/TimeModel.cs
using System;
public class TimeModel
{
public DateTime CurrentTime { get; private set; }
public float TimeScale { get; private set; } = 1f;
public bool IsPlaying { get; private set; } = true;
public event Action<DateTime> OnTimeChanged;
public void SetTime(DateTime t)
{
CurrentTime = t;
OnTimeChanged?.Invoke(CurrentTime);
}
public void SetScale(float scale)
{
TimeScale = scale;
}
public void Play()
{
IsPlaying = true;
}
public void Pause()
{
IsPlaying = false;
}
}
Important :
- ce script ne dépend pas de Unity
- il ne contient que l’état et les événements.
3 — Configuration du système solaire
Certains paramètres doivent être éditables dans Unity.
Créer :
Scripts/Config/SolarSystemConfig.cs
using UnityEngine;
[CreateAssetMenu(menuName = "XR/Solar System Config")]
public class SolarSystemConfig : ScriptableObject
{
public float distanceScale = 0.000001f;
public float planetSizeScale = 0.01f;
public bool showOrbits = true;
}
Créer ensuite l’asset :
Assets → Create → XR → Solar System Config
4 — Utilisation des données astronomiques fournies
Le TP fournit déjà une classe :
PlanetData.cs
Elle contient :
- les paramètres orbitaux
- les calculs d’orbite
- la fonction :
PlanetData.GetPlanetPosition(PlanetData.Planet planet, DateTime time)
⚠️ Cette classe est la source officielle des positions des planètes. Les étudiants ne doivent pas modifier ce fichier.
5 — Service d’accès aux positions planétaires
Même si la fonction existe déjà, on passe par un service.
Pourquoi ?
- garder une architecture propre
- pouvoir remplacer l’implémentation plus tard
- faciliter les tests.
Créer :
Scripts/Services/IPlanetEphemerisService.cs
using System;
using UnityEngine;
public interface IPlanetEphemerisService
{
Vector3 GetPlanetPosition(PlanetData.Planet planet, DateTime date);
}
Implémentation :
Scripts/Services/PlanetEphemerisService.cs
using System;
using UnityEngine;
public class PlanetEphemerisService : IPlanetEphemerisService
{
public Vector3 GetPlanetPosition(PlanetData.Planet planet, DateTime date)
{
return PlanetData.GetPlanetPosition(planet, date);
}
}
Ce service agit comme une couche d’abstraction.
6 — Vue planète
Créer :
Scripts/Views/PlanetView.cs
using UnityEngine;
public class PlanetView : MonoBehaviour
{
public PlanetData.Planet planet;
public void SetPosition(Vector3 pos)
{
transform.localPosition = pos;
}
}
Rôle :
- représenter une planète dans la scène
- appliquer les positions calculées.
7 — Controller du système solaire
Le controller relie :
- le modèle
- le service
- les vues.
Créer :
Scripts/Controllers/PlanetSystemController.cs
using System;
using UnityEngine;
public class PlanetSystemController
{
TimeModel timeModel;
IPlanetEphemerisService ephemeris;
PlanetView[] planets;
public PlanetSystemController(
TimeModel timeModel,
IPlanetEphemerisService ephemeris,
PlanetView[] planets)
{
this.timeModel = timeModel;
this.ephemeris = ephemeris;
this.planets = planets;
timeModel.OnTimeChanged += UpdatePlanets;
}
void UpdatePlanets(DateTime time)
{
Debug.Log("[TIME] Updating planets " + time);
foreach (var planet in planets)
{
Vector3 pos =
ephemeris.GetPlanetPosition(planet.planet, time);
planet.SetPosition(pos);
}
}
}
Responsabilités :
| Élément | Rôle |
|---|---|
| Model | état de la simulation |
| Service | calcul des positions |
| Controller | coordination |
| View | affichage. |
8 — Bootstrapper
Créer :
Scripts/Bootstrap/AppBootstrapper.cs
using UnityEngine;
using System;
public class AppBootstrapper : MonoBehaviour
{
public SolarSystemConfig config;
public PlanetView[] planets;
TimeModel timeModel;
PlanetSystemController controller;
void Start()
{
Debug.Log("[BOOT] Initializing application");
timeModel = new TimeModel();
var ephemeris = new PlanetEphemerisService();
controller = new PlanetSystemController(
timeModel,
ephemeris,
planets
);
timeModel.SetTime(DateTime.Now);
}
}
Rôle :
- créer les objets principaux
- connecter les dépendances
- démarrer la simulation.
C’est ce qu’on appelle le composition root.
9 — Mise en place dans la scène
Créer un GameObject :
App
Ajouter :
AppBootstrapper
Dans l’inspecteur :
- assigner
SolarSystemConfig - assigner les
PlanetView.
Structure possible :
App
SolarSystem
├ Sun
├ Mercury
├ Venus
├ Earth
├ Mars
├ Jupiter
├ Saturn
├ Uranus
└ Neptune
Chaque planète possède :
PlanetView
10 — Test rapide
Lancer la scène.
Résultat attendu :
- les planètes apparaissent
- elles sont positionnées correctement
- log
[BOOT].
Si rien ne se passe :
Checklist :
- les PlanetView sont assignées
- le bootstrapper est actif
- la scène est sauvegardée.
Si vous suivez bien les étapes, vous devriez constater un petit bug. Modifiez le service pour le corriger.
11 — Vérification de l’architecture
Votre code doit respecter :
- pas de
FindObjectOfType - pas de
GameObject.Find - dépendances visibles
- modèle indépendant de Unity
- logique séparée de l’affichage.
12 — Schéma de l’architecture
Résultat attendu
À la fin de cette étape :
- l’application est structurée
- les planètes utilisent
PlanetData - les dépendances sont explicites
- la simulation est pilotée par un modèle.
Tag Git :
v1.1-architecture
Étape 3 — Positionnement des planètes + événements
Tag : v1.2-events — ~1h15
Objectif
Mettre en place le flux principal de la simulation :
- le temps change,
- un événement est déclenché,
- le contrôleur met à jour les planètes.
On évite volontairement de faire tourner toute la logique dans Update().
Flux attendu :
TimeController
↓
TimeModel
↓
OnTimeChanged
↓
PlanetSystemController
↓
PlanetView
On a déjà créé la plupart de ces éléments dans l’étape précédente, il s’agit maintenant de les connecter correctement avec des événements.
Responsabilités :
| Élément | Rôle |
|---|---|
| Model | état du temps |
| Service | calcul des positions |
| Controller | applique les positions |
| View | déplace les objets |
1 — Déclencher via un événement
Ceci a déjà été implémenté dans l’étape précédente, mais il est important de comprendre comment cela fonctionne.
Dans TimeModel :
public void SetTime(DateTime t)
{
CurrentTime = t;
OnTimeChanged?.Invoke(CurrentTime);
}
Dans le controller :
timeModel.OnTimeChanged += UpdatePlanets;
Ainsi les planètes se mettent à jour uniquement quand le temps change.
2 — Faire avancer le temps
Créer un petit composant Unity :
TimeController
public class TimeController : MonoBehaviour
{
public float secondsPerDay = 1f;
TimeModel model;
DateTime current;
public void Init(TimeModel m)
{
model = m;
current = DateTime.Now;
model.SetTime(current);
}
void Update()
{
if (!model.IsPlaying) return;
current = current.AddDays(Time.deltaTime * secondsPerDay);
model.SetTime(current);
}
}
Dans AppBootstrapper :
timeController.Init(timeModel);
3 — Vérification
Quand la scène démarre :
Console :
[BOOT] Initializing application
[TIME] Updating planets ...
Et les planètes doivent se déplacer.
Checklist :
- aucun
FindObjectOfType - aucun
GameObject.Find - la mise à jour passe par OnTimeChanged
- les
PlanetViewsont assignées.
(Optionnel) Trajectoires
Pour afficher une orbite :
- ajouter un LineRenderer
- calculer plusieurs positions
- ne pas recalculer chaque frame.
Principe :
for (int i = 0; i < samples; i++)
{
var t = start.AddDays(i * 10);
points[i] = ephemeris.GetPlanetPosition(planet, t);
}
Résultat attendu
À la fin :
- la simulation est pilotée par
TimeModel - les planètes suivent correctement le temps
- la mise à jour passe par l’événement
OnTimeChanged - l’architecture reste claire et testable.
Dans le rapport, vous devez également inclure :
- un schéma simple du flux
TimeModel → Controller → PlanetView - une courte explication du fonctionnement
- une capture de l’application.
Tag :
v1.2-events
Étape 4 — Interactions VR : grab / focus / échelle
Tag : v1.3-vr-interactions — ~1h30
Objectif
Dans cette étape, vous allez transformer votre simulation en véritable objet manipulable en VR.
L’utilisateur doit pouvoir :
- déplacer le système solaire,
- modifier son échelle,
- sélectionner une planète pour obtenir des informations.
Le but n’est pas seulement de faire fonctionner ces interactions, mais de les intégrer proprement dans l’architecture du projet.
Les interactions XR doivent déclencher des intentions, qui sont ensuite appliquées par un système clair (controller / manager), et non manipuler directement tous les objets de la scène.
Flux recommandé :
XR Interaction
↓
Script d’interaction
↓
Controller
↓
Views
1 — Préparer un “tabletop” manipulable
Pour faciliter les interactions, vous devez organiser la scène autour d’un objet racine manipulable.
Créer une structure proche de :
SolarSystemRoot
├ SolarSystem
│ ├ Sun
│ ├ Mercury
│ ├ Venus
│ └ ...
└ Handle
SolarSystemRoot devient l’objet que vous allez :
- déplacer
- faire tourner
- mettre à l’échelle.
Toutes les planètes doivent être enfants de cet objet.
Pourquoi ?
- cela simplifie énormément les interactions,
- cela évite de modifier chaque planète individuellement,
- cela permet de garder un point central pour les contrôleurs.
2 — Interaction Grab
Objectif
Permettre à l’utilisateur de prendre le système solaire et le déplacer dans l’espace.
À faire
Créer un objet servant de poignée de manipulation.
Ajouter un interactable XR permettant :
- de saisir l’objet avec le contrôleur,
- de déplacer l’ensemble du système.
Quelques éléments nécessaires :
- collider
- rigidbody (souvent kinematic)
- composant d’interaction XR.
Test attendu :
- l’utilisateur peut attraper l’objet,
- le déplacer,
- le relâcher.
Bonnes pratiques
- ne pas attacher cette interaction directement aux planètes,
- garder un objet dédié (handle),
- ajouter des logs utiles :
[XR] Table grabbed
[XR] Table released
3 — Contrôle de l’échelle
Objectif
Permettre à l’utilisateur de zoomer / dézoomer le système solaire.
L’échelle doit :
- rester dans des limites raisonnables,
- être centralisée dans un seul script,
- produire des logs utiles.
À implémenter
Créer un système responsable de :
- recevoir une valeur de scale,
- la limiter si nécessaire,
- appliquer la transformation à
SolarSystemRoot.
Vous pouvez par exemple créer un composant dédié :
ScaleController
Ce composant pourrait exposer une méthode du type :
SetScale(float value)
C’est ce composant qui applique réellement l’échelle.
Interaction utilisateur
Vous êtes libres de choisir comment modifier l’échelle :
- slider UI en VR
- bouton +
- rotation d’un knob
- geste à deux mains (bonus)
Mais dans tous les cas :
- l’interaction ne doit pas manipuler directement le transform
- elle appelle un contrôleur.
Logs attendus
Exemples :
[INPUT] Scale requested
[XR] Scale applied
[WARN] Scale clamped
4 — Sélection d’une planète
Objectif
Permettre à l’utilisateur de pointer une planète avec un rayon XR et de la sélectionner.
Lorsqu’une planète est sélectionnée :
- un événement doit être déclenché,
- le système doit savoir quelle planète est active.
Mise en place
Chaque planète doit pouvoir :
- être détectée par un ray interactor,
- signaler sa sélection.
Vous pouvez par exemple créer un script du type :
PlanetSelectable
Ce script peut :
- référencer la
PlanetView, - déclencher un événement ou appeler un système central.
Important : le script de sélection ne doit pas contenir toute la logique de focus ou d’UI.
Il signale seulement une planète a été sélectionnée.
5 — Mode Focus
Objectif
Quand une planète est sélectionnée :
- la planète doit être “mise en avant” (ex. centrée, agrandie, etc.),
- un panneau d’information apparaît.
À implémenter
Créer un système responsable du focus.
Par exemple :
FocusController
Responsabilités possibles :
- déplacer l’origine XR ou la caméra,
- activer un panneau d’information,
- enregistrer la planète active.
Le panneau peut afficher par exemple :
- nom de la planète
- distance au soleil
- période orbitale
- temps simulé actuel.
Le contenu exact est libre.
6 — Organisation recommandée
Une organisation possible des responsabilités :
| Élément | Rôle |
|---|---|
| XR Interactable | capter l’interaction |
| Script d’interaction | traduire l’action utilisateur |
| Controller | appliquer la logique |
| View | afficher le résultat |
Exemple de flux :
Ray Interactor
↓
PlanetSelectable
↓
PlanetSelectionSystem
↓
FocusController
↓
UI
7 — Vérifications
Avant de passer à l’étape suivante, vérifiez :
Grab
- le système solaire peut être déplacé
- le mouvement est stable
- des logs apparaissent.
Scale
- l’échelle peut changer
- les limites sont respectées
- les transformations restent cohérentes.
Focus
- la sélection fonctionne avec le rayon
- la bonne planète est détectée
- un feedback visuel apparaît.
Résultat attendu
À la fin de cette étape, l’utilisateur peut :
- manipuler le système solaire,
- modifier son échelle,
- sélectionner une planète,
- voir des informations associées.
L’architecture doit rester :
- lisible,
- découplée,
- déboguable.
Tag Git :
v1.3-vr-interactions
Étape 5 — UI world-space + instrumentation (Tag : v1.4) — ~1h00
Objectifs
Rendre l’application pilotable et déboguable dans le casque.
À faire
A) UI world-space
Créer un panneau VR permettant au minimum :
- Date (ou scrub)
- Play/pause + vitesse (x1, x10, x100…)
- Toggle trajectoires
- Reset viewpoint / reset scale
Le panneau UI peut être un Canvas world-space interactif avec le XR Interaction Toolkit.
B) DebugOverlay (obligatoire)
Créer un overlay discret affichant :
- FPS / frame time (approx)
- Date actuelle + vitesse
- Dernière action utilisateur
- Warnings clés (scale hors bornes, perf suspecte, event spam)
Le DebugOverlay peut être un simple panneau TextMeshPro mis à jour périodiquement.
Unity permet de récupérer tous les logs (Debug.Log, warnings, erreurs) via :
Application.logMessageReceived
Vous pouvez utiliser ce callback pour :
- récupérer les messages importants
- garder les derniers logs
- les afficher dans l’overlay.
Exemple
using UnityEngine;
using TMPro;
using System.Collections.Generic;
public class DebugOverlay : MonoBehaviour
{
public TextMeshProUGUI text;
Queue<string> lines = new Queue<string>();
public int maxLines = 15;
void OnEnable()
{
Application.logMessageReceived += HandleLog;
}
void OnDisable()
{
Application.logMessageReceived -= HandleLog;
}
void HandleLog(string logString, string stackTrace, LogType type)
{
lines.Enqueue(logString);
while (lines.Count > maxLines)
lines.Dequeue();
text.text = string.Join("\n", lines);
}
}
C) Logs structurés
Tous les événements critiques doivent loguer au format :
[TIME] …[INPUT] …[XR] …[PERF] …
Rendu attendu
- Capture du panneau UI + overlay debug visibles en VR
- Tag
v1.4-ui-debug
Étape 6 — Debugging XR guidé (Tag : v1.5) — (Optionnel)
Objectifs
Apprendre une méthode de debug XR reproductible.
Travail demandé
Vous devez traiter au moins 2 tickets parmi les 3 suivants (selon starter fourni par l’enseignant) :
- Ticket A — Input qui marche en Editor mais pas sur casque
- Ticket B — Après reload scène, les events se déclenchent en double
- Ticket C — Échelle/repères incohérents : objets “loin” ou “immenses”
Pour chaque ticket :
- fournir un repro minimal
- décrire la méthode (instrumentation → hypothèse → test → fix)
- ajouter un test simple de non-régression (ex. log + guard + assert)
Rendu attendu
- Section “Debug story” dans README
- Tag
v1.5-debug-tickets
Étape 7 — Performance XR (Tag : v2.0) — (Optionnel)
Objectifs
Identifier et corriger un problème perf (CPU/GPU/GC) typique VR.
À faire
- Mesurer (Profiler) :
- CPU main thread
- GC alloc
- scripts en Update
- Choisir un goulot et l’améliorer :
- caching trajectoires
- pooling
- réduction fréquence d’update
- suppression d’allocations
- Prouver l’amélioration :
- capture du profiler avant/après
- décrire le changement (2–5 lignes)
Rendu attendu
- Section “Perf story” dans README
- Tag
v2.0-final
Bonus (facultatifs)
- Rotation propre des planètes (axiale + orbitale)
- Skybox / voûte céleste
- Mode “surface de planète” (point de vue, constellations)
- Téléportation + confort (snap turn, vignette)
- Mini “debug console” in-VR avec filtres par catégories
Critères d’évaluation (indicatif)
- Architecture & propreté — 40%
- Interactions VR — 30%
- Observabilité — 20%
Bonus :
- Debugging (tickets + méthode)
- Performance (mesure + amélioration)
Checklist rapide (avant rendu)
- Repo public/privé accessible, tags présents
- App fonctionne sur casque
- UI world-space OK
- DebugOverlay OK
- README avec captures + schémas
- Pas de
FindObjectOfType/GameObject.Finddans le code final