Timelines Unity — architecture et création de séquences
Rôle des Timelines Unity
Les Timelines sont des séquences audiovisuelles Unity basées sur PlayableDirector. Elles servent à présenter une animation de résultats, de première ouverture ou de lancement produit tout en bloquant temporairement l'interaction normale avec l'UI.
Elles sont différentes des Tutoriels : un tutoriel guide activement l'utilisateur dans l'interface et attend souvent ses clics; une Timeline joue une séquence Unity et appelle un callback lorsqu'elle se termine.
flowchart LR
TRIGGER[Controller / UI / State] --> TL[Timeline subclass]
TL --> BASE[TimelineBase.Play]
BASE --> CAM[Switch cameras + block raycasts]
BASE --> DIR[PlayableDirector.Play]
DIR --> STOP[PlayableDirector.stopped]
STOP --> RESTORE[TimelineBase.Stop]
RESTORE --> CB[onFinish]
TimelineBase — contrat commun
Le socle est Assets/Src/Game/Timeline/TimelineBase.cs.
TimelineBase gère :
uiCamera,offscreenCameraet éventuellementcamera3D;- activation de
scene3dRoot; - changement de
Canvas.worldCamera; - adaptation du
screenTransformselon le ratio d'écran; UICanvasGroup.blocksRaycasts = falsependant la séquence;PlayableDirector.Play();- abonnement à
director.stopped; - restauration de l'UI dans
Stop(); - appel du callback
onFinish.
Ce que Play() fait réellement
- active
scene3dRoot; - désactive la caméra UI;
- active la caméra offscreen;
- redirige le Canvas vers cette caméra;
- copie le viewport de la caméra UI;
- calcule le scale horizontal entre les valeurs 4:3 et 16:9;
- bloque les raycasts UI;
- lance le
PlayableDirector; - s'abonne à
director.stopped.
Lorsque le director s'arrête, OnPlayFinish() se désabonne, appelle Stop(), puis déclenche le callback.
Timelines communes existantes
| Classe | Responsabilité |
|---|---|
CreateProductTimeline |
Séquence générique après création/lancement d'un premier objet métier. |
YearResultsTimeline |
Animation de résultats annuelle By Year avec graph, profit/perte et anti-bankruptcy. |
FirstModuleTimeline |
Première ouverture d'un module By Module. |
ModuleResultsTimeline |
Animation de correction/résultats d'un module summative. |
Timelines game-specific
Les jeux peuvent spécialiser le socle :
Assets/GameSpecific/mk1/Src/Timeline/TimelinesMk1.cs— extension deYearResultsTimeline+ lancement produit;Assets/GameSpecific/ent1/Src/Timeline/LaunchProductTimeline.cs— séquences lancement/résultats ENT1;- les assets
.playablesont présents sous les Resources des jeux concernés.
Une Timeline qui dépend d'une structure game-specific doit rester dans le dossier du jeu, pas dans le socle commun.
Assets .playable
Une Timeline Unity est composée de deux parties :
- le script C# qui choisit le director, prépare les données et appelle
Play(); - l'asset .playable qui contient les tracks, clips et animations.
Changer uniquement le C# sans vérifier l'asset peut laisser des bindings cassés. Changer uniquement le .playable nécessite aussi de vérifier que le prefab/scene fournit toutes les références utilisées par les tracks.
Créer une Timeline de zéro — procédure complète
1. Vérifier qu'une Timeline est le bon outil
Utiliser une Timeline si le besoin est principalement une séquence visuelle/animée. Pour guider un utilisateur vers un bouton, attendre un clic réel ou surligner une zone UI, utiliser le système Tutoriels.
2. Créer l'asset Timeline
Dans Unity :
- créer un asset Timeline
.playabledans le dossier Resources approprié; - ajouter les tracks nécessaires;
- configurer clips, animations et bindings;
- tester la séquence directement dans l'éditeur.
3. Créer la classe C#
public class MyTimeline : TimelineBase
{
public static MyTimeline Instance;
[SerializeField]
private PlayableDirector director;
private void Awake()
{
Instance = this;
}
public void PlaySequence(
string message,
Action onFinish
)
{
character3dText.text = message;
Play(onFinish, director);
}
}
4. Brancher les références du prefab/scene
Le composant hérité de TimelineBase doit avoir des références valides vers :
uiCamera;offscreenCamera;scene3dRoot;screenTransform;UICanvasGroup;character3dTextsi la Timeline affiche une bulle;- le ou les
PlayableDirector.
5. Choisir le bon point de déclenchement
Exemples réels :
DecisionsControlByModuledéclencheFirstModuleTimeline;- le même contrôle déclenche
ModuleResultsTimelinelorsque le module summative est généré; StudentStateet plusieurs TeamControllers déclenchentYearResultsTimeline;- les contrôles de produit/posting peuvent déclencher
CreateProductTimelineou une Timeline game-specific.
6. Préparer les données avant Play()
Une Timeline ne devrait pas aller chercher arbitrairement toutes ses données elle-même. Le trigger prépare les valeurs nécessaires, puis les passe à la Timeline.
Exemple YearResultsTimeline :
- reçoit
teamUid; - reçoit les
RankingsData; - reçoit
yearId; - calcule profit/perte;
- choisit
goodResultsDirectoroubadResultsDirector.
7. Gérer la fin dans onFinish
Le code qui ouvre l'écran suivant ou change la progression doit être exécuté dans le callback.
MyTimeline.Instance.PlaySequence(
message,
() =>
{
OpenNextPanel();
}
);
Ne pas écrire :
MyTimeline.Instance.PlaySequence(message, null);
OpenNextPanel(); // s'exécute immédiatement
Empêcher une Timeline de rejouer
TimelineBase ne persiste aucun état par lui-même. Le mécanisme anti-replay dépend du contexte.
| Cas | Mécanisme |
|---|---|
| Première ouverture de module | Scenario.eScenarioStep.eStudentFirstModule. |
| Résultats annuels | SetYearSequenceSeen(yearId). |
| Résultats d'un module | UserState.ModuleUserState.STUDENT_RESULTS. |
| Premier produit / premier launch | Condition métier + éventuellement ScenarioStep. |
FirstModuleTimeline
FirstModuleTimeline.PlayFirstModule() :
- injecte le texte;
- joue
firstModuleDirector; - à la fin, sauvegarde
eStudentFirstModule; - appelle le callback qui ouvre le module.
Le trigger est présent dans les contrôles By Module lorsque l'étudiant n'a jamais vu cette séquence.
ModuleResultsTimeline
Cette Timeline est plus complexe car les données peuvent ne pas être encore disponibles lorsqu'on veut la jouer.
Attente des résultats
Si TryGetTeamByModuleResult() ne trouve pas encore les résultats :
- la Timeline s'abonne à
onNewTeamModuleResults; - attend le bon
actModuleUid/ module type; - se désabonne dès réception;
- prépare puis joue la séquence.
Ce pattern est à réutiliser lorsqu'une nouvelle Timeline dépend d'une donnée asynchrone : abonnement temporaire + filtre strict + désabonnement immédiat.
Choix du director
Le score moyen du module choisit entre :
goodResultsDirectorà partir de 85 %;okayResultsDirectorà partir de 70 %;badResultsDirectorsinon.
Pause et report
ChangePlayableSpeed(false) met le root playable à vitesse 0 et affiche les options. La Timeline peut alors montrer les notes via reportControl.ShowGrades() avant de continuer.
YearResultsTimeline
PlayNewResults() :
- récupère le
RankingsDatade l'année; - met à jour le whiteboard/graph;
- compare la valeur actuelle à l'année précédente ou à
GraphFirstValue; - détermine profit/perte;
- choisit le director;
- construit le texte localisé;
- marque la séquence annuelle comme vue via
SetEndOfYearSequenceSeen(yearId).
Créer une Timeline avec plusieurs variantes
Pour un même événement avec plusieurs résultats visuels, garder une seule classe et plusieurs PlayableDirector lorsque les inputs/lifecycle sont identiques.
PlayableDirector director =
score >= threshold
? goodDirector
: badDirector;
Play(onFinish, director);
Créer plusieurs classes uniquement si les séquences ont des données et un lifecycle réellement différents.
Interaction avec les caméras et l'UI
TimelineBase.Play() désactive la caméra UI et empêche les raycasts. Un bug pendant la séquence peut donc laisser toute l'application visuellement ou fonctionnellement bloquée si Stop() n'est jamais exécuté.
Stop()
Stop() doit toujours rétablir :
uiCamera.enabled = true;offscreenCamera.enabled = false;scene3dRoot.SetActive(false);Canvas.worldCamera = uiCamera;UICanvasGroup.blocksRaycasts = true.
Gestion du ratio d'écran
TimelineBase interpole le scale horizontal entre scale_4_3 et scale_16_9 selon Screen.width / Screen.height, puis clamp le résultat.
Si une séquence est correctement cadrée en 16:9 mais coupée sur un autre ratio, vérifier ces deux valeurs sur le prefab avant de modifier la caméra ou les clips.
Timelines game-specific
MK1
TimelinesMk1 hérite de YearResultsTimeline et ajoute un launchProductDirector ainsi qu'un whiteboard MK1.
ENT1
LaunchProductTimeline gère un whiteboard de lancement produit et ses propres directors de résultats.
Build et assets
Modifier :
- un script Timeline;
- un prefab qui la contient;
- un asset
.playable; - un binding d'animation;
nécessite un nouveau build Unity/WebGL.
Debug Timeline
| Symptôme | À vérifier |
|---|---|
| Écran noir après la séquence | director.stopped a-t-il été déclenché ? Stop() a-t-il tourné ? |
| UI visible mais non cliquable | UICanvasGroup.blocksRaycasts. |
| Timeline rejouée à chaque ouverture | Mécanisme anti-replay non persisté. |
| Callback plusieurs fois | Abonnement director.stopped ou event résultats non désabonné. |
| Mauvaise variante good/okay/bad | Valeur métier passée à la Timeline et seuils de sélection. |
| Animation avec anciennes données | Le trigger passe-t-il les données actuelles avant Play() ? |
| Résultats module jamais affichés | onNewTeamModuleResults, filtres actModuleUid/moduleType. |
| Layout déformé | scale_4_3, scale_16_9, screenTransform. |
| Playable ne joue pas | Director / asset .playable / bindings Unity. |
Checklist avant merge
- asset
.playablesauvegardé; - tous les bindings valides;
- références caméra/canvas/director non null;
- séquence testée en Editor;
Stop()restaure l'UI;- callback de fin continue correctement le flow;
- anti-replay sauvegardé;
- relog ne rejoue pas une séquence déjà vue sauf si voulu;
- résolutions/ratios d'écran vérifiés;
- WebGL testé.