Setting up a Unity-based application for HoloLens with MRTK
This article is part of a series with best practices from projects executed by a Microsoft CSE team I’m part of. Have a look at Learnings from developing a HoloLens Application to get the overview of the other posts.
To get started with a new Unity-based application for HoloLens there are some good tutorials like MRTK2-Unity Developer Documentation and HoloLens 2 fundamentals: develop mixed reality applications both from Microsoft. If you haven’t looked at them when you’re reading this, you’re should start doing that first (and come back later 🤓).
But like with any development environment, there some extra things you need to get started and have some overview of what you’re building. This is especially true for enterprise apps (which is often the case for HoloLens applications) where a dev team needs to collaborate on the solution. This document describes some best practices we build from experience. We also have a repo with source code and Unity packages to get you started quickly.
Using a Git repo
Like any development tool, Unity stores files for the application you’re building in a folder, but also extra files that are only needed locally. These ‘internal’ files might contain user settings or files that can be generated by building the app. We want to keep the Git repo as clean as possible without losing important files.
A very good and detailed walkthrough of the overall process for getting a Unity project in a Git repo can be found in this post: How to set up a Unity project in GitHub — Unity at Scale.
In our MRTK-Utilities repo we have applied these steps, which can be reused as well. The most important one is a good .gitignore. Feel free to reuse that if you just want to add it to your own solution.
One thing that we discovered worked best for us, is to store the MRTK version we’re targeting in the repo as well. This prevents problems of different developers using different versions of MRTK. It also enables a quick clone of the repo and setup of the project on a developer machine. To do this, once you have used the MRTK Feature Tool to install the packages, execute these commands to add the proper files to your repo. In the commands below assume that the root of the repo is the Unity project. If you have a subfolder-structure you must use the full path for the files.
git add Packages\manifest.json
git add Packages\MixedReality -f
If you have your .gitignore setup and added the MRTK packages, you can commit and push the changes to your repo. When you clone the repo now, MRTK is automatically populated.
Setting up the Main scene
When you have the basics of the Unity project setup and the first branch is merged with your main
branch, you can now go ahead and setup the main scene for the application.
First, we want to start with a good folder structure in our project panel. By default you have an Assets
folder and a Packages
folder in the root of that panel. The Packages
folder is all managed by Unity, we only focus on the Assets
folder.
When you develop an application, you might use external components and Unity packages (like MRTK) next to all the components you develop yourself. Most of these items are placed in the Assets
folder. To keep it clean, we decided to create an Assets\Application
hierarchy to contain all the things we develop ourselves in this application. We start with a small hierarchy with placeholders to make sure it ends up in our Git repo, and to make it clear to the team where to put new developments.
If you want to have a quick start setting this up as described below, we have made a Unity package available that will setup the basic structure with some extra functionality we will described in another post (settings, authentication, API access and more). If you want to use this package, you can download it here. All the sources can be found in the MRTK-Utilities repo.
We create subfolder in Assets\Application
for various parts, like Scripts
, Scenes
, Plugins
and more. Below you can see some imported folders under Assets
and a setup of the Assets\Application
folder:
To explain a bit more about what we mean with the subfolders:
- AppLogo — a separate folder containing all the logo variants. For more information see App icons and logos — Windows apps | Microsoft Docs
- Components — all application components with their own structure there. We elaborate on this below.
- Materials & Textures — materials and textures of the application (what else 😏)
- Plugins — all plugins for the application. This could be external plugins like MSAL, JSON.NET and more. This could also be a plugin part of the application, for instance logic developed in a .NET Standard 2.0 library.
- Scenes — all the application scenes.
- Scripts — all the application scripts.
The Components each have their own structure. Most of the time a component ends up as a prefab and has some logic in scripts. As an example, when we would have a DocumentViewer
component in our app, the hierarchy could look like this:
Now that we have this structure in place, we can create the Main
scene and store it in Assets\Application\Scenes
. You can use the menu Mixed Reality > Toolkit > Add Scene and Configure...
to setup the scene for MRTK. This will add some default objects in your project Hierarchy
. There is a placeholder game object called MixedRealitySceneContent
that can be the root for all the application game objects.
An Application Manager for the main logic
Now we have the structure in place, we want to add logic. In most applications there is some main logic we want to execute and use. Think of reading settings, authenticate the user and access a backend API for initial data. We have chosen to put this in a dedicated game object Application
that lives as child of MixedRealitySceneContent
.
We add the main logic in the ApplicationManager
class, which is stored (of course) in the Assets\Application\Scripts
folder. The ApplicationManager
is a MonoBehavior
with a basic implementation like this:
using UnityEngine;public class ApplicationManager : MonoBehaviour
{
public static ApplicationManager Instance { get; private set; } /// <summary>
/// Event fired when application manager is initialized.
/// </summary>
public delegate void Initialized();
public event Initialized OnInitialized; /// <summary>
/// Gets or sets the flag whether we're initialized.
/// </summary>
private bool _isInitialized = false;
public bool IsInitialized => _isInitialized; /// <summary>
/// On start of the game object.
/// </summary>
private async void Start()
{
_isInitialized = false; // make application manager available through static
Instance = this; // TODO: rest of the startup logic goes here // now let others know we're initialized
_isInitialized = true;
OnInitialized?.Invoke();
}
}
We have a public static variable called Instance
that is set to the itself on startup. Now other parts of the application can get hold of this manager with this code:
ApplicationManager app = ApplicationManager.Instance;
Now you’re set to start with the rest of the implementation of your application.
Conclusion
Once you have a basic setup, it makes it easier to decide where to store scripts and other assets.
In other posts we’ll explain how to add settings, authentication, API calls and more to the application. See Learnings from developing a HoloLens Application for the overview of the posts.