With the rapid growth of AR, VR and game-based learning, I am frequently asked “How can I send xAPI statements from Unity?” I have always responded by pointing the questioner to GBLxAPI.org. As far as I know, this is the only xAPI library developed specifically for Unity. Since many of my customers have started developing in Unity, I thought I should investigate the GBLxAPI library for myself.

First, a couple of disclaimers:

  1. I have very little experience with Unity. If you are looking for a step-by-step guide to Unity, visit Learn.Unity.com. That’s what I did.
  2. For the purposes of this article, I am assuming you have a working knowledge of xAPI.
  3. This article is fairly technical. Development in Unity requires not only knowledge of Unity itself, but also the c# programming language. It’s hard to show you how to add xAPI to Unity without showing you some c# code.
  4. My examples were created using a pre-release of version 1.1 of the GBLxAPI library. It should be available by the time this article is published. Some things may have changed from the pre-release version.

Still with me? Excellent, let’s begin.

Step 1 – Create a game in Unity

If you already have a game in Unity, you can proceed to step 2. I did not have a game, so I followed the Roll-A-Ball example I found on Learn.Unity.com. I then added a “Logon” scene so that I could collect the player’s name to use in xAPI statements. I created a second level and added a timer so I could tell how long it took to complete the game. Finally, I added some dramatic (free) music and voila, I had a game. (Figure 1)

The Roll-A-Ball game from Learn.Unity.com

Figure 1: The Roll-A-Ball game from Learn.Unity.com

Step 2 – Get the GBLxAPI library

Now that you have a Unity game, head on over to GBLxAPI.org and register for the library. It’s free and you can download it right away. I suggest downloading and unzipping the library into its own folder. I cleverly called my folder “GBLxAPI”.

There is a blog article on getting started with GBLxAPI located here. Unfortunately, the “Setup” section is incomplete and misses a couple of steps. Here is what it forgets to tell you:

  1. Step 5 of the blog article tells you how to add your own xAPI verbs, activity types, etc. I could not get this to work, so I will show you how to add your own vocabulary items later in this article.
  2. The instructions also do not mention that you must copy the file “GBL_Interface.cs” into your game’s Assets/Scripts folder.

Step 3 – Setup your vocabulary

The GBLxAPI library comes with a built-in xAPI vocabulary. Verbs, activity types, etc. are defined in a default vocabulary file. You can add or override the default vocabulary with your own verbs, activity types, and other vocabulary items. The way this is supposed to work is that you edit a Microsoft Excel file, then run a Python program to generate the JSON files actually used by GBLxAPI. Although I have Python installed in my development environment, the process did not work. No files were generated and I received no errors.

The solution is that you can edit the JSON file directly, bypassing the Python program. The file is named “GBLxAPI_Vocab_User.json” and it is located under Assets/Resources/data. Here is an example file (Snippet 1):


{
    "action": {},
    "activity": {},
    "domain": {},
    "extension": {},
    "focus": {},
    "grade": {},
    "skills": {},
    "subdomain": {},
    "topic": {},
    "verb": {
	 "completed": {
                 "description": { "en-US": ""  },
                 "id": "http://adlnet.gov/expapi/verbs/completed",
                 "name": { "en-US": "completed" }
               },
	  "satisfied": {
                  "description": { "en-US": "" },
                  "id": "https://w3id.org/xapi/adl/verbs/satisfied",
                  "name": { "en-US": "satisfied"  }
               }	 
     }
}

Snippet 1

First, I am overriding the “completed” verb used by GBLxAPI. The verb identifier that the library uses as a default is not the one I want to use, so I’m changing it to one I feel is more appropriate for my game.

Second, I added the “satisfied” verb. When my game player finishes both levels, I send a statement with the “satisfied” verb.

Step 4 – Initializing GBLxAPI

Open the file “Assets/GBLXAPI/Scripts/GBLConfig.cs”. Near the top you will find the following properties. (Snippet 2)

     public const string LrsURL = “YOUR LRS ENDPOINT”;
     public const string companyURI = “YOUR HOME PAGE FOR ACTORS”;
     public const string gameURI = “YOUR GAME IRI”;
     public const string gameName = “YOUR GAME NAME”;

Snippet 2

Change the values as follows:

  • LrsURL
    This is the endpoint for the LRS that will receive your statements.
  • gameURI
    This is the unique identifier for your game. It should be an IRI (International Resource Identifier).
  • gameName
    The name of your game.
  • companyURI
    This value is used as the “homepage” property for xAPI Actors.

Now head on over to Assets/Scripts/GBL_Interface.cs. Here you need to set the following properties:

  • lrsUser
    The user id or key required to write statements to your LRS.
  • lrsPassword
    The password required to write statements to your LRS.

With these properties set, you are ready to initialize GBLxAPI. Somewhere in your c# code, before you try to send statements, you need the following code: (Snippet 3)

GBLXAPI.Init(new GBLConfig(GBL_Interface.lrsUser, GBL_Interface.lrsPassword));

Snippet 3

Next, let’s look at a couple of properties that I will use to simplify the code in the next step. (Snippet 4)

private static Agent playerAsAgent
    {
        get
        {
            return GBLXAPI.Agent
                .WithUUID(NameTransfer.playerName)
                .WithHomePage(GBLXAPI.Configuration.companyURI)
                .Build();
        }
    }

    private static Activity MainGameActivity
    {
        get
        {
            return GBLXAPI.Activity
                .WithID(GBLXAPI.Configuration.gameURI)
                .WithDefinition(GBLXAPI.ActivityDefinition
                    .WithType("serious-game")
                    .WithName(GBLXAPI.Configuration.gameName)
                    .Build())
                .Build();
        }
    }

Snippet 4

The playerAsAgent property returns an Agent using one of the “builders” in GBLxAPI. I use the builder methods to use the player’s name. I also set the player’s home page. Remember that we set the “companyURI” property earlier in GBLConfig.cs. GBLxAPI also supports using the “mbox” method of identifying an xAPI actor. Note: In the code above, “NameTransfer” is a static object that I use to store information about the player.

Since all of my statements will use the game itself as an activity, I create a MainGameActivity property.

Next, I want to use timers for each level so that I can include a duration on my xAPI statements. Prior to using GBLxAPI, I had created my own timers for this purpose. But with GBLxAPI, I can use the built-in time slots. So in the Start() method of a level, I have the following: (Snippet 5)

// Start a timer for this level.
     GBLXAPI.Timers.ResetSlot((int)GBL_Interface.durationSlots.Level);

Snippet 6

You retrieve the time elapsed as follows:

timeElapsed = GBLXAPI.Timers.GetSlot((int)GBL_Interface.durationSlots.Level);

Step 5 – Send a statement

When a player completes a level, I want to send a “completed” statement with the duration spent on the level. Here is my function to do this using GBLxAPI: (Snippet 7)

protected void SendxAPILevelComplete(string sceneName, int level, TimeSpan duration)
    {	
        // I want to control the name of the "object", so I create an ActivityDefinition
        var activityDef = new ActivityDefinition
        {
            name = new LanguageMap()
        };
        activityDef.name.Add("en-US", sceneName);

        GBLXAPI.Statement
            .WithActor(playerAsAgent)
            .WithVerb("completed")
            .WithTargetActivity(GBLXAPI.Activity
                .WithID(GBLXAPI.Configuration.gameURI + "/level/" + level)
                .WithType("level")
                .WithDefinition(activityDef)
                .Build())
            .WithResult(GBLXAPI.Result
                .WithDuration((float)duration.TotalSeconds)
                .Complete()
                .Build())
            .WithContext(GBLXAPI.Context
                .WithParents( new List
                {
                    MainGameActivity        // The game itself is parent of a level
                })
                .Build())
            .Enqueue(GBLxAPICallBackHandler);
    }

Snippet 7

First, I create an activity definition for my object. This is not required, but I want to control the name of my object.

Next, I call the GBLxAPI statement builder. I add the actor using my playerAsAgent property discussed earlier, then the verb. The target object is the level completed by the player. I use the result builder to add the duration spent on the level and indicate that the level is complete. I then make the game itself a context activity parent for the statement.

Finally, I call the Enqueue method of GBLxAPI to send the statement. I am using a call back handler as the parameter to Enqueue because I keep a count of statements sent and display that in my game. (Snippet 7)

public void GBLxAPICallBackHandler(bool result, string resultText)
    {
        if (result)
        {
            // Statement was successful
            StatementsSent++;
            countText.text = "Statements Sent: " + StatementsSent;
            return;
        }

        Debug.Log("Sending statement failed: " + resultText);
    }

Snippet 8

Pros & cons of GBLxAPI

Let’s start with the “pros” of using GBLxAPI.

  1. As you can see, the actual code I had to write to send a statement is pretty short. I literally had it working in about an hour.
  2. It comes with source code, so you can add additional methods that you may require. For example, I would add the ability to create an Actor using the player’s email address. Or, contribute to the community by proposing code on the GitHub site.
  3. The “Enqueue” method actually caches statements if the player is not connected to the internet, then sends them when the player is back online. That is pretty cool!
  4. The library was just recently updated, so it is good to know that it is being maintained.

Now let’s look at the “cons” of using GBLxAPI.

  1. The method of using Microsoft Excel to modify the vocabulary does not seem to work. The workaround is to just edit the JSON directly.
  2. GBLxAPI only addresses the statement API portion of xAPI. There are no methods to use the State, Agent Profile or Activity Profile APIs.
  3. I am a bit concerned that it is still called a “beta” library. As far as I can tell, it was released in 2018. That’s quite a long beta cycle. On the other hand, you get the source code so you can easily modify it if you run into any issues.

Alternatives to GBLxAPI

Since I now know a bit about Unity, I know that it uses c# as its programming language. This means that the open-source TinCan.Net library can be used. In fact, as I explored GBLxAPI I found that it is using TinCan.Net. If you need to write to the State API, you could use TinCan.Net. On the other hand, if you’re looking to send xAPI statements from Unity and want something fast and simple, give GBLxAPI a try.