/Hub-Tutorial-Unity-Project

Unity example project for learning and teaching purposes.

Primary LanguageC#

Innoactive Hub SDK Unity Tutorial

Important: This tutorial is a living project and is changed over time. We are happy about feedback and will provide support through support@innoactive.de.

This repository provides a step-by-step guide to start developing with the Innoactive Hub Unity SDK.

You will learn how to setup your Unity project with the Innoactive Hub and create Unity scenes, populate them with interactable objects and your own tools and how to persist them. Furthermore, we cover multiplayer capabilities, our extendable XML menu as well as the context menu and many more features.

The tutorial is split into chapters, where every chapter builds upon the previous ones. We do not want you to just read code but learn how to use the Hub by implementing yourself. Therefore, we provide stub code in (almost) every chapter and an explanation on what to do. Just follow the instructions of each chapter. Most chapters also come with a unitypackage which includes our suggested solution.

The tutorial is currently based on version 4.0.0 and will be kept up to date in the future. Note: It may or may not work with older and later versions of the SDK. If you encounter any problems, please contact us via the support portal.

Overview

The Innoactive Hub Unity SDK is based on VRTK to provide an easy entry point into Virtual Reality interactions as well as development for multiple Head Mounted Displays (HMD).

Additionally, Photon as a proven network technology is used to guarantee stable multi-user experience.

What you need to start

To complete the whole tutorial simply clone this repository into an empty Unity project. Additionally you will need:

  • Innoactive Hub SDK v4.0.0 as explained in Chapter 1
  • SteamVR Unity Plugin v1.2.3 [deprecated]
  • Photon Credentials provided by Innoactive
  • Innoactive Hub Client Credentials
  • Innoactive Hub Web Management Console Credentials
  • Innoactive Hub Reality ID
  • Unity 2018.3 (our recommended version)

Table of contents

  Chapter 1 Setup Unity project

            1.1 Importing dependencies

            1.2 Innoactive Hub SDK Wizard

  Chapter 2 Setup Unity scene

            2.1 Prepare scene

            2.2 Setup Hub scene

            2.3 Scene components

            2.4 Basic environment

  Chapter 3 Explore the basics

            3.1 Teleporter

            3.2 Prepare scene

            3.3 Open menu

            3.4 Spawn and grab objects

            3.5 Use tools and make use of their context menu

            3.6 Additional goodies

  Chapter 4 Get around your scene

            4.1 Restrict teleporter

            4.2 Restrict areas

  Chapter 5 Create interactable objects

            5.1 Basic interactable objects

            5.2 Snap drop zones

  Chapter 6 Flashlight: Create custom tool

            6.1 Create interactable flashlight

            6.2 Add logic to your tool

  Chapter 7 Context Menu

            7.1 Default context actions

            7.2 Custom context actions

  Chapter 8 Main menu

            8.1 Create your own menu

            8.2 Add tool to menu

            8.3 Custom menu actions

  Chapter 9 Innoactive Hub Backend Setup

    Note: You can only complete this chapter if you have access to the Innoactive Hub Backend

            9.1 Client config, login check and login scene

            9.2 Realities, Spaces and Environments

            9.3 Load backend content

            9.4 Access and populate your Web Management Console

  Chapter 10 Persistence

    Note: You can only complete this chapter if you have access to the Innoactive Hub Backend

            10.1 Save and load space

            10.2 Persist tools and objects

            10.3 Scene navigation

  Chapter 11 Multi-User

            11.1 Multi-user sessions and Photon config

            11.2 Make basic objects networked

            11.3 Make tools networked

  Chapter 12 Window System

            12.1 Window Factory

            12.2 Add Buttons

            12.3 Add to menu

  Chapter 13 Customize Controllers

            13.1 Create new controllers

            13.2 Controller config and controller config chooser

  Chapter 14 Spectator

            14.1 Setup a spectator

Chapter 1 Setup Unity project

After opening the Unity project you will find some folders like Materials, Models, Prefabs etc. in your project structure. You can ignore these for now.

Importing Innoactive Hub SDK

To start setting up your project, download your version of the Innoactive Hub Unity SDK from http://developers.innoactive.de/. Import the unitypackage by dragging it into your Unity project and wait till it is imported. Reminder: In this tutorial we use version 4.0.0.

The Hub SDK will be imported into Assets/Extensions which is also what we recommend. An error will pop-up which can be ignored for now.

Importing Steam VR

Download and import Steam VR version 1.2.3 from https://github.com/ValveSoftware/steamvr_unity_plugin/releases/tag/1.2.3. We recommend moving the Steam VR folder into Assets/Extensions, right next to the Hub SDK. You should then restart Unity to make sure everything was imported correctly.

Important: Steam VR version 2 which is available on the Asset Store is not supported by VRTK 3.3.0 as currently used in the Innoactive Hub SDK.

After the Hub SDK as well as Steam VR is imported we take care of the following error (if it is shown):

Assets/Extensions/hub-sdk/SDK/Utils/UnityDotNetCheck/UnityDotNetCheck.cs(6,0): error CS1029: #error: 'This Unity project is configured for .Net 2.0 subset, but the Innoactive Hub SDK requires full .Net functionality. Please go to "PlayerSettings > Other Settings" and change Api Compatibility Level to .Net 4.0'

Go to the Unity PlayerSettings and change the Api Compatibility Level in Other Settings from .Net 2.0 Subset to .Net 4.0. The error message can look a little different on your machine, thus we recommend to read it and use the suggested Api settings.

Note: For newer Unity versions (2018.3+), it is required to add the OpenVR package from the Unity Package Manager in order to get SteamVR running.

Note: After compiling another error might pop-up:

error CS2001: Source file `Assets/Extensions/hub-sdk/Extensions/VRTK/Assets/VRTK/Examples/ExampleResources/SceneResources/[001 - Interactions] ControllerEvents/Scripts/VRTKExample_ControllerEventsDelegateListeners.cs' could not be found.

This is unfortunately a really annoying error with too long filenames which cannot be handled by the system. The easy solution is to move your whole Unity project to a shorter absolute path like C:\Users\User\Documents\MyHubTutorial or simply delete the VRTK examples.

Hub SDK Wizard

To make your life easier we included the Hub SDK Setup Wizard which helps you to setup your project. You can find it by clicking on Innoactive > Hub > Setup Wizard. The Wizard will pop-up and show you a list of settings with errors and warnings. We can ignore most of them for now (especially because they are automatically created for you) but want to explain some important ones.

The Hub Settings are your central point for general settings within your application. You can set specific build settings, colors to customize your experience, standard menu actions etc. Most of this will be covered in later chapters.

The Hub Client Credentials are your personal credentials to access the backend which you get from your Innoactive contact person. It is recommended to use the wizard to populate these values but also possible to alter/include the client-config.json file in Hub-Tutorial-Unity-Project/Config. The file gets created as soon as the wizards save button is pressed.

As mentioned before Multi-User is based on Photon. To configure your multi-user settings we use another config file which you can also find in Hub-Tutorial-Unity-Project/Config (after you clicked on save in the SDK Wizard). For now multi-user is disabled but will be explained in Chapter 11.

So for now just enter your Hub Client Credentials and click on save on the bottom of the Hub SDK Wizard. The rest is handled for you. Just to be safe you should check your Photon Settings in Hub-Tutorial-Unity-Project/Config/photon-config.json and make sure hosting is set to "OfflineMode".

Chapter 2 Setup Unity scene

Open the scene TutorialScene in your projects Scenes folder. This is a clean standard Unity scene.

Since we are developing a Virtual Reality application based on VRTK start by removing the Main Camera in the scene and save. We will not need the standard camera.

Next we setup the scene so we can use all features of the Innoactive Hub. We took over this process for you to make sure you have everything you need and are good to go. Clicking Innoactive > Hub > Setup > Setup Current Scene as Hub Scene populates your scene with a bunch of GameObjects which we briefly explain.

Components

[HUB-BOOTSTRAP] is responsible for scene initialization - handles setup of persistence and multi-user, and loading of the correct space (especially for multi-user sessions).

[HUB-MULTIUSER] is responsible for managing multi-user sessions.

[HUB-PERSISTENCE] handles storing and restoring scene states (i.e. saving/loading).

[HUB-LOGIN_CHECK] ensures that a connection to the Innoactive Hub backend is possible. If not, it will redirect to a login scene.

[HUB-DEBUG_UI] provides a Debug Info overlay when pressing Ctrl+G.

[HUB-BUG_LOGGING] Allows to save Debug Logs when pressing Alt+B.

[HUB-VR-LAUNCHER-CLIENT] allows connecting to the Innoactive Hub Launcher, allowing to switch to different VR applications.

[HUB-PLAYER-SETUP-MANAGER] is responsible for setting up the cameras. This includes but is not limited to disabling the HMD if so selected in the player-config.json as well as enabling / disabling the high-resolution spectator view (also configured via the player-config.json) which shows an interpolated first person view in the native resolution of the attached monitor.

[HUB-MENU-SETUP] allows configuration of the menu to use, and also allows configuring the user menu.

[VRTK_Setup] manages the run-time setup and configuration of VR cameras and interactions.


IMPORTANT: Disable [HUB-LOGIN_CHECK] and [HUB-VR-LAUNCHER-CLIENT] for now. We will come back to those later in Chapter 9.

Note: Setting up the scene before importing Steam VR might mess up [VRTK_Setup]!

Environment

To make your scene a less empty space add a simple floor. You can find one in the Prefabs folder and drag it into the scene.

Solution: Find the initially setup scene in ChapterSolutions/Chapter-2_Setup-Unity-Scene.unitypackage.

Chapter 3 Explore the basics

This chapter is all about exploring the basic features of the Innoactive Hub and will show you what is possible even without writing a single line of code. Start the application by clicking the Play button in Unity.

First we cover the teleporter, since we all know getting around a virtual space does not work with just walking. We assume you are using a HTC Vive - button configuration may vary between HMDs. Press the Trigger (with your index finger) to show the teleporter and release to teleport to the circled area. Teleport around the floor a bit to get used to it.

Next let's focus on one of the core features. Touching the Trackpad will open the Main Menu. You will see different categories and can navigate through the menu by swiping right, left, up and down on the Trackpad. Navigate to Tools and spawn a Pencil tool by selecting it and pressing the Trackpad.

If no menu appears, make sure the HubDefaultMenuBundle is set in the Menu Settings inside the Hub-Settings of your project.

The menu grants you access to all kinds of Assets, like Tools, 3D Objects, Images, Videos, PDFs, other Scenes and many more. You can change and extend the menu as you like which will be covered in Chapter 8.

After you spawned the pen you can grab it by touching it (it will get highlighted) and then pressing the grip button on the side of your Vive Controller. We included an optional smart grab functionality which is enabled by default. You can either grab an object by keeping the grip button pressed or by quickly clicking the grip button. Try it out and see the difference. By clicking the grip button again you will release the object.

Using an object is as simple as grabbing one. If you released the pen, grab it again and use the Trigger to draw some lines in your virtual space. You will draw as long as you keep the Trigger pressed.

In addition to the Main Menu, which is a global menu, there is the object-based Context Menu. You can open an object's Context Menu by either touching or grabbing it and then touching the Trackpad of your controller. Besides default features like deleting and enable gravity you can add object specific behavior. For example you can change the pen's color, so newly drawn strokes will have a different color. Try it by deleting a previously drawn line and then drawing a new one in another color.

Just play around with the menu and spawned objects and see what you can do with the different things. For instance, spawn a basic shape from the objects menu, change it's color and physics and use bi-manual scaling to make it bigger and smaller (grab it with both hands and move the hands apart). Furthermore, you can change your own representation in VR by changing your avatar in the Avatar menu section. It is also possible to create your own avatar which is a more advanced topic and will not be covered in this tutorial.

Chapter 4 Get around your scene

As briefly mentioned in the previous chapter, using the teleporter is your way to get around the scene. But often it is necessary to restrict the user from going everywhere in the scene.

Start by dragging the Table and the BrickWall prefabs into your scene. We obviously do not want the user to stand on top of the table or the wall, so we want to restrict these areas. Add the script DoNotAllowTeleportingHere to both GameObjects to mark them. In VRTK's Policy List for teleport targets ([VRTK_Setup] > [VRTK_Scripts] > PlayArea > Script VRTK_PolicyList) you can see that the Check List as well as the checked elements are already set up to disallow teleporting on every object with the just added script. You can of course change that list and add new elements. When copying a string into the field it may happen that an empty space is added at the end. This will cause issues because the Policy List will not handle that properly and recognizes the entered string as different than the one you actually want to ignore. So make sure there is no empty space added.

Sometimes, instead of just restricting a user from accessing a certain area, it is required to hide what they can see. Keep in mind that people in Virtual Reality can physically walk as well as teleport. If the user teleports right in front of a wall of an apartment in the 30th floor and then just physically moves his head, he they might be able to see something you, as a developer, do not want them to see, like your skybox without a floor underneath. To avoid this, we simply fade out the user's view.

Let's create such prohibited zone right behind the previously added wall. Create a GameObject called Prohibited Zone, place it somewhere behind the wall and add a BoxCollider component to it. Scale the Collider so it at least covers the whole area behind the wall and make sure the Collider is a Trigger (to prevent it from influencing with the physics simulation). For reference, we placed our Prohibited Zone to (-3.5, 1.5, 0) and scaled it to (0.5, 3, 8). Additionally, add the FadeOutViewInCollider script to the prohibited zone and change its layer to IgnoreRaycast.

Now make usage of VRTK logic to be aware when the user is in the prohibited zone. Create a new GameObject under [VRTK_Setup] > [VRTK_Scripts] called HeadsetCollisionFade. And add the following components with the specific settings:

VRTK_PolicyList with Include, only check Scripts and as element FadeoutViewInCollider (which we added to the prohibited zone).

VRTK_HeadsetCollision with a collider radius of 0.001 and the just added PolicyList.

VRTK_HeadsetFade nothing to adjust here.

HeadsetCollisionFade with the added VRTK_HeadsetCollision and HeadsetFade and set the Mode to FADE_WHEN_INSIDE_COLLIDER.

Solution: Find the populated scene in ChapterSolutions/Chapter-4_Get-Around-Your-Scene.unitypackage.

Chapter 5 Create interactable objects

After we set up a basic scene where you can move around, this chapter covers the process of adding new interactable objects. The goal is to have an interactable wooden box in the scene which can be grabbed, highlighted, scaled and snapped to a predefined drop zone. Everything without a single line of code!

We start by creating the interactable box. Drop the Woodenbox prefab into the scene. Now we have to add some components.

Rigidbody with Drag and Angular Drag set to 3 and gravity disabled. Needed to use some physics on the object.

InteractableObject with IsGrabbable set to true, so we can actually grab the object.

VRTK_FixedJointGrabAttach with PrecisionGrab set to true. This allows us to grab the object at the exact position and not snap it to the controller.

VRTK_InteractHaptics with Strength On Touch and Duration On Touch both set to 0.1. This will give some feedback when we touch the object.

VRTK_AxisScaleGrabAction with uniform scaling enabled. This allows us to scale the object through bi-manual grabbing.

Finally rename the object to InteractableWoodenBox and save it as a prefab for later use. Run the application and see how you can now manipulate your interactable object.

Solution: Find all created prefabs and the populated scene in ChapterSolutions/Chapter-5_Create-Interactable-Objects.unitypackage.

Chapter 6 Flashlight: Create a custom tool

In Chapter 3 you learned how to use the pen and some other tools which are already included in the Innoactive Hub SDK. In this chapter you will develop your first custom tool - a flashlight. This will require your first lines of code.

Drop the already prepared flashlight prefab into the scene and make it interactable, just like in the previous chapter. Add Rigidbody, InteractableObject and VRTK_FixedJointGrabAttach, but instead of VRTK_AxisScaleGrabAction you can add VRTK_SwapControllerGrabAction, since we don't need scaling but want to swap between the grabbing hands. This time you also don't need VRTK_InteractHaptics necessarily.

Add a SpotLight component to the Flashlight, position it properly at the tip of the flashlight and change the color to make it a slight yellow. Run the application, grab the light and move around. You just created your first stupid tool.

Now add some logic.

Add the Flashlight script to the flashlight GameObject. Enable grabbable, usable and hold button to use to make your tool interactable on the InteractableObject. Set your spot light as Light to Toggle and the InteractableObject as reference, too.

Open up the Flashlight script in Assets > Scripts > Flashlight. Notice how it has a reference to an InteractableObject, so all we do is register to the interaction events to give it more functionality but still keep interaction consistent between all objects.

Note: You will find ToDo's in the already prepared scripts which describe how and where you need to add logic. Therefore, this written documentation will be kept a bit shorter but the intended behavior will be described. We left some basic concepts out but make sure to go through the complete script to understand the logic completely. The solution package will have the fully implemented script.

The flashlight should be disabled from the beginning and the size/spread angle of the spotlight should be set to its initial value as set in the Unity Editor. When the tool is used the light is supposed to be enabled. We also want the spread angle to be adjustable.

Add a SnapDropZone

Next, create a SnapDropZone to allow the object to be snapped to a certain position. Create a new empty GameObject and call it ChargerSnapDropZone, assign it a SphereCollider with center (0, 0, 0) and radius 0.1 and make it a trigger. Place the SnapDropZone on top of the table, for example at (-0.5, 0.9, -2). Additionally, assign a ConditionalSnapDropZone component to the object, set Snap Duration to 0.2 and Highlight Always Active to true.

Now you need to add a prefab which is shown as highlight object for the SnapDropZone. Simply duplicate the Flashlight prefab, rename it to HighlightFlashlight and assign the FlashlightHighlight material which is just a transparent material without any textures. In the Highlight Object Prefab of the FlashlightCharger, choose the newly created prefab.

Finally, choose colors for the both highlighting (blue) and valid highlighting (green) with transparency. Run the application and see what happens when you grab the flashlight, move it close to the SnapDropZone and release it when marked as valid.

At this point, the SnapDropZone accepts all kind of objects, including pencils and other tools. In order to restrict it, add a HasLabelsConditionComponent and add "Flashlight" to the expected labels. Now the SnapDropZone will only accept objects with a Labeled Component containing the given "Flashlight" tag. Make sure to add the Labeled Component to your flashlight prefab as well and add "Flashlight" to the Labels List.

Optionally, a IsWithinAngleConditionComponent can be added to the FlashlightCharger in order to force the user to put the objects in an approximatelly correct orientation. 45 is a good value for the angle threshold.

Note: Custom conditions can be easily created by implementing the ICondition<T> interface. For more information, checkout the Conditions Documentation.

Charge the flashlight battery

As you may have noticed, the flashlight battery is not fully loaded and prevents us from using it for a long time. Let us make the FlashlightCharger charge the flashlight while it is snapped. For this, simply add the BatteryCharger script. This script is already fully implemented and serves as a reference on how to link objects with snap drop zones.

In order to make the charger look better, add the FlashlightRecharger mesh from the Assets > Models > FlashLightRecharger > Meshes folder as a subobject to the SnapDropZone.

Solution: Find the implemented script and the populated scene in ChapterSolutions/Chapter-6_Create-Custom-Tool.unitypackage.

Chapter 7 Context menu

As you probably noticed, you can turn the flashlight on and off but not adjust the spread angle or even delete the flashlight. That's why this chapter covers the implementation and extension of the object-based ContextMenu.

First give the default context actions a look and add the DefaultContextActions component to your flashlight. This will also add the ContextMenuHandler automatically. Disable Use, Scale, Clone and Align, which we do not need. You will see now that those default actions are shown in the context menu of the flashlight.

Open the Hub-Settings which were created in Chapter 1. Here you can change the default color of your menu items for all objects and the default context actions for objects spawned from the menu.

After making sure all default actions are working, let's take care of customizing the context menu. Open up the FlashlightContextMenu script and go through the ToDo's. Notice that the menu inherits IContextMenuAction and has a priority field. The priority defines the position of the action within the menu.

The desired look are two new added buttons with the default design. One button to increase and one to decrease the spread angle of the light, you can use the icons we provided in Assets > Resources > Icons and call the buttons Decrease Angle and Increase Angle. Additionally, the spread angle should be capped at a maximum and minimum value.

Finally, add the script to the flashlight object.

Solution: Find the implemented script and the populated scene in ChapterSolutions/Chapter-7_Use-Context-Menu.unitypackage.

Chapter 8 Main menu

You now created your first fully functional tool as an object in the scene which is always at the same position. The goal is to be able to spawn the flashlight directly from the Main Menu just like all the default tools.

To make the main menu adjustable and extendable we decided on a XML based solution. You can read everything about it in the Main Menu Documentation. In this tutorial we will just cover some basic concepts.

You need to create your own menu first. To do this choose Innoactive > Hub > UI > Create XML Menu to open the menu creation window. The Menu Bundle ID is basically the name of your menu. For this tutorial set it to Tutorial. You also want to make use of the default Innoactive Hub menu to reduce work and to get the same behaviour. Therefore, leave Inherit from other Menu enabled and choose Hub for Bundle Id and MainMenu for Base Main Menu. This simply, as the name says, inherits from the standard menu and provides a good starting point for you.

After you clicked Create Menu the XML description of your new menu will be created in Assets > Menu > Resources. Additionally, a ScriptableObject TutorialMenuBundleSource is created in Assets > Menu. Next choose Set Menu for current Scene. You will see in your [HUB_MENU_SETUP] GameObject that your XML menu is set in the Runtime Menu Setup. This currently is just a copy of the default menu. Open your menu and also the default Hub menu (Assets > Extensions > hub-sdk > SDK > UI > Menu > Resources > Menu > HubDefaultMenu.xml) for reference. Make sure you have a MainMenu section which extends Hub.MainMenu. You can leave it empty but need to provide this section. Make sure to check Hub Menu Settings Innoactive > Hub >Setup > Highlight Hub Settings. The Menu Bundle Sources Size should be set to 1 and the the TutorialMenuBundleSource should be added as an Element.

Extend the menu

The first step to extend your menu, is to add your flashlight as a new tool. Before we can do this we need to add it as a Resource. Remove the Default Context Actions (but leave the custom ones) from your flashlight object in the scene and save it as a new Prefab in Assets > Resources (create a new folder). We do not need the default actions anymore because they are automatically added when the object is spawned from the menu. You can delete the flashlight in the scene now. Keeping them on the object will overrule the automatic override which can be usefull when some objects should work differently than the default.

To properly show the flashlight within the menu a thumbnail is useful. Duplicate the clean flashlight from the Prefabs folder which does not have any scripts, rename it to FlashlightThumbnail and move it to the just created Resources Prefab folder. Reset its position to (0, 0, 0) and set rotation to (-35, -90, 0). This way it will look a bit more fancy when displayed in the menu. Also remove every collider on the object and all of its children.

Now open your TutorialMenu.xml and the TutorialMenu_ToDos.xml to see what is to do to show the flashlight in the menu and actually spawn it on click. As said before, we also suggest opening the HubDefaultMenu.xml for reference.

First extend the ToolsMenu section by extending as well as replacing the Hub.ToolsMenu to keep all its elements but to be able to add new ones. Then add a new MenuItem which is actually a DefaultResourceSpawnButton with an Id and make sure it is always placed first. Furthermore, include the flashlight resource, a description text ("Flashlight") and the thumbnail. This will do exactly what it says: Create a button with the thumbnail as icon and the text as title which spawns the created prefab on click. Hint: Keep in mind, you can always peek into the implementation of the default menu.

Run your application and try out your new tool spawned from the Tools section of your menu. You will also see that it has all context menu actions.

Custom menus

You can customize your menu even further by adding your own submenus to it. The goal is to have a submenu of the main menu that has multiple buttons which change the light settings of the scene, switching between day and night. Make sure to add the LightSetupController prefab to your scene, and delete the original Directional Light to avoid interferences with the new lighting. You can access the LightSetupController script by calling LightSetupController.Instance_. Open the CustomLightSetupMenu script which already inherits from Menu and IMenuProvider. Implement the ToDo's for Chapter 8 and ignore the ones from Chapter 12 which we will tackled later on to extend this feature.

To add new buttons to the custom menu use the Menu.Add() method with ButtonItem as MenuItem. For the Icon simply use the ResourceIcon with a descriptive text ("Day"/"Night") and the icons from Assets > Resources > Icons > day/night-icon and for the action make use of the SetLight method.

The logic of the menu is now done but you will not see it in the application yet. Jump back to the XML description of the menu and add a new MenuItem to your MainMenu. Provide an Id for the MenuItem which is actually a DefaultLink. A DefaultLink references a Menu and includes it. See the structure of the link in the HubDefaultMenu. For the Behaviour you need the provider which points to the Menu class (including its namespace) you want to add and for the Content add a text (in our case "Light Menu") and an icon where you can simply use the spaces icon (... > menu > Icons > SpacesIcon).

Run the application and open your menu. You will see a new entry at the end of the list. Navigate to it and make use of the two newly added menu functions.

Solution: Find the implemented script, menu and the cleaned scene in ChapterSolutions/Chapter-8_Use-Main-Menu.unitypackage.

Chapter 9 Innoactive Hub Backend Setup

This chapter is all about setting up your Innoactive Hub Backend and how to make use of it.

Note: Get your client credentials from your Innoactive contact person or you will not have access to the Web Management Console.

There are two ways to configure your client settings, the settings you need to make to have actual access.

The first and recommended option is to open up the SDK Setup Wizard again and fill in the Hub Client Credentials, if not already done in Chapter 1. You get the Hub URL, the Client Id as well as the Secret from your Innoactive contact person. The values you enter here will be copied into the client config file (Project > Config > client-config.json) which is also the second option. You can just include everything in this file directly without using the Wizard.

Enable the [HUB-LOGIN-CHECK] object in your scene and open up the Hub-Settings in your project. Include your provided reality into the Editor Reality Id field.

Note: The authorization with the backend will require loading a new scene, LoginSceneAuthCode. For this to work, both your tutorial scene and the login scene must be added to your project's build settings.

A reality is one unique version of your application. You can see realities as parallel universes. While in Reality A you have three pens, a flashlight and some drawings floating around, in reality B you only moved your box right next to the wall and have a measuring tape to check the distance to your table. Both realities build upon the same basic scene/application and exist next to each other but the states of the objects are different. This allows multiple people to work within the same application without interfering with each other. Realities do not know each other and within one application you cannot jump between realities!

Start your application and open your menu. You will see new entries which you can only use when you have access to the backend. Spawn some models, images or presentations from the backend just like you did with local resources. To populate your scene with your own objects open up your Web Management Console (WMC) online and upload a new model. When you are all done run the application again and see how your just added object is already available in the menu without changing the application at all.

Solution: Find the scene with activated login check in ChapterSolutions/Chapter-9_Innoactive_Hub_Backend_Setup.unitypackage. Note: You still need to setup your client config credentials by hand.

Chapter 10 Persistence

Persistence is the idea of loading and saving states of the current environment and one of the most important concepts of the Innoactive Hub. We call those states Space Versions. You can see a Space Version as a save state/game of your current space. Therefore, a Space Version can be saved as well as loaded and will be uploaded to the Innoactive Hub Backend to use at a different time.

To get into more detail, a Space Version has objects and those objects have properties. Each property defines a special state of the object like location, physics, color, grabbability etc. By default all spawned objects are persisted. Thereby, the PersistenceManager acts as the central component. You can save a Space Version explicitly through the menu. By default a Space Version is saved when you leave your environment or quit your application.

Note: Find more information in our official documentation. To allow persistence to work, one must have a scene object that manages the persistent scene. You probably remember that we already added such an object in Chapter two, the [HUB-PERSISTENCE] object. Feel free to take a look at it and make sure that it's active before you proceed.

Save and load

Loading is done by the SceneNavigationManager which has actions to load a certain Space Version by Id but typically the last saved Space Version is loaded. While being in multi-user, every user in the same room loads the same Space Version.

The goal of this chapter is to first save a Space Version and then load it again via the menu. Additionally, the flashlight from Chapter 6 will be persisted and you will learn how to switch between scenes without losing the changes made before.

Let's start by spawning some tools and moving them around. Then open the menu and save your Space Version. You can load the Space Version by selecting Load Previous State, choosing a saved state to go back to and putting the spawned space sphere over your head.

Make tools persistent

As you may have noticed, your flashlight tool keeps its position but not its last set state and spread angle. This might not be a huge issue for the flashlight, but for more complex objects/tools or for colored things it can be vital.

To make a tool persistent, it needs its own PropertyData as well as Translator. Open the FlashlightPersistenceData script which already inherits from PersistentProperty.PropData and carries the DataContract-Attribute. In here you actually just have to create nullable types (by adding a ? after the type like Color?) that you want to persist within your flashlight, so your state and your spread angle. Both get the DataMember-Attribute with unique names. As you can see PropertyData is more or less a container which holds the information you want to persist.

The translator does the actual work. Edit your FlashlightPersistenceTranslator which in this case is a SingleComponentPropertyTranslator with the FlashlightPropertyData as well as the Flashlight component. Start by returning the correct PropertyTypeName which is defined in the data.

The other two ToDo's are, simply said, one to get the data from the object and save it (UpdatePropertyFromComponent) and the other to load the object data which was saved before (UpdateComponentFromProperty). So in UpdatePropertyFromComponent set the property data to the components current values and in UpdateComponentFromProperty set the current values to the saved property data.

This now handles the storing and loading of data in general but will not be called by the PersistenceManager yet. We first have to register the translator. Open the TutorialPersistenceExtension script which inherits from PersistenceConfigExtension and add the FlashlightPersistenceTranslator to the config by registering it. You need to register every tool and object you want to persist and which required a PersistenceTranslator within the extension.

The last step to have your flashlight persisted is to add the TutorialPersistenceExtension component to some object in your tutorial scene (as well as in the Login scene).

Solution: Find the implemented scripts and the updated scene in ChapterSolutions/Chapter-10.1_Persistence_Tools.unitypackage.

Jump between scenes

Note: Make sure your TutorialScene as well as TutorialScene2 are added to the BuildSettings.

The last thing covered in this chapter is how to switch scenes. You can change scenes with the SceneNavigationManager in your code or, as done in this tutorial, through the menu. Open your XML menu again and extend your previously edited MainMenu further. Add two new entries which are basically the same but differ in the scene they are loading. Create a default button with environment condition which hides the button if your current environment/scene is the same as the one to load. Therefore, you cannot switch to a scene you are already in. For the behaviour use a local CommandBehaviour, loading the last saved space without saving when loading. Name the button "Go to Tutorial 1" and use the simple icon with the number 1. Do the same for the scene TutorialScene2 which you can find in the same folder and is an empty scene just to show how to switch scenes.

Solution: Find the extended menu in ChapterSolutions/Chapter-10.2_Persistence_Switching-Scenes.unitypackage.

Chapter 11 Multi-User

Up to now everything so far was in offline mode, so not networked and not intended for multiple users in the same application. Let's switch to multi-user sessions!

In this tutorial, three different ways of implementing networked methods are covered: event based RPC, continuous StateSynchronization and HubCommands. Every method has its own use case and is shown in this chapter.

The Innoactive Hub SDK is built upon Photon for networking and multi-user capabilities. To get started with multi-user, open your photon-config.json in the project's Config folder where hosting is currently set to OfflineMode. Change it to either SelfHosted or PhotonCloud depending on your preferences and update your appId (and serverAddress if you prefer SelfHosted) or use the photon credentials you were given. Save the config, find a friend and start your application to meet in the virtual world. You probably notice that the wooden box is not networked and moving it will only be done for the local user while objects spawned from the menu are automatically networked.

Adjusting an object to make it multi-user ready is quite simple. Add an InteractableObjectNetworking component to the wooden box which automatically attaches a PhotonView. This will synchronize the transform between all users but the snapping to the drop zone is still missing. Continue by adding a SnapDropZoneNetworked component to the ChargerSnapDropZone, run the application and see how other people can manipulate objects within the same scene.

Flashlight networking with RPCs

When you spawn your flashlight you can see that it automatically gets an InteractableObjectNetworking and moving the object is networked but the state as well as the spread angle are not. Therefore, those special functionalities have to be synchronized by hand. Open the FlashlightNetworking script which already inherits from InteractableObjectNetworking and thus just the special behavior has to be implemented.

First, it is important to know when and what has to be networked and when other users have to be notified. Since it is not necessary to constantly synchronize the current spread angle even though it does not change, subscribe in OnEnable to the flashlight events created in Chapter 6 - and unsubscribe OnDisable. Within the class you find already two methods which should be called when the appropriate events are fired.

Let's focus on the callback method which handles the spread angle first which will make use of Photon's RPC. Here you can make use of the attached PhotonView and call its method RPC with the name of the method to call (in this case "RemoteSpreadChanged"), who is the receiver of this call (only other players, since the local user has already changed the angle) and the new angle which you can get from the passed arguments. That is all you have to do when making use of Photon.

The remote method is similar. It has a [PunRPC] attribute, so it is registered for each user and therefore can be called with Photon's RPC functionality. Here you just have to change the spread angle of the flashlight. The same applies to the RemoteChangeLightState. It gets the new light state and simply sets it in the local flashlight.

As you probably noticed you can get into a nasty recursion, since one user for example changes the spread angle of the flashlight and fires the matching event. Then the networked flashlight makes sure all other users update their flashlights which triggers the event again and therefore notifies the FlashlightNetworking which tries to synchronize its state with the other users. Thus we introduce a simple recursion lock flag which secures that things are only executed when they should. Hence, activate the recursion lock before you update the local flashlight, deactivate it afterwards and ensure that the callbacks are only executed when the recursion lock is inactive.

Finally, add your FlashlightNetworking script to the flashlight prefab and disable Synchronize Usage for testing purposes. Run your application and try your new networked tool.

Battery networking with state synchronization

In order to get the Battery networked as well, add the BatteryNetworking component to the flashlight and go through its TODO's.

Note: The battery networking works via Photon state synchronization. Check out the Photon Documentation for more information.

LightSetup networking with Hub Commands

Currently, the light setup change is not networked.

Open the LightSetupCommand class which inherits from HubCommand and go through its TODO's. A HubCommand is basically a command which can be configured and then executed for every user.

The command is now ready but still needs to be executed. Open the CustomLightSetupMenu, remove or comment out the SetLightSetup implementation from Chapter 8 then create and execute a LightSetupCommand instead. Make sure to pass the lightSetupIndex to the command before executing it.

Executing the Hubcommand will automatically propagate to other users.

Solution: Find the implemented script and updated prefab in ChapterSolutions/Chapter-11_Multi-User.unitypackage.

Chapter 12 Window System

This chapter covers one of the many helper and utility features within the Innoactive Hub SDK which will make your life easier. Sometimes you might want to show a notification, dialog or error message for the user in the virtual environment. The WindowFactory will save you a lot of time and also keeps your messages consistent.

To demonstrate how the WindowFactory works, we have created the InfoWindow component. It is meant as example to give an idea about how to use it. Just drag it onto some object in your scene. When playing the application, a window should open after the defined delay time.

You might not like the current color scheme or it does not fit your company's style. In that case, customize the WindowFactory to your needs. Let's try by settings the background color to black, the text, title and button color to white and the border color to white. Let creativity run wild!

Chapter 13 Customize controllers

Currently, when you start an application, controllers with default behavior (default Innoactive Hub configuration) are loaded. This includes how buttons are mapped, how you grab things and how the teleporter works along with a lot of other settings. In some cases you might not want to show a menu or allow only the trigger to be used. Maybe you want to change the appearance of the teleporter or even want your left controller to be different from your right one.

In this chapter you will learn how to change the appearance of your teleporter to the default VRTK look.

You find configured controller prefabs in Assets > Extensions > hub-sdk > SDK > Interaction > Controller > Prefabs > Controller. We assume you are using the HTC Vive, so duplicate the Steam_Vive_LeftController as well as the RightController, move the copies to Assets > Controller (create a new folder if needed, the location does not matter though) and rename them to Tutorial_Vive_LeftController and Tutorial_Vive_RightController. These will be your new controllers whenever you start the application.

We have to add the controllers to the scene to actualy make use of them. Create a new PrefabControllerConfig in a new folder Config within the Controller folder. Do this by right clicking the folder and choosing Create > InnoactiveHub > Controller > PrefabControllerConfig. Rename the newly created ScriptableObject to TutorialControllerConfig. Set the name to Tutorial and drag your prefabs into the controller fields. In your hierarchy navigate to [VRTK_SETUP] > [VRTK_Scripts] > ControllerConfigChooser, expand the Configs section of the ChooseController script and replace the config of Steam VR + HTC Vive with your TutorialControllerConfig. From now on, whenever you start your application - or to be more precise this scene, since controller configs are in scene not in project scope - your new custom controllers will be loaded.

The next step is to customize your controllers to see the difference. As mentioned before we simply change the appearance of the teleporter. In both of your custom controllers change the VRTK_BezierPointerRenderer of the TeleportPointer child object. On the bottom of the script you find AppearanceSettings which you can replace with the prefabs in Assets > Extensions > hub-sdk > Extensions > VRTK > Assets > VRTK > Examples > ExampleResources > SharedResources > Prefabs > AnimatedBezierPointer.

Run your application and teleport around to see the difference. To also make use of the new controllers in the TutorialScene2 change the setup accordingly.

Solution: Find the new controller prefabs, config and updated scene in ChapterSolutions/Chapter-13_Customize-Controllers.unitypackage.

Chapter 14 Spectator

The Innoactive Hub is intended for business applications. Even though a user will train or plan in VR on their own they might have someone standing next to them without wearing a HMD, monitoring what the user in VR is doing. Therefore, the Innoactive Hub SDK offers the possibility to show what the user sees on a second display via a spectator. By default a spectator camera is created at run-time and shows what the user sees (with some additional information). Just start your application in the Unity Editor and find a Spectator Camera object in your scene. You can customize this spectator camera by setting your own in the PlayerSetupManager in the [HUB-PLAYER-SETUP-MANAGER] in your scene. Usually this is not necessary but the possibility is given.

You can enable and disable this spectator via the player-config.json which you can find in the Config folder of your project. Since this option is included in the configuration files you can also turn it on and off after the application is built.