March 21, 2023
Note: The add-on provides comprehensive documentation as well. You can find it in Unity in path: MindPort → VR Builder → Add-ons → Randomization → Documentation
In this VR Builder tutorial we will learn how to use the Randomization add-on to set a few random parameters every time a process is run, thus creating a different situation every time and potentially different challenges.
A VR Builder process is usually deterministic. Every time the process is executed, the same object will appear in the same state requiring the same interaction. It can be useful to mix things up instead - to create random differences in every process run. This can cause the user to think about what they need to do, instead of just following a rote procedure, and arouse curiosity for new things to be discovered.
The Randomization add-on provides some tools to make it easier to add variance to processes. First, we have a completely new type of node, the Random Branch, which is used in place of a standard step. This node immediately outputs to a random one of its multiple transitions. The outputs can also be weighted to have different frequencies of the following scenarios. This allows you to easily create branching paths and add random side events. In this tutorial, we will use it to randomly select one of three different scenarios.
The other feature allows you to randomly set properties of objects. Due to its nature, it can be powerfully be combined with with other property related add-ons like States and Data or Track and Measure. Stand-alone, it is possible to assign a random value to boolean and number properties. This can be useful to randomize quantities, or set some switches which can cause random things to happen in a process. In our tutorial, we will randomize the pressure in the tires of a car, making every process run more interesting and engaging for the user in VR, as they have to respond to a new situation every time.
For this tutorial, our process involves checking the pressure in the tires of that car, and adjusting it as needed. A random scenario will determine the starting pressure and the success conditions. Once the process is over, the user will get evaluated.
After following the tutorial, you will have a good understanding of the Randomization add-on features to enhance your own VR content in Unity. In doing so, we will cover:
We prepared a simple scene that provides you with everything so that you can directly get started with learning how to apply randomization to a VR Builder project. You can find a Unity package with this scene and assets here. Alternatively, you can follow along with your own assets. Since in this scenario users will have to adjust the pressure of the tires, we created a tool that displays the current pressure of the tires and can increase and decrease the pressure on user input.
The initial scene contains the following assets.
This tutorial does not cover in detail how the pressure tool was built, but it will point out the parts relevant to VR Builder, and its functionality could be easily replicated with some basic scripting.
Since the scene and assets are already taken care of, let’s start by constructing the process in the VR Builder Process Editor. First, let’s define what this process will actually do.
We will split the process in two chapters. In the first, a random scenario is selected and the user is given control. At the end of the tutorial, it will look like this.
The second chapter is about the evaluation. Since we are evaluating the state of each tire, this section has quite a few repetitive steps, so it makes sense to separate it in a different chapter.
Time to get started!
Let’s start by creating the main step where the user will actually interact with the scene. As this is a sandbox environment, the user will be able to snap and unsnap the tool freely. We will need to set up VR Builder’s restrictive environment so that they’re able to do so. The end condition for this step is putting the tool in the box on the table. When that happens, the user gets evaluated and the process ends.
Let’s start by creating a step and calling it Check Pressure. We don’t need to connect it to the start node, as we will add some logic before it.
In the Transitions tab, add the condition Environment > Move Object in Collider. Select the pressure tool as the object to be moved, and the box as the collider. This way, the step will end when the tool is put in the box.
Next, move to the Unlocked Objects tab. Since there is no condition specifically asking the user to grab or snap the pressure tool, it will not be possible to do so by default. In addition, we placed four snapzones in the scenes, one on each of the car's tires. This allows the user to attach the tire pressure tool to the different tires. By default, these snap zones will be inactive as well. We can override this behavior here. Drag the pressure tool and the four snap zones in the list, then check all properties on them. Now it will be possible to interact with the tool and snap zones during this step, even if they are not referenced in a condition.
Now this is done, we can dive into the Randomization add-on and create the actual content.
When the process starts, we want it to select one out of several possible scenarios. A scenario is configured in a few steps before handing control to the user. This means we will have a few groups of steps, each corresponding to a scenario, and we’ll want the process to select a random group each time. The right tool for the job is the Random Branch node: a special node that immediately selects one transition at random. We will create one with three transitions, each of which will lead to a different setup.
Let’s start by creating a Random Branch node. After installing the Randomization add-on, this is available on the right click menu, just under the standard Create Step entry. Let’s create this between the start node and the Check Pressure step, which we can move further to the right for now.
Once the node has been created, let’s click the + to add one more transition, and rename the node to Select Scenario.
The number fields on the transitions represent the weight of that specific transition relative to the others. By leaving these to the same default value, every transition has an equal chance of being selected. Weights can be set to any arbitrary value above zero, and the probability of a transition to be selected is proportional to its weight. Feel free to experiment with different weights if you want one scenario to be more probable than another.
The next two steps will take care to set up the scenario, and will have the same function (but different contents) in all transitions. The first step will randomize the pressure in the tires, while the second will set the targets and deliver the TTS instructions. Since there are no conditions between the first and second steps, these could indeed be condensed in a single step, but we’ll keep them separate for clarity.
The first step in the set up will randomize the initial pressure of the tires in a specified range. This will ensure the playing field is different and unpredictable every time, while still giving us control in specific scenarios.
Tire pressure is stored in a Number Data Property on every snap zone for the pressure tool. We have not assigned a default value to them, as we want to set them to a random value every time the process is started. Create a new step and call it Set Random Pressure, then open the Behaviors tab.
There you will need to add four behaviors to randomize the pressure of all four tires. Add four Set Random Number behaviors from the Randomization sub-menu. Drag each of the snap zones in the SnapZones game object to one behavior, and set a minimum and maximum value for each of them. We set them to the probably unrealistic limits of 10 to 50 psi. It doesn’t really matter as we’ll edit them later depending on the scenario.
We don’t need to tick the Randomize Integer box as we are looking for a float value within the range - that option would give us random integer values from 10 to 50 instead.
Let’s copy and paste this step two times, and connect one to each of the transitions from the Random Branch node. Even if now they are the same, we will be able to edit and customize them for specific scenarios later.
The second step in the set up is about objectives. Depending on the scenario, the user needs different instructions. They also have different objectives, so we will store the target pressure values in data properties here.
Create another step and call it Briefing. Like before, add a few behaviors to it: one from Guidance > Play TTS Audio and four from Randomization > Set Number. We don’t want random numbers this time, as these will be the target pressure numbers.
Then, find the four pressure limit objects under the DataProperties game object.
Drag one of these properties to each of the four Set Number behaviors.
This is a template for all three scenarios, so now is a good time to copy/paste it to all three branches. The Play Text to Speech behavior will be used to provide instructions to the user. The Set Number behaviors will set the acceptable range both for front and back tires. We want to have some tolerance, so if the instructions say 30 psi, we will have a lower and upper limit of 29 and 31 respectively, for example.
Time to edit some values!
Time to work on the content for our process. We want three random scenarios to select from. Let’s edit the Set Random Pressure and Briefing nodes accordingly. Here are some examples, feel free to come up with different ones!
Briefing: This car hasn’t been used in a while and the tires have low pressure. Inflate them to 32 psi on the front, and 30 on the rear.
Since the pressure should be pretty low, let’s set the random pressure of each tire between 15 and 30 psi, so there is a chance that some tires are still ok.
The lower and upper limits in the Briefing step should include some tolerance, so let’s set those to 31 to 33 for the front, and 29 to 31 for the rear.
Briefing: It's a hot season and the pressure in the tires is too high. Reduce it to 28 psi in the back and 30 in the front.
Let’s set the starting random pressures to 30-40.
Considering tolerances, the lower and upper limits should be 27-29 for the rear tires and 29-31 for the front ones.
Briefing: The rear left tire has just been replaced but it could have the wrong pressure. Check the other rear tire and make them match.
Here, we want just a single tire to have a random pressure. Let’s set the random pressure to 32-32 for the front tires, and 30-30 for the rear right tire, so they will always be 30 or 32. The rear left one can be anything, so let’s leave it to 10-50 for fun.
Let’s use again the limits of 31-33 for the front and 29-31 for the rear.
Now we have input the data for the possible scenario, it’s time to start implementing the interaction part.
It should now be possible to run through the process. However, nothing will happen when the user puts the tool back in the box, the process will just end.
To rectify that, we want to check all tires one by one and give feedback if the pressure is too high or too low, then wrap up a conclusion. Doing this will require a few steps, so it can be cleaner to create a new chapter. Let’s do so by pressing the Add Chapter button, and rename both chapters while we are at it.
We are starting with evaluating the front left tire pressure, so let's create a step called Evaluate Front Left. In the conditions tab we are checking if the pressure is too high or too low. If the pressure is neither too high or too low, it is correct and we can proceed to evaluating the next tire. So, in total, we need 3 transitions. Go to the Conditions tab, and add two more transitions. Add two Randomization > Compare Numbers condition to the first two transitions, and set them up as follows.
We are using the Compare Numbers condition to compare two data properties: the one that stores the tire’s pressure, and the upper or lower limit that has been set in the briefing. This allows us to use the same evaluation logic regardless of the scenario that was selected, and of the actual targets.
First we will check if the pressure is below the lower limit. If so, we will communicate it via TTS in the next step. Then, we check if the pressure is above the upper limit, and again we will notify the user in the next step. If the third transition is reached, it means the pressure is within specification, so we will move directly to checking the next tire.
The structure we are trying to create will look like this, repeated for all four tires.
Let’s create the Pressure too Low and Pressure too High nodes now. For now, these will just notify the user that the pressure is wrong on a specific tire. Let’s just add a TTS message like the following one.
Let’s repeat this for all four tires, making sure the correct data properties are referenced in each step and the TTS messages refer to the correct state. If we run the process now, the user will be notified of all tires that have a wrong pressure. But they won’t hear any message if they did everything correctly. We will rectify this in the next and final chapter.
We want to add one last TTS message that wraps up the process, so the user will hear something also when they did everything right. To do so, we can make use of a Boolean Data Property which stores whether the user did everything correctly or not. In the DataProperties game object there is a child called AllValuesCorrect. As we can see, its default is set to true in the inspector.
Our approach is that the user did everything correctly until proven otherwise. If an evaluation step checks pressure too low or too high, it sets this property to false. Then, the final evaluation step will check the property, and deliver a different message depending on whether it’s true or false.
Let’s start by adding a Randomization > Set Boolean behavior to every one of the eight “wrong pressure” steps.
Let’s reference the property AllValuesCorrect, and set the value to false.
Now let’s create a new step at the end of the process and call it Final Evaluation. This step will check if the AllValuesCorrect property is true or false, and lead to a different behavior playing the final sentence.
We will use the Randomization > Compare Booleans condition to check the data property.
This works similarly to the Compare Numbers condition we already used to check the pressure, except this time we are not comparing the data property with another property, but with a constant value. So let’s select Constant value as the right operand, and set the value to true. If the property is true, the first transition will be selected. Else, it means that it has become false, and we will trigger a message for a failed process.
Finally, let’s create two new steps for the final messages - these will only contain a single Guidance > Play TTS Audio behavior, delivering one last line to wrap up the experience.
The text could be something like “Congratulations! All tires have been set correctly!” for the All Values Correct step, and “You made some mistakes. Run the application again to try another time!” for the Mistakes Were Made step.
Now the process is fully functional. Press Play and try your best! Below are presented some ideas to expand it further using VR Builder and its add-ons.
This simulation could benefit from tracking the time it takes to complete the operation. If you have our Track and Measure add-on, you can create a timer that starts with the Check Pressure step and ends in the next step. You can then use the acquired data to make a deeper evaluation.
You can easily randomize the affected wheel by using a second Random Branch node and providing different scenario setups.
If you have the States and Data add-on, you can also randomize the pressure to be matched. In this case, instead of hardcoding all values, you can set the tire to be matched to a random value. Then, use Arithmetic Operation behaviors to set the target limits to that value + or - 1, or whatever value you want to use for tolerance.