October 14, 2019
Code & Design
A “must-have” for any VR experience, the ability to seamlessly transition between scenes can be implemented relatively easily through the SteamVR 2.0 plugin. Like the previous laser-pointer tutorial, the SteamVR plugin comes with a functional script for accomplishing a smooth load screen: SteamVR_LoadLevel.CS. Unfortunately, however, this script requires some minor setup & currently lacks rich documentation.
Mainly the script is straight-forward, the slight ambiguity lies in the triggering & timing mechanics. The tutorial below aims to make it perfectly clear how to implement the SteamVR _LoadLevel script with an emphasis on these two more challenging concepts. We’ll break down the tutorial into two modules:
I. Registering Scenes & Triggering A Scene Change
II. LoadLevel Timing Functions Overview
In order to properly test a scene transition, we need to setup a handful of components including a laser pointer, button, an event handler script, & two Unity scenes. If you’re up for an intermission read, head over here for the laser pointer tutorial, if not, feel free to download the scene zip found here.
The package above includes two scenes, a main scene & secondary landing scene. The main scene contains all functionality through an implemented laser pointer, a [CameraRig] prefab, the SteamVR_LaserPointer.CS script, an event-handler wrapper script, & a single button with an empty child GameObject (“TransitionPlaceHolder”). If you followed the previous tutorial, just delete the cube, create an empty new scene “SecondScene” & add an empty GameObject to the existing button (very important!), name it “TransitionPlaceholder.”
The overall plan of attack is to trigger the SteamVR_LoadLevel.CS OnAwake property by placing the script in an inactive, child GameObject (“TransitionPlaceholder”). When the user clicks the button with a laser pointer, an event-handler script will awaken the child GameObject, which in turn activates the LoadLevel script & transitions our user to a target scene.
Let’s Get To It!
Assuming an entry-level understanding of Unity, it‘s not immediately obvious how to prepare scenes for cross-scene switching. For the uninitiated, assuming you’re in the main scene, click on File -> Build Settings. On the popped out Build Settings panel, click the mid-top right “Add Open Scenes” button. Next, open up the second scene & repeat the process; both scenes should now be listed in the dialog box in the Build Settings panel:
This essentially registers both scenes whenever Unity packages & runs a build of your project — a prerequisite to implementing the scene transition either manually or through a helper script.
Next, we’ll setup the crux of it all, the SteamVR_LoadLevel script. Assuming you’ve imported the SteamVR plugin correctly, search within your project files for the actual script. Go ahead & drag it onto the TransitionPlaceHolder GameObject, which should be a child of our Button component (this is already set in the .zip).
Great. Only two more quick steps before we can run an unpolished transition. First, we need to setup at least* the following two properties within the SteamVR_LoadLevel script: Level Name & Auto Trigger On Enable.
The first property is a reference to the target landing scene, labeled “Level Name.” The second property controls when the script is triggered. We need it to run when the gameobject it’s attached to awakens; we can define this property by checking the box in the very last label “Auto Trigger On Enable.” Done. That entails the bare-minimum necessary customization needed to trigger a scene transition.
Next, we need to update our scene-wide event-handler script to make sure we’re setting the inactive placeholder gameobject to active. Again, when our button is clicked, we’ll set the currently-inactive TransitionPlaceHolder gameobject to active, which will trigger our scene transition. To access this gameobject we’ll create a public variable, placeHolder:
public void PointerClick(object sender, PointerEventArgs e)
if (e.target.name == "Button")
Debug.Log("Button was clicked");
Excellent, a simple SetActive() method is all that’s needed — this is now enough to fire up a basic scene transition. It’s certainly rough & displays default assets (such as the Steam loading screen), however, it should work for testing purposes. Go ahead & run this bad boy, strap on your headset & click the main button — you should experience a smooth scene transition!
Triggering a transition is as easy as awakening the gameobject that contains the LoadLevel script. The largest hurdle with customizing this script, however, stems from the ambiguity towards time-control, aka, “how long is the transition?” Controlling that transition is really unclear & the property variables aren’t very helpful; therefore, we’ll review the four exposed chronological methods that, through float inputs, control the timing mechanism of the transition. The following are the four properties exposed in the LoadLevel script panel:
A more appropriate name for this property is “Fade Out Time of Current Scene.” The functionality of this exposed method is pretty straightforward: how many seconds should it take to fade out of the current scene?
On the flip side, a more accurate name for this property is “Fade In Time of Future Scene.” With this context, the functionality of this exposed method is again much more clear: how many seconds should it take to finishing fading into the future scene?
While the former two properties handle timing & fading of the game scenes, the latter two properties focus on the timing & fading of the transition scene. The transition scene, as expected, contains the literal scene that users will briefly step-into as they transition to a future scene. This in-between transition scenes offers the ability to set a logo in this transition scene through the “Loading Screen” attribute.
The timing mechanics for this in-between scenes loading screen is controlled by two exposed properties: Loading Screen Fade In Time & Loading Screen Fade Out Time. The former, LSFI, controls the time it’ll take to fully fade the loading screen game object in. Note, this confused for me for a while, but for all intents & purpose, this property best reflects the overall length of the rendered loading scene.
The flip-side of the loading screen functionality, this property controls the time required to fully fade the loading screen gameobject out. Immediately after the LSFI is fully faded in, both the this property & the FIT property begin counting down.
Whew! As you can tell by the four definitions above, the timing mechanics of this scene are a bit tricky to figure out; you can remember & approximate the order of each of the properties with the following: FIT + LSFI + LSFO + FOT. It likely requires a ton of manual testing to nail down exactly the timing you’re seeking; however, outside of tweaking these properties, the rest of the functionality is set!
And there, we, go. With the example walk-through & timing breakdown included in this tutorial, implementing a smooth scene transition should be a relatively-simple undertaking. Of course, like any PreFab, the customization options on this provided LoadLevel script are capped & while it certainly serves it’s purpose as the standard for scene-transitions, there’s nothing stopping us from building our own system for transitions from scratch.
If you enjoyed this tutorial/series please consider supporting my indie project, CryptoSpace. It’s a VR blockexplorer with the goal of educating people on cryptocurrency projects with a hands-on, interactive experience: https://store.steampowered.com/app/1132550/CryptoSpace/