Skip to main content
Applies to: Molca VR SDK

Overview

This recipe shows you how to add scoring to VR training activities. You’ll configure step-level scoring with StepScoringAuxiliary, aggregate scores at the activity level with ActivityScoring, and choose appropriate scoring types for different training objectives. This enables performance tracking, session posting, and real-time score feedback during VR training scenarios.

Prerequisites

Step-by-step

Step 1: Add StepScoringAuxiliary to steps

Add StepScoringAuxiliary components to individual steps that should contribute to the activity score.
Why this works: StepScoringAuxiliary is a step auxiliary that hooks into the step lifecycle (OnStepBegin, OnStepUpdate, OnStepCompleted). It automatically tracks elapsed time, calculates scores based on the configured ScoringConfig, and optionally posts results to the session backend.

Step 2: Configure step scoring type

Choose a ScoringType that matches your training objective for each step.
Why this works: Each ScoringType implements a different scoring formula in ScoringConfig.CalculateScore(). Time-based types (TimeBonus, TimePenalty, Countdown) refresh every frame during OnStepUpdate, while static types (PointValue, Binary) calculate once on completion. The scoreMultiplier field scales all results, and allowNegativeScore controls clamping.

Step 3: Set accuracy for Binary and Accuracy scoring

For steps that validate correctness, call SetCorrect(), SetIncorrect(), or SetAccuracy() before the step completes.
Why this works: StepScoringAuxiliary defaults to accuracyValue = 1.0 (full credit). For Binary scoring, accuracy >= 0.5 awards correctPoints, otherwise incorrectPoints. For Accuracy scoring, the value lerps between minPoints and maxPoints. Calling these methods before Complete() ensures the accuracy is set before FinalizeScore() runs.

Step 4: Add ActivityScoring component

Add an ActivityScoring component to the activity root GameObject to aggregate step scores.
Why this works: ActivityScoring is marked [RequireComponent(typeof(ScenarioActivity))] and automatically collects all StepScoringAuxiliary components from the activity’s SequenceController.Steps on OnActivityStarted. It combines them using the selected ScoreAggregationMode and adds any base activity score from activityScoringConfig.

Step 5: Configure activity-level scoring (optional)

Add a base activity score that applies regardless of step performance.
Why this works: The activity’s own ScoringConfig can contribute a base score independent of step scores. This is useful for rewarding overall activity completion speed or providing a fixed bonus. The activity score updates during Update() if the activity is active and the config is time-based.

Step 6: Subscribe to score events for UI updates

Listen to OnScoreChanged and OnScoreFinalized events to update UI displays.
Why this works: OnScoreChanged fires whenever any step score updates (including time-based scores that refresh every frame). OnScoreFinalized fires once when the activity completes. GetMaxPossibleScore() returns the theoretical maximum based on all step and activity scoring configs, useful for percentage calculations.

Step 7: Enable session posting (optional)

Configure step scoring to post results to the backend session system.
Why this works: StepScoringAuxiliary.PostStepScoreAsync() runs after FinalizeScore() if postScoreToSession is true. It resolves the ScenarioSessionManager, ScenarioActivity, and org IDs from the scenario data config, then posts the score, elapsed time, and accuracy to the backend. This enables persistent progress tracking and analytics.

Complete example

Here’s a complete fire extinguisher training activity with multi-step scoring:
This example demonstrates:
  • Multiple scoring types across different steps
  • Activity-level time bonus on top of step scores
  • Custom step logic that sets accuracy based on performance
  • Real-time score updates during fire extinguishing
  • Total possible score: 10 + 50 + 30 + 100 + 50 = 240 points (if all maximums achieved)

Troubleshooting

  • Step scores always zero: Check that ScoringType is not set to None. For time-based scoring, verify targetTimeSeconds and maxTimeSeconds are configured correctly. For Binary scoring, ensure SetCorrect() or SetIncorrect() is called before the step completes.
  • Activity score missing step contributions: Verify Include Step Scores is checked on ActivityScoring. Ensure steps are registered in the activity’s SequenceController.Steps array. Check that StepScoringAuxiliary components are attached to step GameObjects.
  • Session posting fails: Confirm ScenarioSessionManager is active and a session is created. Verify OrgScenarioId, org_activity_id, and org_step_id are set in scenario data. Check that postScoreToSession is enabled on StepScoringAuxiliary. Review console logs for specific error messages.
  • Time-based scores don’t update: Only TimeBonus, TimePenalty, and Countdown types refresh during OnStepUpdate. Ensure the step is active and Update() is running. For activity-level time scoring, verify the activity is active (IsActive() returns true).
  • Binary scoring feels inverted: Binary scoring awards correctPoints when accuracy >= 0.5. Use SetCorrect() (sets accuracy to 1.0) for correct actions and SetIncorrect() (sets accuracy to 0.0) for incorrect actions. Don’t manually set accuracy values between 0 and 1 for binary scoring.
  • Negative scores appearing: Set allowNegativeScore to false in ScoringConfig to clamp final scores to >= 0. Alternatively, adjust minPoints or reduce pointsPerSecond for TimePenalty scoring to prevent negative results.
  • Max possible score calculation wrong: GetMaxPossibleScore() sums the maximum from each step’s ScoringConfig plus the activity’s base config. It does not include runtime manual offsets from AddBonusPoints(). Verify each step’s scoring config has correct maxPoints or basePoints values.