-
Notifications
You must be signed in to change notification settings - Fork 0
Sending xAPI Traces
Trace submission from Xasu is done using an asynchronous queue. This prevents the game from freezing and allows Xasu managing the trace submission in batches, reducing the connection load and handling the different errors. In addition, it is possible to know when a trace is submitted and if any (handled) error happened while sending the specific trace.
There are two possibilities when sending traces:
- Using the High-Level API: Xasu High-Level API simplifies the trace creation by using static templates from the Serious Game xAPI Profile and the CMI-5 Profile.
- Using the TinCan.NET API: Xasu uses the TinCan.NET library to model the traces. Thus, custom traces created using this API can be sent using Xasu too.
Details on each case are found bellow. Note that, when a trace is added to Xasu queue, some parameters may be autocompleted.
The High Level API is a simpler way to send xAPI traces that reduces the learning curve and can potentially reduce errors.
There are 4 different APIs for the Serious Games Profile and 1 more API for CMI-5.
To use any of the APIs make sure you include the appropiate namespace in your .cs files.
using UnityTracker.HighLevel;
Here's an example of how can you send one trace using Xasu High-Level API:
GameObjectTracker.Instance.Interacted("mesita");
Any High Level API will return a TraceTask structure, including the enqueued trace (modifable) and the Task associated to its submission. Since the trace is sent asynchronously, it is possible to use the async/await C# interface to await until the trace is sent. Errors from the trace submission will be thrown as AggregateException (since there could be multiple errors from the different working modes).
Below you can see how to manipulate the trace, await for its result and retrieve errors.
try
{
var statementPromise = GameObjectTracker.Instance.Interacted("mesita");
// Doing any modifications to the Statement in traceTask.Statement property is safe.
var statement = await statementPromise.Promise;
Debug.Log("Completed statement sent with id: " + statementPromise.Statement.id);
}
catch (AggregateException aggEx)
{
Debug.Log("Failed! " + aggEx.GetType().ToString());
// You can check the inner exceptions from each working mode.
foreach (var ex in aggEx.InnerExceptions)
{
Debug.Log("Inner Exception: " + ex.GetType().ToString());
}
}
Below you can see the rest of the APIs.
The high-level API just provides a simpler interface to send traces. Inside of each high-level API implementation, traces are enqueued into XasuTracker
and a StatementPromise
object is returned. This object is awaitable but also works like the Builder
pattern, letting you modify the trace before it gets send. Some of these modifications include adding extensions, results or scores.
For instance, to send a trace with score
var statement = await CompletableTracker.Instance.Completed("tutorial")
.WithSuccess(true)
.WithScore(0.8f); // completed successfully with 0.8/1.0 score
Debug.LogFormat("Statement sent with id!", statement.id);
If you want to implement your own High-Level API and/or contribute it to this repository we encourage you to inherit from the AbstractHighLevelTracker
class.
There are four different Serious Games APIs for sending traces related to serious games.
The Game Object API is used to send traces when the player performs an interaction. The Interacted verb should be the main interaction type, but when the element is consumed, Used is more appropiate.
Some examples are listed below:
// Interacted traces are sent when the player interacts with something
GameObjectTracker.Instance.Interacted("alarm-trigger");
// Interacted traces are sent when the player uses (and consumes) something
GameObjectTracker.Instance.Used("potion");
// Types of the elements can be specified for instance:
GameObjectTracker.Instance.Interacted("john", GameObjectTracker.TrackedGameObject.Npc);
GameObjectTracker.Instance.Interacted("grenade", GameObjectTracker.TrackedGameObject.Item);
GameObjectTracker.Instance.Interacted("demon", GameObjectTracker.TrackedGameObject.Enemy);
Full list of TrackerGameObject types:
TrackedGameObject.GameObject
TrackedGameObject.Npc
TrackedGameObject.Item
TrackedGameObject.Enemy
The Accessible API is used to send a trace whenever the player accesses a new screen. Appart from the Accessed verb, it is possible to send also Skipped traces when the screen is manually skipped.
Some examples are listed below:
// Accessed traces are sent when the player accesses an screen
AccessibleTracker.Instance.Accessed("stage-1");
// Skipped traces are sent when the player skips an screen
AccessibleTracker.Instance.Skipped("tutorial");
// Types of the elements can be specified for instance:
AccessibleTracker.Instance.Accessed("main-menu", AccessibleTracker.AccessibleType.Screen);
AccessibleTracker.Instance.Skipped("tutorial", AccessibleTracker.AccessibleType.Cutscene);
AccessibleTracker.Instance.Accessed("storage-box-1", AccessibleTracker.AccessibleType.Inventory);
Full list of Accessible types:
AccessibleType.Accessible;
AccessibleType.Area;
AccessibleType.Cutscene;
AccessibleType.Inventory;
AccessibleType.Screen;
AccessibleType.Zone;
The Alternative API is used to send a trace whenever the player makes an election. Appart from the Selected verb, it is possible to send also Unlocked traces when an new option is unlocked in the game.
Some examples are listed below:
// Accessed traces are sent when the player accesses an screen
AlternativeTracker.Instance.Selected("alternative-1", "option-2");
// Skipped traces are sent when the player skips an screen
AlternativeTracker.Instance.Unlocked("alternative-1", "super-secret-option-3");
// Types of the elements can be specified for instance:
AlternativeTracker.Instance.Selected("main-menu", "start-game", AlternativeTracker.AlternativeType.Menu);
AlternativeTracker.Instance.Unlocked("stage-1", "secret-door", AlternativeTracker.AlternativeType.Path);
AlternativeTracker.Instance.Selected("dialog-1", "option-1", AlternativeTracker.AlternativeType.Dialog);
// In this tracker is also recommended to include the success extension by using the simplified sintax
AlternativeTracker.Instance.Selected("alternative-1", "option-2").WithSuccess(true);
Full list of Alternative types:
AlternativeType.Alternative;
AlternativeType.Arena;
AlternativeType.Dialog;
AlternativeType.Menu;
AlternativeType.Path;
AlternativeType.Question;
The Completable API is the most abstract of the SGs APIs. It can be used to track the different tasks the player has to do in the game.
A Completable can be Initialized, Progressed and Completed (verbs).
Some examples are listed below:
CompletableTracker.Instance.Initialized("tutorial");
CompletableTracker.Instance.Progressed("tutorial", 0.5f); // 50% progress
CompletableTracker.Instance.Completed("tutorial").WithSuccess(true).WithScore(0.8f); // completed successfully with 0.8/1.0 score
Full list of Completable types:
CompletableType.Game,
CompletableType.Session,
CompletableType.Level,
CompletableType.Quest,
CompletableType.Stage,
CompletableType.Combat,
CompletableType.StoryNode,
CompletableType.Race,
CompletableType.Completable,
CompletableType.DialogNode,
CompletableType.DialogFragment
The CMI5 High-Level API is explained later in section.
The TinCan.NET API is the most flexible API for sending traces.
More details about the TinCan.NET API can be found in their repository at https://rusticisoftware.github.io/TinCan.NET/
To use this API, first include the TinCan.NET library in your .cs
file;
using TinCan;
To use this API you must create an Statement and enqueue it in Xasu as explained below:
var actor = new Agent();
actor.mbox = "mailto:[email protected]";
var verb = new Verb();
verb.id = new Uri ("http://adlnet.gov/expapi/verbs/experienced");
verb.display = new LanguageMap();
verb.display.Add("en-US", "experienced");
var activity = new Activity();
activity.id = "http://rusticisoftware.github.io/TinCan.NET";
var statement = new Statement();
statement.actor = actor;
statement.verb = verb;
statement.target = activity;
await Xasu.Instance.Enqueue(statement);
Debug.LogFormatted("Statement {0} sent!", statement.id);
When using Xasu, it is possible to avoid fulfilling some parameters in the traces when they are not present. These parameters include:
- ID: The trace id is added using the .NET Guid library.
- Actor: The trace actor is added using the Xasu.Instance.DefaultActor value.
- Context: The trace context is added using the Xasu.Instance.DefaultContext value. This context is automatically configured when using CMI5 so traces are CMI5 allowed.
- Timestamp: When the trace has no timestamp, DateTime.Now is setted as Timestamp.