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.

Product: Molca VR SDK (paths assume molca-sdk-vr). Not used by the Digital Twin SDK (_MolcaDT). Script: Assets/_MolcaSDK/_VR/Scripts/Scenario/Data/ScenarioData.cs
Type: ScriptableObject (registered in Scenario Data Config collections)

When to use

Use ScenarioData (or its subclasses SimulationScenarioData / TourScenarioData) to define a VR training scenario:
  • Scenario metadata — name, description, image, tags, time limits
  • Activity definitions — ordered list of ScenarioActivityData entries
  • Scene loadingSceneLoadPlan for single-player and per-role multiplayer configurations
  • Launch options — execution mode support (Training/Assessment), config options
  • Multiplayer roles — role definitions and per-role scene plans
Each scenario asset is registered in Scenario Data Config collections and resolved at runtime by ScenarioSessionManager or Home UI.

Role

ScenarioData is the authoring asset that defines everything about a scenario:
  1. Metadata — displayed in Home UI, used for filtering/search
  2. ActivitiesScenarioActivityData entries matched to runtime ScenarioActivity components by RefId
  3. Scene loadingSceneLoadPlan structure executed by SceneLoadPlanExecutor to load ordered multi-scene setups
  4. Multiplayer — role definitions and per-role scene configurations
  5. Backend integrationScenarioId (org ID) links to backend scenario records
At runtime, Scenario Manager reads this asset to configure the scenario state machine, activity sequence, and scene loading.

Inspector setup

Basic metadata

FieldPurpose
Ref IdStable unique identifier (auto-generated). Used for local lookups and localization keys.
Scenario IdOrganization scenario ID from backend (set via SetOrgScenarioId). Used for session creation.
Scenario NameDisplay name (DynamicLocalization). Shown in Home UI.
Scenario DescriptionDescription text (DynamicLocalization). Shown in scenario detail UI.
Scenario ImageThumbnail texture. Displayed in Home UI scenario cards.
Scenario TagsArray of ScenarioTag for filtering/categorization.
Time Limit SecondsScenario time limit. 0 = no limit. Enforced by ScenarioManager.
Supports MultiplayerEnable for multiplayer scenarios. Requires role definitions and role scene mappings.

Launch configuration

FieldPurpose
Supports AssessmentEnable Assessment mode button in scenario detail UI. Training is always available.
Config OptionsArray of ScenarioConfigOptionSO — runtime-selectable options shown before launch (e.g., difficulty, language).

Activities

FieldPurpose
Scenario ActivitiesOrdered array of ScenarioActivityData. Each entry defines an activity’s metadata (name, description, RefId). Runtime ScenarioActivity components match by RefId.

Roles (multiplayer only)

FieldPurpose
Scenario RolesArray of ScenarioRole definitions. Each role has a RefId, name, description, and optional icon. Used for role selection UI and role scene mapping.

Scene loading

Single Player Plan

FieldPurpose
Preload ScenesArray of SceneEntry loaded before the scenario scene. Index 0 loads as Single (active scene); subsequent entries load Additive. Use for shared environments, lighting, or asset preloading.
Scenario SceneThe main scenario logic scene (required). Loads Additive when preloads exist, Single when no preloads.

Role Scene Mappings (multiplayer only)

FieldPurpose
Role Scene MappingsArray of RoleSceneMapping. Each entry maps a roleId (matching ScenarioRole.RefId) to a full SceneLoadPlan for that role in multiplayer.

SceneLoadPlan structure

The SceneLoadPlan defines ordered multi-scene loading with synchronization:
[Serializable]
public struct SceneLoadPlan
{
    public SceneEntry[] preloadScenes;  // Loaded before scenario scene
    public SceneEntry scenarioScene;    // Main scenario logic scene
}

Load order

  1. preloadScenes[0] loads as Single (becomes active scene)
  2. Wait for preloadScenes[0].waitForRefIds (or 2 frames if empty)
  3. preloadScenes[1..n] load as Additive (each waits for its RefIds)
  4. scenarioScene loads as Additive (when preloads exist) or Single (no preloads)

SceneEntry fields

Each scene entry has:
[Serializable]
public struct SceneEntry
{
    public string sceneName;              // Build-settings scene name
    public AssetReference sceneReference; // Addressable scene (takes priority)
    public string[] waitForRefIds;        // RefIds to wait for before next scene
}
FieldPurpose
Scene NameBuild-settings scene name. Used when Scene Reference is not set.
Scene ReferenceAddressable AssetReference. Takes priority over Scene Name when valid.
Wait For Ref IdsArray of RefIds that must be registered in ReferenceManager before the next scene loads. Leave empty to wait two frames only. Timeout: 15 seconds.

Code

Get scene load plan

using MolcaSDK.VR.Scenario;

// Single-player
SceneLoadPlan plan = scenarioData.GetSceneLoadPlan(ScenarioPlayerMode.Single);

// Multiplayer (specific role)
SceneLoadPlan plan = scenarioData.GetSceneLoadPlan(ScenarioPlayerMode.Multiplayer, "operator");

if (plan.IsValid)
{
    Debug.Log($"Scenario scene: {plan.scenarioScene.sceneName}");
    Debug.Log($"Preload count: {(plan.HasPreloads ? plan.preloadScenes.Length : 0)}");
}

Access metadata

// Basic info
string name = scenarioData.Name;
string description = scenarioData.Description;
float timeLimit = scenarioData.TimeLimitSeconds;
bool hasTimeLimit = scenarioData.HasTimeLimit;

// Activities
IReadOnlyList<ScenarioActivityData> activities = scenarioData.Activities;
foreach (var activity in activities)
{
    Debug.Log($"Activity: {activity.Name} (RefId: {activity.RefId})");
}

// Roles (multiplayer)
if (scenarioData.SupportsMultiplayer)
{
    IReadOnlyList<ScenarioRole> roles = scenarioData.Roles;
    foreach (var role in roles)
    {
        Debug.Log($"Role: {role.Name} (RefId: {role.RefId})");
    }
}

Check for legacy data

// Check if asset has unmigrated legacy scene fields
if (scenarioData.HasLegacySceneData)
{
    Debug.LogWarning("Scenario has legacy scene data. Migration recommended.");
    
    // Migrate automatically (editor only)
    #if UNITY_EDITOR
    bool migrated = scenarioData.MigrateLegacySceneFields();
    if (migrated)
    {
        UnityEditor.EditorUtility.SetDirty(scenarioData);
        Debug.Log("Legacy scene data migrated to SceneLoadPlan.");
    }
    #endif
}

Set organization scenario ID

// Called after fetching scenario metadata from backend
scenarioData.SetOrgScenarioId(orgScenarioId: 12345);

Scene loading examples

Simple single scene

// Inspector configuration:
singlePlayerPlan:
  preloadScenes: (empty)
  scenarioScene:
    sceneName: "FireSafetyTraining"
    sceneReference: null
    waitForRefIds: (empty)

// Result: Loads "FireSafetyTraining" as Single

Environment + scenario (two scenes)

// Inspector configuration:
singlePlayerPlan:
  preloadScenes:
    [0] sceneName: "Environment_Factory"
        sceneReference: null
        waitForRefIds: ["factory_floor", "control_panel"]
  scenarioScene:
    sceneName: "Scenario_SafetyTraining"
    sceneReference: null
    waitForRefIds: (empty)

// Result:
// 1. Load "Environment_Factory" as Single
// 2. Wait for "factory_floor" and "control_panel" RefIds (15s timeout)
// 3. Load "Scenario_SafetyTraining" as Additive

Addressable scenes

// Inspector configuration:
singlePlayerPlan:
  preloadScenes:
    [0] sceneName: ""
        sceneReference: AssetReference("Environments/Factory")
        waitForRefIds: (empty)
  scenarioScene:
    sceneName: ""
    sceneReference: AssetReference("Scenarios/SafetyTraining")
    waitForRefIds: (empty)

// Result: Loads both scenes via Addressables
// Scene Reference takes priority over Scene Name when valid

Multiplayer with role-specific scenes

// Inspector configuration:
singlePlayerPlan:
  scenarioScene:
    sceneName: "Scenario_Operator_Solo"

roleSceneMappings:
  [0] roleId: "operator"
      plan:
        preloadScenes:
          [0] sceneName: "Environment_Factory"
        scenarioScene:
          sceneName: "Scenario_Operator_View"
  [1] roleId: "supervisor"
      plan:
        preloadScenes:
          [0] sceneName: "Environment_Factory"
        scenarioScene:
          sceneName: "Scenario_Supervisor_View"

// Usage:
var operatorPlan = scenarioData.GetSceneLoadPlan(ScenarioPlayerMode.Multiplayer, "operator");
var supervisorPlan = scenarioData.GetSceneLoadPlan(ScenarioPlayerMode.Multiplayer, "supervisor");

API reference

Properties

PropertyTypeDescription
RefIdstringStable unique identifier (IReferenceable).
ScenarioIdintOrganization scenario ID from backend.
NamestringDisplay name (localized).
DescriptionstringDescription text (localized).
SupportsMultiplayerboolWhether multiplayer is enabled.
ActivitiesIReadOnlyList<ScenarioActivityData>Activity definitions.
RolesIReadOnlyList<ScenarioRole>Role definitions (multiplayer).
TagsIReadOnlyList<ScenarioTag>Scenario tags for filtering.
ImageTexture2DThumbnail image.
TimeLimitSecondsfloatTime limit in seconds (0 = no limit).
HasTimeLimitboolWhether time limit is set.
SupportsAssessmentboolWhether Assessment mode is available.
ConfigOptionsIReadOnlyList<ScenarioConfigOptionSO>Launch configuration options.
SinglePlayerPlanSceneLoadPlanScene load plan for single-player.
RoleSceneMappingsIReadOnlyList<RoleSceneMapping>Per-role scene plans (multiplayer).
HasLegacySceneDataboolWhether unmigrated legacy scene fields exist.

Methods

MethodReturnsDescription
GetSceneLoadPlan(playerMode, roleId)SceneLoadPlanReturns the scene load plan for the given player mode and optional role. Returns default (invalid) if no plan is configured.
SetOrgScenarioId(id)voidSets the organization scenario ID (called after backend fetch).
MigrateLegacySceneFields()boolMigrates legacy flat scene fields to SceneLoadPlan. Returns true if migration was performed. Editor only.
Initialize()voidInitializes localization and nested data (activities, tags, roles). Called by ScenarioDataConfig.

Troubleshooting

  • Scenario not found at runtime: verify the asset is registered in Scenario Data Config collections. Check RefId matches the lookup key.
  • Scene doesn’t load: confirm scenarioScene has either sceneName or sceneReference set. For Addressables, verify the scene is marked as Addressable and the key is valid. Check SceneLoadPlan.IsValid.
  • Preload scenes load out of order: preload scenes load sequentially by array index. Index 0 always loads as Single (active scene); subsequent entries load Additive.
  • Scene loads but objects missing: check waitForRefIds on preload scenes. The executor waits up to 15 seconds for those RefIds to be registered in ReferenceManager. Check console for timeout warnings.
  • Multiplayer role scene not found: verify roleSceneMappings contains an entry with matching roleId. Check that the role’s SceneLoadPlan.IsValid.
  • Activities don’t match runtime components: ensure ScenarioActivity components in the scene have activityId matching ScenarioActivityData.RefId. Mismatches log warnings.
  • Legacy scene fields not migrating: call MigrateLegacySceneFields() in editor. Check that singlePlayerPlan is empty before migration (migration only runs when plan is invalid).
  • Time limit not enforced: verify TimeLimitSeconds > 0 and Scenario Manager is active. Time limit is checked in ScenarioManager.Update().

Unity Editor

Scenario Data Inspector
Migration from legacy fields: If you have existing scenario assets created before the SceneLoadPlan refactoring, they contain hidden legacy fields (sceneName, singlePlayerSceneName, environmentSceneName, etc.). These are automatically migrated to singlePlayerPlan when accessed at runtime. For editor migration, call MigrateLegacySceneFields() in a custom editor script or inspector.
Addressable priority: When both sceneName and sceneReference are set on a SceneEntry, the Addressable reference takes priority. If the Addressable key is invalid, the scene load will fail — it does NOT fall back to sceneName. Set only one field to avoid confusion.