Skip to main content

Scénarios runtime — configuration, réponses et évaluation

ButRôle dedu cettesystème pageScenario

Le projetsystème contientScenario troisgère systèmesle prochescontenu visuellementd'introduction/contexte, maisles techniquementquestions, distinctsles réponses de segments et leur évaluation. Il ne doit pas être confondu avec :

  • Tutoriels — Systèmeguides Responsabilitéinteractifs Sourcequi demontrent vérité
Scenario Pages de contexte, questions, réponses, segment gridl'UI et évaluation.attendent evaluation.scenariodes +actions; staticTimeline data— +séquences ScenarioPages.Unity/PlayableDirector audiovisuelles; ScenarioStep Flags— flags de progression :persistés tutorieldans vu, première action faite, questions terminées, etc. userDetails.scenarioStepuserDetails. Timeline Séquences Unity/PlayableDirector : résultats, premier module, lancement produit, etc. Assets .playable + scripts TimelineBase.

Ne pas utiliser un ScenarioStep comme stockage d'une réponse métier et ne pas utiliser une Timeline comme mécanisme de progression principal. Chaque système a son lifecycle propre.

Architecture Scenario

flowchart LR
SD[Static data / ScenarioPages] --> UI[ScenarioPanelControlBase]
UI --> Q[Questions / Segment grid]
Q --> SAVE[SaveScenarioAnswers / SaveEvalSegment]
SAVE --> EVAL[(evaluation.scenario)]
EVAL --> COMPUTE[compute_scenario_score / compute_segment_score]
COMPUTE --> SCORE[evaluation.scores]
SCORE --> CORRECT[ScenarioEval / corrected UI]
UI --> STEP[eFinishQuestions]
STEP --> USER[(userDetails.scenarioStep)]

Modèles principaux

    Fichier Responsabilité Assets/Src/Game/Data/Scenario.cs — réponsesRéponses booléennes +et réponses de segments.segments de l'étudiant. ScenarioAnswer.cs — réponse statiqueRéponse attendue par UID. ScenarioEval.cs — scénarioScenario + goodScenarioAnswersnombre +de goodSegmentAnswers.bonnes réponses scénario/segments. ScenarioPages.cs —ScriptableObject définitionqui desdécrit les pages affichées. ScenarioPanelControlBase.cs — moteurMoteur UI principal.principal, navigation, validation et sauvegarde. ScenarioSegGridControlBase.cs — baseBase de la grille de segments. EvalCorrectedPopupControl.cs — affichageVue de lacorrection. correction.

    ScenarioPages : configuration des pages

    ScenarioPages est un ScriptableObject. Chaque PagesDefinition contient :

    • headerText — UID/base du titre;;
    • segGridUid — segment grid associé si applicable;;
    • pages[] — sous-pages de contenu;;
    • questionsStartIndex;
    • numQuestions.

    Chargement By Year

    StaticDataByYearController.Initialize() charge :

    ScenarioPages = Resources.Load<ScenarioPages>(
        $"data_{GameUid}/ScenarioPages"
    );

    LeCe nomchargement etest lefait chemindans de l'asset sont donc contractuels.StaticDataByYearController.Initialize().

    Chargement By Module

    StaticDataByModuleController construit un asset par module :

    string moduleUid = moduleByUid.Key.SnakeCaseToPascalCase();
    
    ScenarioPages scenarioPages =
        Resources.Load<ScenarioPages>(
            $"data_{GameUid}/{moduleUid}ScenarioPages"
        );

    Exemple : un module bank_reconciliationStaticDataByModuleController chercheracharge donc un asset dontScenarioPages ledistinct nom suit la conversion PascalCase du module. Si l'asset n'est pas trouvé, lepar module n'estlorsqu'il simplement pas ajouté à ScenarioPages.existe.

    Créer ou modifier un Scenario de A à Z

    1. Définir le contenu et les réponses attendues

    Les réponses des questions utilisentsont stockées sous des UIDs du typecomme question_Nquestion_0., Lesquestion_1, bonnesetc. Vérifier que questionsStartIndex et numQuestions correspondent aux réponses viennentstatiques de la static data chargée par le jeu. Si une question est ajoutée, vérifier que son UID est cohérent avec l'index utilisé par PagesDefinition.questionsStartIndex/numQuestions.attendues.

    2. Modifier le bon ScenarioPages asset

    Ajouter

    la page dans le bon ScenarioPages. Pour By Year,Year modifier: data_<gameUid>/ScenarioPages.; Pour By Module,Module modifier le: data_<gameUid>/<ModuleUid>ScenarioPages. du module.

    L'inspecteur custom ScenarioPagesEditor.cs masque la référence de grillesegment grid lorsque useSegmentsInScenario est désactivé.false.

    3. Ajouter les textes

    Ajouter les textestitres, deparagraphes, page, questions, choixquestions et titreschoix via le pipeline de localisation. Ne pas hardcoder le textecontenu dans ScenarioPanelControlBase.

    4. Ajouter un renducomposant spécifiquegame-specific si nécessaire

    ByLe Yearsocle possèdeScenario est commun, mais certains jeux possèdent des contrôlesrendus game-specific commepropres :

    • MK1/MK2/HR1 : ScenarioSegGridControl dans MK1/MK2/HR1;;
    • ScenarioSegmentsTableMgt1,MGT1 ScenarioEmployeesTableMgt1,: ScenarioFeatureTableMgt1,tables ScenarioDisciplinaryTableMgt1.scenario spécialisées pour segments, employés, features et discipline.

    SiUn lenouveau scénariorendu nemétier peutreste pas être exprimé avec les composants communs, créer le contrôle danssous Assets/GameSpecific/<gameUid>/Src/UI, pas dans Commun..

    5. Vérifier la génération runtime des pagesInitRuntimePages()

    ScenarioPanelControlBase.InitRuntimePages() combineconstruit les pagesDefinitionspages avecruntime. En By Year, il peut insérer les segments dudans jeula séquence lorsque useSegmentsInScenario est actif. LoadPageDefInContent() instancie ensuite les éléments UI.activé.

    6. Sauvegarder les réponses

    Lors de la validation, OnConfirmValidateAnswers() construit les nouveaux question_N puis appelle :

    questions
    flowchart LR
    UI[ScenarioPanelControlBase] --> STUD[Student.SaveScenarioAnswers]
    STUD --> BE[BackEnd.SaveScenarioAnswers]
    BE --> CMD[_command/save_scenario_answers]
    CMD --> E[(evaluation.scenario.answers)]
    

    save_scenario_answers.js merge uniquement les réponsesclés reçues dans evaluation.scenario.answers, puis sauvegarde le document evaluation.

    7. Sauvegarder les segments

    Les réponses de segment passent par SaveEvalSegment →appelle _command/save_eval_segment. CeLe script :

      écrit le segment dans evaluation.scenario[segmentUid],; sauvegarde,sauvegarde puisevaluation; envoie au professeur un message get_data qui devient update_eval.

      8. Calculer leles scorescores

      Le client appelle les commandes game-specific :

      • <gameUid>_compute_scenario_score;
      • <gameUid>_compute_segment_score.

      Ces wrappersElles délèguent au moteur Eval<Game> correspondant,, sauvegardent le score dans evaluation.scores, puis retournent unles stubinformations utilisées par ScenarioEval.

      9. Marquer la fin du Scenario

      Une fois la séquence terminée, le client appelle :

      Game.Student.SetScenarioStep(
          Scenario.eScenarioStep.eFinishQuestions
      );

      eFinishQuestions doit rester la valeur 1.1. Le commentaire dansdu Scenario.cscode indique que le serveur suppose explicitement cette valeur.

      10. Tester la correction

      Tester GetScenarioEvaluation, ScenarioEvalScenarioEval.Deserialize() et l'UI corrigée. Une nouvelle question n'est pas terminée tant que, la vue professeur/correctioncorrigée saitet l'afficher.la vue professeur. Ajouter une question sans corriger ce chemin laisse la feature incomplète.

      ScenarioStep :— progression,progression pas évaluationpersistée

      Scenario.eScenarioStep est un enum de flags.flags stocké sous forme de bitmask. Il suit la progression utilisateur, pas les réponses de Scenario.

      Exemples actuels :

      • eFinishQuestions;
      • eFinishFirstProduct;
      • eStudentAfterFirstResults;
      • eFinishFirstRealLaunch;
      • eStudentFirstModule;
      • eStudentFirstCorrection;
      • eByModuleHomepageTutorial;
      • eStudentFirstFormula;
      • eStudentFirstFormulaActivation.

      Ajouter un nouveau ScenarioStep

      1. Ajouter un nouveau bit libre dans Scenario.eScenarioStep avec 1 << n.
      2. Ne jamais changerrenuméroter la valeur d'un ancien bit déjà persisté.
      3. Tester IsScenarioStepDone() avant dele déclencherflow la séquence.concerné.
      4. À la fin, appeler SetScenarioStep().
      5. Si la featureséquence peut être rejouée manuellement,volontairement, décider si UnSetScenarioStep() est nécessaire.

      User.SetScenarioStep() OR le nouveau bit dans _scenarioFlagle masque local puis appelle _command/set_scenario_step, qui persisteécrit le masque complet dans userDetails.scenarioStep.

      States liés au Scenario

      • StudentLoadScenarioState charge l'évaluation avant l'entrée dans le scénario.évaluation;
      • StudentPlayScenarioByYearState attend eFinishQuestions avantpuis dechoisit passerle àstate l'équipe / activité.suivant;
      • StudentPlayScenarioByModuleState déclenchelance le welcome By Module puis attend le même flag.

      Si

      Scenario unréouvert nouveau scénario bloqueaprès le flow, debugger d'abord le flag eFinishQuestions et le state courant avant de chercher un problème de navigation UI.

      Architecture Timeline

      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 commundébut

      TimelineBaseScenarioPopupControl gère :

        bascule uiCamera → offscreenCamera; activationpermet de scene3dRoot;consulter Canvasle renduscénario paraprès le flow initial. Il conserve la caméradernière offscreen;page adaptationconsultée duet scaleréutilise ScenarioPanelControl. En By Module, ScenarioWindowControl adapte les titres selon le ratio 16:9 / 4:3; blocage des raycasts UI pendant la séquence; abonnement temporaire à PlayableDirector.stopped; restauration complète dans Stop(); callback onFinish.

        Timelines existantes

        Classe Usage CreateProductTimeline Séquence générique après création/lancement d'un premier objet métier. YearResultsTimeline Résultats annuels By Year, graph + résultat positif/négatif. FirstModuleTimeline Première ouverture de module By Module. ModuleResultsTimeline Résultats d'un module summative + report de notes. TimelinesMk1 Extension MK1 pour launch produit. ENT1 LaunchProductTimeline Launch produit / résultats spécifiques ENT1.

        Créer une nouvelle Timeline de A à Z

        1. Décider si une Timeline est réellement nécessaire

        Utiliser Timeline pour une séquence audiovisuelle qui doit bloquer temporairement l'UI. Pour un guide interactif qui montre des contrôles et attendses des clics, utiliser le système Tutoriels.

        2. Créer l'asset .playable

        Créer/configurer l'asset Unity Timeline dans le dossier Resources approprié, puis créer les tracks/animations nécessaires.

        3. Créer une classe héritant TimelineBase

        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 toutes les références Unity

        Le prefab/scene doit fournir au minimum les caméras, scene3dRoot, canvas group, screen material/transform, texte 3D et le PlayableDirector.

        5. Choisir le déclencheur

        Exemples réels :

          DecisionsControlByModule → FirstModuleTimeline; DecisionsControlByModule → ModuleResultsTimeline; StudentState / TeamControllers → YearResultsTimeline; contrôles de création/lancement produit → CreateProductTimeline ou timeline game-specific.

          6. Décider comment empêcher le replay

          Une Timeline n'a pas elle-même de persistance. Utiliser selon le cas :

            un ScenarioStep si c'est une progression globale/tutoriel; SetYearSequenceSeen(year) pour les résultats annuels; UserState.ModuleUserState.STUDENT_RESULTS pour le résultat d'un module; une condition métier comme “premier produit seulement”.

            7. Toujours terminer le flow dans onFinish

            La logique qui ouvre l'écran suivant, change le state ou marque la progression doit être appelée après la fin de la Timeline. Ne pas supposer que Play() est synchrone.

            Cas spécial ModuleResultsTimeline

            ModuleResultsTimeline peut être déclenchée avant que les résultats soient arrivés. Elle s'abonne alors à Game.CurrentTeamByModule.onNewTeamModuleResults, filtre sur le module/type, puis joue la séquence lorsque les données existent.

            Si une nouvelle Timeline attend un RTT ou une donnée backend, reprendre ce pattern : abonnement temporaire, filtre strict, désabonnement dès réception.settings.

            Debug Scenario

            Symptôme Vérifier
            Page absente Nom/path dude ScenarioPages, pagesDefinitions, importasset dans Resources.
            Question affichéevisible mais réponse non persistée question_N → SaveScenarioAnswers → evaluation.scenario.answers.
            Segment non mis à jour chez le prof save_eval_segment → message get_data/update_eval.
            Score toujours faux Static goodGood answers static data + Eval<Game>.computeScenarioScoreAndSave().
            Étudiant bloqué après les questionsvalidation userDetails.scenarioStep contient-ilet bit eFinishQuestions ?.
            Correction vide get_scenario_evaluation, ScenarioEval.Deserialize()ScenarioEval, corrected popup.

            Debug Timeline

            Symptôme Vérifier Écran noir après la séquence TimelineBase.Stop() est-il exécuté ? Le director a-t-il émis stopped ? UI non cliquable après UICanvasGroup.blocksRaycasts doit revenir à true. Timeline rejouée à chaque ouverture Le flag de persistance approprié n'est pas sauvegardé. Callback exécuté plusieurs fois Vérifier l'abonnement/désabonnement à director.stopped et aux events résultats. Résultats incorrects dans l'animation Vérifier l'année et les RankingsData passés, pas l'animation elle-même. Layout déformé Références scale_16_9/scale_4_3 et screen transform.

            Checklist avant merge

            • Scenario : asset ResourcesScenarioPages au bon nom.nom/path;
            • Scenario : textes FR/EN présents.EN;
            • Scenarioréponses : réponse sauvegardéesauvegardées puis reluerelues après relog.relog;
            • Scenariosegments :synchronisés score game-specific correct.professeur;
            • Scenarioscore :scenario correction professeur/étudiant correcte.correct;
            • ScenarioStepscore :segments aucuncorrect bitsi historique renuméroté.utilisé;
            • Timelinecorrection : prefab/director/caméras référencés.correcte;
            • TimelineeFinishQuestions :déclenche UIle restauréebon à la fin.state;
            • Timelineaucun :ancien conditionScenarioStep anti-replay persistée.renuméroté;
            • TimelineBy :Year onFinish/ continueBy Module testés selon le flow.
            WebGL testé pour les deux systèmes.scope.