Lighting Up Your Unity Game on Windows 10
C# GLSL Python Objective-C Other
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
Images Merge branch 'master' of… Mar 23, 2016
Source gamemanger offline for ads Mar 30, 2016
.gitattributes Initial binary check-in Mar 20, 2016
LICENSE Template added Mar 16, 2016 workaround to unity not refreshing firstpass Mar 29, 2016
UnityLightUp.pptx presentation tweaks Mar 30, 2016

Integrating Unity Games With Windows 10#


A great game should include both great game play and native platform integration that engages and aids the user to get the most out of the game. This lab will give you an overview of different techniques you can use to integrate with the Universal Windows Platform from within your Unity game; we will discuss some of the pros & cons of each approach so you can reuse these techniques across any integration scenario you may encounter.


In this module, you will learn about these concepts:

  • Understand the different #define pre-processor directives for writing native code on a Windows game.
  • Understand how to dispatch calls between Unity's app thread and Windows' UI thread.
  • Integrate with Windows 10 by writing inline code to your Unity game.
  • Integrate with Windows 10 by consuming and configuring Unity plugins.

Note: This module is optimized to show you integration techniques. Most of the Unity code (game play, menus, event handlers) has been coded for you; you will need to inject the native code at right places, but the module does not explain the 'glue' code we wrote in Unity. You will be able to walk through that on your own (as you have all the source). If you want to learn how to write the game, you can find very detailed video tutorials for building the game at Unity's learning site.


The following software is required to complete this module:

The following software packages are used within the module. These can be optional, as you can choose to skip the exercises, but covering all exercises is recommended for the most comprehensive learning; to do all exercuses, you should install these extra pre-requisites.


This module includes the following exercises:

  1. [Exporting Unity Project to Windows Store with C# Projects] (#Exercise1)
  2. [Using Unity's native integration helper libraries] (#Exercise2)
  3. [Adding WinRT inline code to a Unity game] (#Exercise3)
  4. [Configuring a reference to a Unity plugin] (#Exercise4)
  5. [Taking a bridge approach to integration] (#Exercise5)

Estimated time to complete this module: 60 minutes

Exercise 1: Exporting Unity Project to Windows Store with C# Projects

  1. Open the Ex1/Begin project in Unity.

    Note: If using default setup at the event, the folder is at C:\labs\CodeLabs-GameDev-2-UnityWin10\Source\Ex1\Begin.

  2. In Unity, let's prepare our project so we can build a player targeting UWP. You can do this via File-> Build Settings menu. Don't click Build yet, we need to configure a few options.

  3. In the Build Settings dialog box, select Universal 10 as target SDK.

  4. Select XAML as the UWP Build Type. XAML is used for most games as it allows you to bring XAML UI (like Web Views, and dialogs) into your game. In this case, we need XAML UI for showing ads in a later task.

  5. For this exercise, make sure you select the option to Export Unity C# projects; we will explain what this option does when we walk through the output from the build step.

    Build Action

    Build Action

  6. Before we click Build, we should also set our Player Settings. Click the Player Settings button.

  7. In player settings you can configure the name of your app, the splash screen, icons and tiles, orientation, rendering options, sensors, capabilities, and many other options. Explore all of these and feel free to change them.

    Player Settings

    Player Settings

  8. The setting that we are after in our case is under capabilities in the Publishing Settings section. Capabilities is a UWP concept, it declares an intent to use a protected resource. For our project, we need to add the InternetClient capability so that later when we show ads these can be downloaded from the internet.

    Player Settings Capabilities

    Player Settings Capabilities

  9. When done configuring player settings, click Build. You will be prompted to select a folder you are building to. Give it any project name. If you want to mimic our solution (not required) you can call it Win10Solution.

  10. The build process will take a few minutes, when it is completed, Unity will launch a Windows Explorer window to your build folder. Locate the Tanks.sln in that folder and open it in Visual Studio.

  11. Let's now explore the solution and projects that Unity created.

    Visual Studio Solution Explorer

    Exported Projects

    • Tanks (Universal Windows) is our game; this is the project we will submit to the Windows Store.

    • Tanks (Universal Windows) references Assembly-CSharp.dll, which is the game generated by Unity. This assembly has all our MonoBehaviours and game logic.

    • The data folder in Tanks project is where Unity put the projects' resources, assets, levels, etc.

    • Package.appxmanifest is the manifest (configuration file) for our project. In this file, you will find what we configured under Player Settings and Build Settings in the Unity Editor. There are more settings here, so do explore it.

    • The Assembly-Csharp and Assemby-CSharp first pass projects are what Unity usually builds in the Editor. In this case, Unity generated them because we chose the option to create Unity C# projects. These will be handy for today as they allow us to recompile Assembly-CSharp (our game logic) from within Visual Studio without having to rebuild from Unity. Of course that works if all we modify is the code; if we modify scenes or add resources or change player or build settings, then we must rebuild from Unity. If you rebuild from Unity to the same build folder, Unity will not override the code and settings for Tanks (Universal Windows) project. Unity preserves these so that any changes you make to the project are not overwritten.

Note: Since AssemblyCSharp has our game, that is where all files we edit today will be. There are two folders to look after:

  • Complete is the folder that has the original game. It is called Complete as that is the folder at end of Unity's tutorial. Our GameManager is there; we did tweak this file beyond Unity's tutorial.
  • CodeLab has a few scripts we added for the codelab. Our SocialDialogManager and the Microsoft Ads scripts are there.

Let's see it!####

Our game is already functional.

  1. To run it, change the target platform in Visual Studio from ARM to x86.

  2. Press F5 or click on the Local Machine button in Visual Studio to start the game.

Of course, our game right now has no Windows integration; we will add that in our next steps.

Exercise 2: Using Unity's native integration helper libraries

For native integration, Unity includes a few wrappers for Windows Store features like tiles, toast notifications, and launchers. These features are in the UnityEngine.WSA namespace.

Let's use the UnityEngine.WSA.Launcher APIs, to launch Help for our game, and to add a "rate us" feature to our game.

  1. In GameManager.cs (located in Complete\Scripts\Managers folder), there is already an Input handler for F1 key in the Update loop, so we can add the code to launch our help screen in the browser.

    void Update ()
    	if (Input.GetKeyUp(KeyCode.F1))
  2. This same technique can be used to implement the 'Rate us' functionality. There is already a "Rate us" button in the game. For demo purposes, it is coded to come up every 4th time you finish a round. So all we have to do is add code to the OnRateClicked event handler in the SocialDialogManager Behaviour (this file is located in CodeLab\Scripts\SocialDialogManager folder).

    public void OnRateClicked ()
  3. For our final example of this technique, and to illustrate a little more immersive integration, let's add live tiles to our game. At the end of each round, we can add a teaser message so users can come back if they quit game in middle of a round. Our GameManager already has a SetLiveTile method that gets called at end of each round. This method will update the text on our main live tile, and set an image to scroll with the tilea to make it pop with more interactivity.

    void SetLiveTile ( string textmessage )
    	 UnityEngine.WSA.Tile.main.Update( "ms-appx:///Data/StreamingAssets/TanksIcon_150x150.png" ,
    			"ms-appx:///Data/StreamingAssets/TanksIcon_310x150.png", string.Empty, textmessage);

Let's see it!####

We have added three features to our game. Let's now go see them in action!

  1. Viewing the Help file is easy. Any time during the game, press F1.
  2. To see the social dialog that prompt the user to rate the game, just play a few rounds. At the end of a round you will be prompted with a dialog to rate the app.
  3. The live tile is getting updated at end of each round; there is no UI to ask user if they want to update tile, etc. so play a round and if you want to see the code, set a break point in SetLiveTile. To see the live tile, you do need to have it pinned to your Start menu. You can pin the tile before, or after you have run the game.

Discussion around Unity WSA APIs

These UnityEngine.WSA.* APIs are convenient and easy to use. You did not notice this yet (we will cover it next), but there are threading requirements the API is abstracting and handling for you, we will explore these in our next exercise.

Unfortunately, the UnityEngine.WSA APIs do not handle all native integration scenarios. No worries though, there are more options, let's explore our next one: writing inline code in your Unity project to access WinRT APIs.

Exercise 3: Adding WinRT inline code to a Unity game

When Unity games target Windows Store (with a .NET backend, not IL2CPP), they are compiled using the .NET compiler and therefore can access WinRT APIs; as part of their build process, Unity links against WinRT libraries.

To inline WinRT code into your Unity project all you need to do is protect yourself so that Unity does not try to compile that code when targeting other platforms; for this, we will use the NETFX_CORE pre-processor.

Unity's documentation has more guidance on pre-processors defines for platform specific compilation. For today' exercises we will use these directives:

  • NETFX_CORE to filter for code that compiles against .NET Coreand links to WinRT.
  • WINDOWS_UWP to ensure code is used only on Windows 10, when APIs are Windows 10 specific.
  • UNITY_EDITOR to filter out code that only runs in the editor.

To exercise inlining code, we want to add support to enter and exit full screen mode when the user presses F11 in our game. The game is already listening for keyboard input in the Update loop in GameManager.cs, so we can add the code to enter/exit full screen there:

else if (Input.GetKeyUp (KeyCode.F11))
	 //Dispatch from Unity App thread to Windows UI Thread
	 UnityEngine.WSA.Application.InvokeOnUIThread( ()=>
		  var appView = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView();
		  if (appView.IsFullScreen)
	 } , false);

Here are the relevant details to notice from our snippet:

  • It is inside a #if NETFX_CORE && WINDOWS_UWP so other platforms don't call into it and Unity does not try to compile when targeting other platforms.
  • It is inside a delegate invoked by UnityEngine.WSA.Application.InvokeOnUIThread. Two of the most useful functions in the UnityEngine.WSA namespaces are:
    • Application.InvokeOnUIThread used to dispatch a call from Unity's app thread to Windows' UI thread
    • Application.InvokeOnAppThread used to dispatch calls from any thread to Unity's app thread.

In our case, ApplicationView.TryEnterFullScreenMode requires that it be called from the UI thread, that is why we dispatched the call, but not all WinRT calls need to be dispatched. For example, scheduled local toast notifications (which can be used to reengage users) can run from the Unity App thread and doesn't need dispatching. Our game is already stubbed to schedule a toast notification, it has a ScheduleReEngagement method in GameManager.cs, where we can add some notification code:

void ScheduleReEngageToast()
    if (needsSessionToast)
        string invite = "Your ammo is piling up. Let's battle!";
        DateTime dueTime = DateTime.Now.AddSeconds(40);

        var notificationXmlDoc = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
        var textNodeList = notificationXmlDoc.GetElementsByTagName("text");
        if (textNodeList.Count > 0)
            var toast = new ScheduledToastNotification(notificationXmlDoc, dueTime);
            toast.Id = (++toastId).ToString();
        needsSessionToast = false;

Notice that in this case, we only need a NETFX_CORE pre-processor, since these APIs work in Windows 8.x too. The full screen API we used earlier is Windows 10 only, that is why we had NETFX_CORE && WINDOWS_UWP. That said, for the code above to compile you need to add the right using statements near the top of GameManager.cs

using Windows.UI.Notifications;
using Windows.Data.Xml.Dom;   

Let's see it!####

To toggle in and out of full screen mode, just run the game and press F11. If you are curious, comment out the dispatcher call and call TryEnterFullScreenMode without dispatching and see what happens (ahem, crash, ahem).

To see the toast notification, just win a battle in the game and wait 40 seconds. You can even exit the game, the notification will fire even when game is not running. This way you could re-engage a user who has not played your game in a few days.

Discussion around inlining code

You have now seen how easy it is to inline WinRT code. Just understand and pay attention to your threading models, and you will be fine; it works great for WinRT and .NET APIs. However, what about calling custom libraries (such as an analytics library, or an ads SDK)? Of course, Unity allows that, via plugins. Let's cover that next.

Exercise 4: Configuring a reference to a Unity plugin

This section discusses referencing a managed Unity plugin; it focuses on the Windows' specific options to configure it and reference it. For practical purposes here, let's summarize a plugin as a .NET assembly or WinRT component (winmd) that we need to reference from within our Unity code.

If you want to dive deeper into plugins, please see this reference in Unity's documentation.

  1. Go back into Unity Editor.

  2. Add a reference to Vungle's Unity Package, which includes a managed plugin. To do this, select Assets -> Import Package -> Custom Package.

    Import Custom Package in Unity Editor

    Import Custom Package in Unity Editor

  3. Navigate to the folder where your VungleSDK.UnityPackage is located. If you are using the one pulled with this repo, it is in the Source/ExtraFiles/Vungle folder.

  4. Click Open to add the package to our Unity project.

  5. After selecting the package, you will see the Unity Import Dialog. In Vungle's case, they ship code for all platforms (iOS, Android, Windows). This is fine for us. Click Import to import all the files. Let's now review the important ones (for Windows Store and Windows Phone) configurations; the files are in the Plugins folder.

    • Notice the files are under a Plugins/Metro folder structure. Unity follows a specific folder hierarchy and naming convention, with a folder name for each different target platform. Starting with Unity 5, the folder hierarchy is not required, as Unity allows you to configure it manually via the new Plugin inspector, but for ease of use, it is still handy to follow. You can find out more about these folder names in Unity's Plugin Inspector documentation.

    • There is a UWP folder. This is used for plugins for Universal (Windows 10) Projects.

    • There is a WindowsPhone81 folder, since Vungle supports Windows Phone 8.1 too. We are not going to need that today, so delete that Windows Phone 81 folder and its contents.

  6. Let's now configure the rest of our SDK references, pay close attention as there is a lot details here. Go to the Plugins Folder and notice the VungleSDKProxy.dll. This DLL since is at the root of the plugins folder right now is applicable to all platforms. Click the dll to see it's import settings, ensure the plugin's selected platform is Editor (for this, uncheck the Any Platform box and check Editor). This will be used only when running in the Editor. Click Apply after you make your changes.

    VungleSDKProxy Import Settings

    VungleSDKProxy Import Settings

  7. Next, we can go into the Plugins\Metro folder and configure the VungleSDKProxy.winmd.

    • This winmd needs to target WSAPlayer (the platform for Windows Store).

    • Since we are only targeting UWP, select UWP under SDK.

    • The Scripting Backend should be Dotnet

    • Check the Don't process checkbox. Don't process is used to tell Unity that it should not patch the assembly. Unity needs to do extra processing to patch assemblies that have classes that need to be serialized in Unity; for most managed plugins that just add native integration, the Don't process should be checked as these assemblies rarely contain Unity serializable objects. To be precise, the setting is not required since we have a .winmd and Unity knows not to process these, but i wanted to explain the setting, so leave it checked. It this was a dll, then the setting would be needed.

    • For placeholder, point it to the VungleSDKProxy dll in the Plugins folder. This tells Unity to use that placeholder assembly when compiling to run in the Unity editor. We need placeholder assemblies because the Unity Editor is running Mono, it can't load .NET 4.5 assemblies (or WinMDs). Placeholders mimic the API signatures of the real assembly we want to use, but placeholders are compiled against .NET 3.5. that way every thing compiles within the editor (using placeholder assembly), and it also compiles when building to target UWP (using the target assembly ).

    • Here is our complete settings for our VungleSDKProxy in Plugins\Metro, so you can verify you made all the changes. Again, click Apply when you are done.

    VungleSDKProxy.winmd Import Settings

    VungleSDKProxy.winmd Import Settings

  8. Next, delete the VungleSDK.winmd file in the Plugins\Metro folder. The Vungle package includes this extra file to workaround an issue in earlier versions of Unity, but for 5.3.3 and later we don't need it. Do pay attention to the folder and make sure you only delete the one in Plugins\Metro, not the one in Plugins\Metro\UWP.

  9. Next, we can configure VungleSDK.winmd in Plugins\Metro\UWP using these settings.

    VungleSDK for UWP Import Settings

    VungleSDK for UWP Import Settings

    Note 1: If you are wondering why VungleSDK does not have a placeholder, it does not need it. All possible calls to this assembly are 'brokered' through VungleSDKProxy, so we don't need a placeholder dll for this.

    Note 2: If you want more details around these plugin import settings, Unity's documentation has a good overview of the import settings.

  10. By adding the Vungle plugin to our Unity project we have changed what Unity needs to compile, so before we go further, we should Build within Unity like we did on our first task.

Note: Unity is going to re-create our projects (.csproj files) for our three projects since it needs to update the references to these so they include VungleSDK. If you get a prompt in Visual Studio to reload the projects, accept the prompt, by clicking Reload All.

  1. After Unity builds, confirm the new Tanks.csproj has a reference to VungleSDK and VungleSDKProxy; if that looks right, rebuild all just to ensure it is all working correctly.

  2. Now that we have our references configured within Unity and have recompiled, we can call the code to show the ads. Let's go back to Visual Studio.

  3. To save you a little typing time (and since the logic is simple and not critical to Windows integration, the code is written, but excluded via a #if SHOW_VUNGLE_ADS pre-processor. Go to the top of the GameManager.cs file, and uncomment out the #define SHOW_VUNGLE_ADS line and let's now review how Vungle is getting called.

  4. In the Start function, we initialize Vungle and subscribe to adCompleted event.

    Vungle.init("com.prime31.Vungle", "vungleTest", "vungleTest");
    Vungle.onAdFinishedEvent += OnAdFinished;  
  5. Then, within OnAdFinished event handler, we call OnAdCompleted().

  6. Within OnAdCompleted we will reset the volume (since game background music gets muted prior to playing the ads). This OnAdCompleted event is worth looking at because it demonstrates using UnityEngine.WSA.Application.InvokeOnAppThread() to unmute the background music.

    void OnAdCompleted()
    	 m_isDisplayingAd = false;
    	 if (!UnityEngine.WSA.Application.RunningOnAppThread())
    		  UnityEngine.WSA.Application.InvokeOnAppThread(() =>
    		  }, false);
  7. In GameManager's RoundEnding function we call the ShowAd method, which first mutes the background music, then calls Vungle.showAd and sets a flag so the game loop knows to wait for ad to complete (m_isDisplayingAd).

With that, we are now ready to test ads in our game.

Note: If after uncommenting the #define SHOW_VUNGLE_ADS you get an error that looks like this: "The type or namespace name AdfinishedEventArgs could not be found" it means Unity did not refresh the reference to Assembly-CSharp-firstpass.dll. Implement this workaround:
1. Expand Assembly-CSharp project's references.
2. Look at the properties for the Assembly-CSharp-firstpass reference and locate the Path property. If you are using the //build configuration, the path is c:\labs\CodeLabs-GameDev-2-UnityWin10\Source\Ex1\Begin\UWP\Assembly-CSharp\bin\x86\Debug\Assembly-CSharp-firstpass.dll.
3. Once you have the path, in Windows Explorer, navigate to the containing folder and delete that dll.
4. Rebuild in Visual Studio.

Let's see it!####

To see Vungle ads, just run the game and win two rounds. After the second round, the ad will show up.

Discussion around plugin configuration

You have now seen how to integrate a managed Unity plugin or a WinRT component into your Unity project. Plugins are a critical part of code reuse and a frequent source of integration for games, as most games leverage shared analytics, ads, social networks, etc. All of these are powered by reusable plugins.

Exercise 5: Taking a bridge approach to integration

In the previous example, we consumed a plugin from within Unity by importing the binary (VungleSDK.winmd) into Unity and linking to it. Sometimes, we don't want to do this due to relationships or dependencies between the host app (in this case our Tanks UWP project) and the code we want to run in Unity; or maybe, including the binaries in Unity would be too cumbersome -often this happens if you have an SDK that ships as a nuget package and has a lot of platform dependencies, you end up needing ARM/x86/x64 dlls in Unity). For those situations, we use a standard software development bridge pattern where we can put an abstract defintion of what we want to do (most often an interface) on the Unity side and then leave the implementation to the target host project. Let's illustrate that by replacing our Vungle ads with Microsoft ads on our project.

The code has already been typed for you, it is just hidden under #if SHOW_MS_ADS pre-processors, so to run this scenario, comment out the #define SHOW_VUNGLE_ADS at the top of game manager and uncomment out the #define SHOW_MS_ADS.

// #define SHOW_VUNGLE_ADS  
#define SHOW_MS_ADS

Let's now review the implementation details on the Unity side.

  1. We first need an abstraction to the APIs we want to call. In this case, there is an interface typed for us in CodeLab\MSAdsBridge\IMicrosoftAdsBridge.cs.

    public interface IInterstittialAd : IDisposable
    	 void AddCallback(AdCallback type, Action<object> cb);
    	 void ClearCallback(AdCallback type);
    	 void RequestAndShow(string appId, string adUnitId);
    	 void Request(string appId, string adUnitId, AdType type);
    	 void Show();
    	 InterstitialAdState State { get; }
  2. Next, we need a way to connect our abstraction to our concrete implementation, in this case a factory class.

    public class MicrosoftAdsBridge
    	 public static IInterstitialAdFactory InterstitialAdFactory { get; set; }
    public interface IInterstitialAdFactory
    	 IInterstittialAd CreateAd();
    	 IInterstittialAd CreateAd( Action<object> readyCallback,
    								Action<object> completedCallback,
    								Action<object> cancelledCallback,
    								Action<object> errorCallback );
  3. Within Unity, we just check to see if we have a valid Factory when we want to instantiate our ads. We do this in GameManager's Start method like we did for Vungle ads. The most important code here is the check to see if we have a factory.

    if (Microsoft.UnityPlugins.MicrosoftAdsBridge.InterstitialAdFactory != null)
    	 m_showAds = true;
    	 m_MicrosoftAd = Microsoft.UnityPlugins.MicrosoftAdsBridge.InterstitialAdFactory.CreateAd();
    	 m_MicrosoftAd.AddCallback(Microsoft.UnityPlugins.AdCallback.Completed, OnMSAdCompleted);
    	 m_MicrosoftAd.AddCallback(Microsoft.UnityPlugins.AdCallback.Cancelled, OnMSAdCancelled);
    	 m_MicrosoftAd.AddCallback(Microsoft.UnityPlugins.AdCallback.Error, OnMSAdError);
    	 m_MicrosoftAd.Request(MicrosoftAdsAppId, NextMicrosoftAd, Microsoft.UnityPlugins.AdType.Video);

    Once ad is initialized, we can play the video on the same ShowAd method as before.

    if (m_MicrosoftAd != null)
    	 if (m_MicrosoftAd.State == Microsoft.UnityPlugins.InterstitialAdState.Ready)
    		  m_MicrosoftAd.RequestAndShow(MicrosoftAdsAppId, NextMicrosoftAd);
  4. As you can see, with the above we have code to instantiate ads and play ads in Unity, yet we have not added a reference to the Microsoft Ads SDK. Unity is completely abstracted/isolated from the SDK. We now need to add the implementation on the UWP project. In Visual Studio, in our Tanks project, add a reference to the Microsoft Universal Ads SDK.

    Add Ads SDK Reference in Visual Studio

    Add Ads SDK Reference in Visual Studio

  5. Next, we need to add the implementation of our IInterstitialAd interface. Right click on the Tanks project in Solution Explorer, Select Add->Existing Item and navigate to the ExtraFiles folder in this repo and select the MSAdsBridge\MicrosoftAdsBridge.cs file.

If you are using a //build PC, the ExtraFiles folder is at C:\labs\CodeLabs-GameDev-2-UnityWin10\Source\ExtraFiles.

  1. Finally, add the class factory so that the Unity code knows how to instantiate ads. This can be done anywhere, but for this kind of implementation, I always prefer to do it in the App.xaml.cs InitializeUnity; that feels like a good place to ensure we don't run into a race condition later.

    // right after appCallbacks.SetAppArguments(args); add our code..
    	 = new Microsoft.UnityPlugins.MicrosoftAdsFactory();

Let's see it!####

To see Microsoft ads in action, just run the game and win two rounds. After the second round, the ad will show up.

Discussion around bridge technique

The bridge pattern can be handy to avoid lots of platform specific dependencies (if you have a winmd that depends on native libraries that will be different for ARM/x86/x64) it can be easier to do it on the UWP side than on Windows. It is also easier to manage nuget references that auto-update themselves and can be restored automatically. This example mostly should give you context on the many options you have to integrate Unity games with native WinRT functionality; referencing .NET assemblies, nuget packages, etc. It all works, since we are just using Visual Studio to manage app and nuget references.

## Summary ##

Congratulations!! You have now successfully completed our lab and should be deeply familiar with how to add native functionality to your Unity game.

There is one topic we did not cover in this lab write-up (due to lack of time). Unity's Windows Store plugins documentation summarizes succinctly what you need to do, and if you want real-world examples with source code included, you can find plenty at repo.