in Unity

Adding a trial experience in your Windows game built with Unity

The case has been made that if you offer a trial mode for your game, then your purchase rate will increase along with the increase in installation footprint. I agree and I know several developers who have increased their game’s revenue by organically growing their install base using a trial mode.

Fortunately, the Windows and Windows Phone stores each make it very simple to implement such an experience. Coupled with Unity who was gracious enough to include a simple API in its platform to check for a trial license from the store platform.

Step 1 – As in game, you will need to decide the basis of your trial experience. Is it going to be:

  • 7 days with unlimited access to features?
  • Unlimited with limited features?
  • Somewhere in between?

***Let me warn you – if you don’t give the player something compelling to do in trial mode, they will *not* purchase your game. I suggesting loading up your search engine of choice – preferably Bing – and doing some research on what works as far as trial experiences.

Once you’ve decided on what your super-awesome trial experience is going to look like, you can start to implement what you need in your code.

Step 2 – On to the code!

As I mentioned above, Unity has included a simple API for probing the license information for the game from the Store platform. I wasn’t able to find any documentation for the API on the Unity docs site, so I’ll just post the code definition here.

using System;
using UnityEngine;

namespace UnityEngine.Windows
{
    public sealed class LicenseInformation
    {
        public LicenseInformation();

        public static bool isOnAppTrial { get; }

        [WrapperlessIcall]
        public static string PurchaseApp();
    }
}

Somewhere in my game scripts, I will want to query this API to see if I need to setup my game to support trial mode or the player can just proceed with normal gameplay. Typically, in a Unity game, you will have some kind of GameController object that will persist across scenes. That is where I am going to put my code for the sake of this tutorial. So, let’s open that up.

We need to store the status of trial mode in a public static field, so that other scripts that query it to limit features in the game.

    public static bool IsTrial = true;
    void Awake()
    {
        GameObject.DontDestroyOnLoad(this); // cause this object to persist

#if UNITY_WINRT
        IsTrial = UnityEngine.Windows.LicenseInformation.isOnAppTrial;
#endif
    }

After I store the state, I can use it to draw a Buy button in my game’s GUI. When the play presses the button, it will call PurchaseApp() to launch the Store purchasing process.

    void OnGUI()
    {
        if (IsTrial)
        {
            if (GUI.Button(new Rect(50, 30, 100, 25), "Buy me!"))
            {
#if UNITY_WINRT || WINDOWS_PHONE
                var receipt = UnityEngine.Windows.LicenseInformation.PurchaseApp();
                IsTrial = false;
#endif
            }
        }
    }

At this point, I can’t tell that the string that PurchaseApp returns is anything at all. I get an empty string when I debug, but I am also not debugging against a game that is published in the store. It appears to just be a fire-and-forget style API, so I want to put in some logic to recheck the trial mode state when FixedUpdate runs, but only after 1 second has passed. So, while the app is in trial mode, we will check for a change every 1 second.

    void FixedUpdate()
    {
        if (IsTrial && (Time.realtimeSinceStartup - _lastTrialCheck) >= 1f)
        {
            _lastTrialCheck = Time.realtimeSinceStartup;

            // we'll detect if the trial state has changed
            if (UnityEngine.Windows.LicenseInformation.isOnAppTrial != IsTrial)
            {
#if UNITY_WINRT
                IsTrial = UnityEngine.Windows.LicenseInformation.isOnAppTrial;
#endif
            }
        }
    }

That’s it. Hopefully, you have enough information to implement a trial in a Windows game. Remember, make the trail mode fun enough where the players will want to purchase your game to continue playing and to support the great work that you’ve done!

Cheers!

Also, here’s the full source for my GameController class.

using UnityEngine;
using System.Collections;

public class GameController : MonoBehaviour {

    public static bool IsTrial = true;

    private float _lastTrialCheck = 0f;

    void Awake()
    {
        GameObject.DontDestroyOnLoad(this); // cause this object to persist

#if UNITY_WINRT
        IsTrial = UnityEngine.Windows.LicenseInformation.isOnAppTrial;
#endif
    }

    void FixedUpdate()
    {
        if (IsTrial && (Time.realtimeSinceStartup - _lastTrialCheck) >= 1f)
        {
            _lastTrialCheck = Time.realtimeSinceStartup;

            // we'll detect if the trial state has changed
            if (UnityEngine.Windows.LicenseInformation.isOnAppTrial != IsTrial)
            {
#if UNITY_WINRT
                IsTrial = UnityEngine.Windows.LicenseInformation.isOnAppTrial;
#endif
            }
        }
    }

    void OnGUI()
    {
        if (IsTrial)
        {
            if (GUI.Button(new Rect(50, 30, 100, 25), "Buy me!"))
            {
#if UNITY_WINRT || WINDOWS_PHONE
                var receipt = UnityEngine.Windows.LicenseInformation.PurchaseApp();
                IsTrial = false;
#endif
            }
        }
    }
} 

Write a Comment

Comment

  1. This is fantastic,thanks for sharing this. It’s really difficult to find examples like this one all over the internet! The problem i have is that the line UnityEngine.Windows.LicenseInformation.isOnAppTrial; returns always false. I downloaded my game as trial (off course not with my account because i already own the game) and the “Buy the game” button (witch supposedly exists in the main menu as long as the game is in trial mode) disappeared! In my script is like this
    – if(UnityEngine.Windows.LicenseInformation.isOnAppTrial)
    – if(GUI.Button(Rect(960, 15, 300, 90),””,styleButton))
    – Receipt = UnityEngine.Windows.LicenseInformation.PurchaseApp();
    Any ideas why this is happening? Is something wrong with my code? Thanks again!

  • Readers who shared this
  • Thank you!