Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs-unity.molca.id/llms.txt

Use this file to discover all available pages before exploring further.

Applies to: Molca VR SDK

Overview

This recipe shows you how to create a complete VR training scenario from scratch. You’ll create a Scenario Data Config asset, set up a scenario scene with ScenarioManager, add activities with VR-specific steps, and configure the session flow. This is the foundation for building immersive VR training experiences with ordered tasks, scoring, and backend integration.

Prerequisites

  • SDK modules: Molca Core, Molca VR SDK installed
  • Unity setup: RuntimeManager configured, XR Plugin Management installed, GlobalSettings configured
  • Prior knowledge: RuntimeManager, SequenceController, Step

Step-by-step

Step 1: Create a Scenario Data Config asset

Create a ScenarioDataConfig asset to register your scenario collections and configure execution mode.
// In the Unity Editor:
// 1. Right-click in Project → Create → Molca → VR → Scenario Data Config
// 2. Name it "MyScenarioDataConfig"
// 3. Add it to your GlobalSettings asset's SettingModules list
Why this works: The ScenarioDataConfig is a SettingModule that tells the runtime which scenarios exist and how to resolve them by ID. It’s loaded during RuntimeManager initialization and used by the session system to find scenario assets.

Step 2: Create a Simulation Scenario Data asset

Create a SimulationScenarioData asset that defines your training scenario metadata.
// In the Unity Editor:
// 1. Right-click in Project → Create → Molca → VR → Simulation Scenario Data
// 2. Name it "FireSafetyTraining"
// 3. Set the following fields:
//    - Scenario Name: "Fire Safety Training"
//    - Ref Id: (auto-generated, e.g., "fire-safety-001")
//    - Time Limit: 300 (5 minutes, or 0 for no limit)
//    - Single Player Plan:
//      - Scenario Scene → Scene Name: "FireSafetyScene" (or use Scene Reference for Addressables)
//      - Preload Scenes: (leave empty for simple scenarios; add environment scenes if needed)
Why this works: SimulationScenarioData holds the metadata that links your scenario asset to its Unity scenes via the SceneLoadPlan structure. The RefId is used for local lookups, while ScenarioId (org ID, set later) links to backend scenarios. The SceneLoadPlan supports ordered multi-scene loading — preload scenes load first (environment, shared assets), then the main scenario scene.

Step 3: Add the scenario to ScenarioDataConfig

Add your scenario data asset to the ScenarioDataConfig collections.
// In the Unity Editor:
// 1. Select your "MyScenarioDataConfig" asset
// 2. In the Inspector, expand "Scenario Collections"
// 3. Add a new collection entry
// 4. Drag your "FireSafetyTraining" asset into the collection
Why this works: The ScenarioDataConfig uses collections to register available scenarios. At runtime, methods like GetScenarioDataByScenarioId search these collections to resolve scenario references.

Step 4: Create the scenario Unity scene

Create a new Unity scene for your VR scenario and add the VR Scenario prefab.
// In the Unity Editor:
// 1. File → New Scene
// 2. Save as "FireSafetyScene" (matching the Scene Name in your scenario data)
// 3. Drag the "[VR Scenario]" prefab into the scene
//    (typical path: Assets/_MolcaSDK/_VR/Prefabs/Base/)
// 4. This prefab includes: XR rig, ScenarioManager, scenario UI, and VR player setup
Why this works: The [VR Scenario] prefab provides the complete VR runtime infrastructure: XR Origin for tracking, ScenarioManager for orchestration, scenario UI for pause/completion, and player management. This ensures consistent VR setup across all scenarios.

Step 5: Configure ScenarioManager

Select the ScenarioManager component in your scene and configure it to reference your scenario data.
// In the Unity Editor:
// 1. Select the GameObject with ScenarioManager (usually on the [VR Scenario] prefab root)
// 2. In the Inspector, set:
//    - Scenario Data: Drag your "FireSafetyTraining" asset here
//    - Auto Start: Check this for testing (uncheck for session-based flow)
//    - Use Session Manager: Uncheck for testing, check for production
//    - Start All Activities Simultaneously: Usually unchecked (sequential activities)
Why this works: ScenarioManager is the runtime orchestration component that manages scenario state (Inactive, Active, Completed), drives activities in order, and integrates with the session system. The scenarioData reference links the scene to your scenario metadata.

Step 6: Create activity GameObjects

Under the ScenarioManager, create child GameObjects for each training activity.
// In the Unity Editor:
// 1. Right-click ScenarioManager → Create Empty
// 2. Rename to "Activity1_ExtinguisherGrab"
// 3. Add Component → Molca → VR → Scenario Activity
// 4. Add Component → Molca → Sequence → Sequence Controller
// 5. Set Activity ID: "extinguisher-grab" (must match ActivityData.RefId if using session)
// 6. Repeat for additional activities (e.g., "Activity2_FireExtinguish")
Why this works: Each ScenarioActivity wraps a SequenceController and integrates with scoring and session posting. The activityId field links the runtime component to backend activity metadata when using connected sessions.

Step 7: Add VR steps to activities

Create child GameObjects under each activity and add VR step components.
// In the Unity Editor:
// For Activity1_ExtinguisherGrab:
// 1. Right-click Activity1_ExtinguisherGrab → Create Empty
// 2. Rename to "Step1_LookAtExtinguisher"
// 3. Add Component → Molca → VR → Steps → Look At Step
// 4. Configure the Look At Step:
//    - Target: Drag the fire extinguisher GameObject
//    - Duration: 2.0 seconds
//
// 5. Create another child: "Step2_GrabExtinguisher"
// 6. Add Component → Molca → VR → Steps → Grab Step
// 7. Configure the Grab Step:
//    - Target Object: Drag the fire extinguisher GameObject
//    - Require Correct Hand: Optional (check if specific hand required)
Why this works: VR steps are specialized Step subclasses that handle VR interactions. LookAtStep completes when the user gazes at a target for a duration. GrabStep completes when the user grabs a specific object. The SequenceController executes these steps in hierarchy order.

Step 8: Wire activities to ScenarioManager

Add your activity GameObjects to the ScenarioManager.activities array.
// In the Unity Editor:
// 1. Select the ScenarioManager GameObject
// 2. In the Inspector, expand "Activities"
// 3. Set Size to match your activity count (e.g., 2)
// 4. Drag Activity1_ExtinguisherGrab into Element 0
// 5. Drag Activity2_FireExtinguish into Element 1
Why this works: The ScenarioManager executes activities in array order (unless startAllActivitiesSimultaneously is checked). When an activity’s sequence completes, the manager advances to the next activity or completes the scenario.

Step 9: Configure session flow (optional)

For production scenarios with backend integration, configure the session flow.
using Molca;
using MolcaSDK.VR.Scenario.Session;
using UnityEngine;

public class ScenarioBootstrap : MonoBehaviour
{
    private ScenarioSessionManager _session;

    private async void Start()
    {
        await RuntimeManager.WaitForInitialization();
        _session = RuntimeManager.GetSubsystem<ScenarioSessionManager>();
        
        if (_session == null)
        {
            Debug.LogError("ScenarioSessionManager not found");
            return;
        }

        // Subscribe to events
        _session.OnError.AddListener(OnSessionError);
        _session.OnLoadingProgress.AddListener(OnLoadingProgress);

        // Create session (orgScenarioId from backend)
        bool created = await _session.CreateSessionAsync(orgScenarioId: 42, mode: "TRAINING");
        if (!created)
        {
            Debug.LogError("Failed to create session");
            return;
        }

        // Load scenario scene
        await _session.LoadSessionWithProgressAsync();
    }

    private void OnDestroy()
    {
        if (_session != null)
        {
            _session.OnError.RemoveListener(OnSessionError);
            _session.OnLoadingProgress.RemoveListener(OnLoadingProgress);
        }
    }

    private void OnSessionError(string message) => Debug.LogError($"Session error: {message}");
    private void OnLoadingProgress(float progress, string status) => Debug.Log($"Loading: {progress:P0} - {status}");
}
Why this works: ScenarioSessionManager handles backend session creation, scene loading via Addressables, and progress tracking. The CreateSessionAsync call establishes a session with the backend, and LoadSessionWithProgressAsync loads the scenario scene and starts the training. This flow requires useSessionManager to be checked on ScenarioManager.

Complete example

Here’s a complete custom VR step that waits for the user to press a button before completing:
using Molca.Sequence;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

/// <summary>
/// A VR step that completes when the user presses a specific button.
/// </summary>
public class ButtonPressStep : Step
{
    [SerializeField] private XRSimpleInteractable button;
    [SerializeField] private string instructionText = "Press the red button";

    protected override void OnStepActivated()
    {
        base.OnStepActivated();
        Debug.Log(instructionText);
        
        if (button != null)
        {
            button.selectEntered.AddListener(OnButtonPressed);
        }
    }

    private void OnButtonPressed(SelectEnterEventArgs args)
    {
        Debug.Log("Button pressed, step complete");
        Complete();
    }

    protected override void OnStepDeactivated()
    {
        base.OnStepDeactivated();
        
        if (button != null)
        {
            button.selectEntered.RemoveListener(OnButtonPressed);
        }
    }
}
Attach this script to a child GameObject under your ScenarioActivity, and it will wait for the user to interact with the specified button before advancing.

Troubleshooting

  • Scenario never starts: Ensure RuntimeManager.WaitForInitialization() completes before calling StartScenario(). If using autoStart, verify the ScenarioManager is active in the scene.
  • Activities don’t advance: Check that each activity’s SequenceController is properly configured and that steps call Complete() when finished. Use the Sequence Visualizer to debug step hierarchy.
  • Session creation fails: Verify ScenarioNetworkConfig endpoints are correct, authentication token is valid, and orgScenarioId exists in the backend. Check OnError event logs.
  • Scene doesn’t load: Confirm the SceneLoadPlan is configured in SimulationScenarioData — check that singlePlayerPlan.scenarioScene has either sceneName or sceneReference set. For Addressables, verify the scene is marked as Addressable and the key matches exactly.
  • Steps execute out of order: Steps execute in hierarchy order (top to bottom). Reorder GameObjects in the Hierarchy or check for parallel step configurations.
  • Preload scenes don’t load: Verify singlePlayerPlan.preloadScenes array is populated. Index 0 loads as Single (active scene); subsequent entries load Additive. Check console for scene load errors.
  • Scene loads but objects missing: Check waitForRefIds on preload scene entries — the executor waits up to 15 seconds for those RefIds to be registered in ReferenceManager. If timeout occurs, check console warnings.