Sample Tutorial: Training Simulation (Unity)

Game Engine

Unity

Tutorial Level

Advanced

Project Files

https://github.com/Cavrnus-Inc/trainer-demo-csc-unity

Lets transform a single-user training application into a multi-user persistent training experience using both no-code and simple script bindings. The final project can be accessed on GitHub.

image-20240223-223849.png

Initial Project Without Cavrnus

We are starting with a VR car maintenance trainer that includes all of the logic to guide a single user through a sequence of steps; moving the vehicle, replacing tires, all while interacting with various levers. The XR interactions are included using Unity’s XR Toolkit components along with OpenXR.

Initial Cavrnus Setup

We first need to Add Cavrnus to the project and follow the Setup Your Scene guide in order to prepare your scene. Lets use the dropdown button for a One-Click setup.

image-20240223-222651.png

Configure Cavrnus Spacial Connector

Set the Cavrnus Spatial Connector to use our server, Login to our account automatically, and Automatically Join a “Car Trainer” space we created in the web.

Setup XR Rig UI

Optionally we can setup included XR menus to interface with Cavrnus features, such as microphone control and users list. Use the package manager to import the XR UI sample.

1

2

3

 

 

4

 

5

6

Now we have UI appearing when the space loads!

Cavrnizing the Trainers

Identify and Add Sync Components

At this point we need to identify all of the components in the sim that need to be synced and add the corresponding No-Code components to each object. We need to add Sync Transform components to the car, tires, and lever.

 

 

Determine The Underlying Data

Now that we have the objects synced, lets begin thinking about how to design your data. This data will not only drive the step progression, but also handle UI states and other dependent components that follow.

In this project, we are creating a linear step trainer that guides users through each step, one after the other. Naturally, we can represent this as an integer value.

Use Data to Establish Scene State

We have established representing an int as our underlying data, so lets establish the binding in our script’s setup. Notice we are using a FloatProperty to store our int.

private void SetupSteps() { SpaceConn.DefineFloatPropertyDefaultValue("CarLiftProcedure", "CurrentStep", 0); }

Now we have defined data representing our trainer! Notice the default value of 0 - if the property at this CarLiftProcedure doesn’t exist, it will be created and initialized with 0 which signifies the initial step in our trainer.

 

Next, we can drive the trainer through a single binding off the underlying float data we just defined above. Notice when the data is updated, we can respond accordingly via a switch statement.

private void SetupSteps() { SpaceConn.DefineFloatPropertyDefaultValue("CarLiftProcedure", "CurrentStep", 0); SpaceConn.BindFloatPropertyValue("CarLiftProcedure", "CurrentStep", currentStep => { switch (currentStep) { case 0: StepZero(); break; case 1: StepOne(); break; case 2: StepTwo(); break; case 3: StepThree(); break; case 4: StepFour(); break; case 5: StepFive(); break; default: throw new Exception("Invalid step number received!"); } }); }

Handling Logic and Posting Step Completion

Now that we have our data defined and bound, we must now determine where to post step completions. Remember, each step should be treated as an independent state, ensuring that if a user joins the trainer mid-session, the corresponding components in that state should be independent of other steps. More information on this paradigm can be found here.

Lets look at a simple step in our trainer where a user must press a button.

private void StepZero() { button.enabled = true; button.onPress.AddListener(ButtonPressed); }

 

After establishing the game logic, we mark this complete with a post to the next step, which in this case is step 1. This update is received in the bounded callback we setup previously which posts a value of 1.

Binding the UI

Tie Data Into UI Checklist

To visually show progression through the trainer, lets create a checklist panel.

Checklist Item Creation

Following the conventional UI pattern of instantiating list items, lets provide each item (which represents a step in the trainer) a CavrnusSpaceConnection reference, and the corresponding ContainerName and PropertyName values. Since we are zero-indexed, we can also just provide the for-loop index since it correlates to the step.

Checklist Item Binding

For each item that is instantiated in the list we simply bind each entry to the underlying float data we setup above. It's important to note that we continue to use the FloatPropertyValue to track the current state of each item entry. We are not introducing any other data to the mix.

Checklist State Handling

When the OnTaskComplete callback occurs, we can interpret the provided state to reflect in our UI. Here, incomingTaskrepresents the current task progression value, which means we must determine a few things:

  • If the stepId (representing the current step or task) matches incomingTaskInt, the MarkActive() method is called to indicate that the current step is active.

  • If the stepId is less than incomingTaskInt, it implies that all previous steps are completed, so the SetCompleteStatus(true) method is called to mark them as complete.

  • If none of the above conditions are met, the SetCompleteStatus(false) method is called to mark the step as incomplete or in its default state.

Reset Button

Intuitively, resetting the trainer to the initial step just means posting a property update to step 0. So let’s trigger this from a UI button click.

 

 

And finally with the UI properly configured, we can simply post the property update to 0, which effectively resets the trainer!

Setting Next and Previous

Following this same idea, we can also manually trigger the next and previous steps if we need to.

Final Product

With the addition of no-code components and a handful of script bindings, we have transformed the signal user experience trainer to one that can be accessed via anyone with a Quest headset that offers copresence with voice and video!