Setting up a Unity-based application for HoloLens with MRTK

Martin Tirion
6 min readAug 16, 2022

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.

Getting started

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:

the 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:

Components structured in the hierarchy

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.

Main Application game object

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.

--

--

Martin Tirion

Senior Software Engineer at Microsoft working on Azure Services and Spatial Computing for enterprise customers around the world.