Now that we have learned to master Unity to develop an application for HoloLens, we get to the more advanced scenarios. In this part, we go into threading and the use of plugins.
Threading
I was very surprised when I started developing for mixed reality devices like the HoloLens using Unity. Unity is a single-threaded computing application. For me, working mostly with multithreaded application development, it was like going back in time. Why on earth is this application single-threaded? Are there even benefits to using such a system? Yes, there are! There is a very good reason for it.
Multithreaded applications take advantage of the capabilities of the CPU of your computer system and use parallelism. It allows you to process many different threads to execute all kind of different tasks. Because these threads run in parallel, programming applications using that are somewhat different than normal. Running multiple threads allows you to divide different tasks in your application at the same time. The main thread from which those threads are started gets the results returned. Because you never know which thread is finished first, you must consider that when handling the results of those threads, especially when tasks or results are depending on each other. There are several advantages of having different threads running parallel. Your main thread, which mostly runs your user interface, will perform when, for example, a thread is executing a lot of calculations or retrieving some information and is depending on the speed of the current connection. Another advantage is the improvement in performance by simultaneously executing the computation and I/O operations. Also, when correctly used in code, it minimizes the number of system resources used. Finally, it allows simplifying of programming structure. Since threads run a specific task, the code is written in the context of the task and its used objects. This creates better and more readable code.
Using multithreading works well if the number of tasks is low and manageable overseen. Code written for game development mostly contains a great number of small tasks to be executed at once. Using multithreading, a thread per task, would cause many threads with a short lifetime running simultaneously. That can push your processor capacity to its limits and will result in performance loss. This is caused by something called context switching. Context switching is the process of saving the state of an executing thread, switching forward to another thread to give it computing power, and switching back again to the saved thread. In other words, multiple threads are sharing the same computing space.
Coroutines : These methods can be executed in pieces by borrowing time from the main thread.
Plugins : Plugins allow you to create additional UWP libraries, which can be included in your project and can execute multithreaded tasks.
Type of task | Option |
---|---|
Complex calculations and algorithms | Plugin |
Machine learning & AI services | Plugin |
Game related code | Coroutines |
Small tasks within the user interface like animations, changing values over time and other | Coroutines |
File operation access | Plugin |
Accessing web services depending on network communication and service provider | Plugin |
Coroutines
As explained earlier, the void Update() method of each active GameObject is called for each frame displayed by Unity. With normal behavior, this method is called approximately 30 times per second. A coroutine can pause and continue its execution over each of those frames displayed by Unity. This allows a coroutine to spread the complete run of a method across multiple frames, which are also used by Unity in its messaging system to call each void Update() method of each active GameObject. This allows coroutines to run their execution concurrent to the system. But keep in mind that this is still running in a single main thread and is not comparable to multithreading.
If we run the method at once from the void Update() method, it would be completely executed within a single frame update. That means that we wouldn’t see the actual animation and we are holding up other GameObjects that need to be updated too in that same frame update. Hence there is the possibility of freezing objects.
All the values of properties are kept in the same state during the pause of the method. This allows you to spread the execution of the code in the for loop of that same method across multiple frame updates.
When the method that is called from the Coroutine is finished without a yield
Calling the method Monobehaviour.StopCoroutine with the name of the specific coroutine
Calling the method Monobehaviour.StopAllCoroutines will stop all running coroutines for that GameObject
When the MonoBehaviour object is destroyed
When the attached GameObject is disabled
In the preceding example, we wait a tenth of a second before we continue the method. Keep in mind that this is in the next frame update after that specified time.
Plugins
Managed plugins : These plugins are managed .NET assemblies. They only support features that are supported by the .NET framework against which they are compiled. There are different forms of these plugins depending on for what platform you are building an application.
Native plugins : These plugins are platform-specific libraries using native code. This allows you to call operating system- or platform-specific functionalities. Native plugins will perform better than managed plugins, since they are optimized for the specific platform you are developing for.
This book focuses primarily on managed plugins for our application.
Since Unity version 2019.2.x and higher, only IL2CPP scripting backend is supported for building projects. Unity has stated that it is almost impossible to maintain two different platforms, and therefore decided that scripting used in Unity is always compiled to IL2CPP. That means that building and deploying the solution from Visual Studio to a HoloLens device will optimize the application, since it is using C++. But it also means that debugging is not easy anymore as when you normally would debug .NET code in Visual Studio. Making any changes to the code requires you to fully generate the Visual Studio solution before you can run it on the HoloLens device.
It is common to use managed plugins when building applications for the Universal Windows Platform. In the past, we had plugins on .NET scripting backend. Nowadays we are encouraged to use plugins on IL2CPP scripting backend. But the latter requires extensive knowledge of C++ and is much more difficult to implement. While the first, plugins on .NET scripting, is part of the legacy documentation, it does not necessarily mean that you are not allowed to use them. For Unity, it shouldn’t matter which platform and computer language are used for building your plugin. And indeed, it is still possible to use these .NET scripting backend plugins. The classes and methods from these plugins can still be used from scripting in Unity.
The placeholder plugin is also based on a .NET DLL.
The placeholder plugin should have the same name as the first plugin.
The same namespace needs to be used.
The same classes and methods need to be used, but they can contain dummy code.
- 1.
Start Visual Studio 2019. The dialog as shown in Figure 5-1 will appear. If Visual Studio does not show the dialog, open it via the main menu File ➤ New ➤ Project.
- 2.
Type Class Library in the search field to find the project template for .NET standard related projects.
- 3.
Select the project template called Class library (.NET standard).
- 4.
Press the Next button to continue to the next step.
- 1.
Fill in a Project Name. Enter Office365DataHub as the project name.
- 2.
Select a location for your project. Since we will have multiple projects, it would be wise to put them all under the same folder. In my case this is the ImmersiveOffice365 folder. The solution name will be filled in automatically.
- 3.
Press the Create button to create the project.
Now let’s start writing some code. We are going to create a manager class. But we need to have only one instance of that class present in our project. Therefore we start creating a templated singleton class. The singleton pattern restricts the instantiation of a class to one true single instance. This allows us to always get the same instance of that manager.
You will notice that the project contains a generated Class1.cs file. We remove this file from the project before we start adding our own classes.
Create a new code Class file via the solution explorer using right-click the project and select Add ➤ New Item. Call the new class Singleton.cs.
Accessing a web service can take some time. It depends on the web service itself if any security handshake needs to be done and the amount of data to be transferred. Therefore, calling a web service is best done by using asynchronous calls. That means that we are going to call our method from a new thread. Since our method needs to pass some results back, we are going to use a callback method. Using a callback method allows us to pass a method as a property to another method. The definition of such a method is done by using the keyword delegate in the method definition.
We need to implement one more method called GetSomeInformation. This is the method that will call the GetSomeInformationAsync method from a new thread. This is also the method that will be publicly accessible and called from the scripting in Unity.
- 1.
Start Visual Studio 2019. The dialog will appear, as shown in Figure 5-4. If Visual Studio does not show the dialog, open it via the main menu File ➤ New ➤ Project.
- 2.
Type Class Library in the search field to find the project template for .NET standard related projects.
- 3.
Select the project template called Class library (.NET standard).
- 4.
Press the Next button to continue to the next step.
- 1.
Fill in a Project Name. Enter Office365DataHubStub as the project name.
- 2.
Select a location for your project. Since we will have multiple projects, it would be wise to put them all under the same folder. In my case this is the ImmersiveOffice365 folder. The solution name will be filled in automatically.
- 3.
Press the Create button to create the project.
You will notice that the project contains a generated Class1.cs file. We remove this file from the project before we start adding our own classes.
- 1)
Right-click the project and select properties.
- 2)
Change both the Assembly Name and Default namespace to Office365DataHub.
Include platforms – This defines which platforms the DLL is available too. Since it is a placeholder DLL, only the option Editor should be checked.
CPU – Any CPU
OS – Any OS
When the changes are made, press the Apply button to acknowledge the settings for the plugin.
Include platforms – Only the option WSAPlayer Is checked.
SDK – Any SDK
ScriptingBackend – Any scripting backend
Don’t Process – This option needs to be unchecked. Otherwise you will receive compiler errors when you build the solution from Unity.
Placeholder – Select the previously configured placeholder. It will be found, since it uses the same name as this .NET standard DLL.
When the changes are made, press the Apply button to acknowledge the settings for the plugin. Now both plugins are configured and ready for use.
The TextMesh from the HelloWorld! GameObject is retrieved in the Start() method and the text property of the TextMesh is updated in the Update() method.
The main thread used by Unity is not thread-safe. This means that all calls to the Unity API need to be done from the main Unity thread. Synchronizing data back from a separate thread requires another form of implementation.
This row will instantiate the Manager from the Office365DataHub namespace and calls the GetSomeInformation method. The result is returned in the OnGetSomeInformationCompleted.
When you start the scene in Unity, the placeholder plugin is used. You will see that, since the Hello World! text is replaced by the URL specified in the call to the Office365DataHub.Manager.Instance.GetSomeInformation method. If you build the solution to a Visual Studio solution, deploy it to a HoloLens device, the UWP plugin is used. This will show the first 100 characters of the page requested by the URL.
Using a plugin can be very useful, since you have a lot of functionality present that is normally not present in Unity. The downside is that you are not able to test such functionality from the play mode of Unity. On the other hand, you can test the UWP plugin extensively using a UWP test application outside Unity and the HoloLens device. It also allows you to still use C# code in the backend because Unity does not compile the UWP DLL.