Stats & Achievements API

The VIVEPORT Stats and Achievements API provides an easy way for your content to offer persistent, achievements and statistics tracking for your users.

How to use Stats & Achievements API

Note

Remember that you should call Top Level API - Api.Init() and Stats & Achievements API - UserStats.IsReady() to setup the SDK library and client runtime before you call UserStats APIs. And please remember to call Top Level API - Api.Shutdown() to teardown the library runtime after you finish using VIVEPORT SDK.

Accessing and modifying the data from within the content is easy:

  1. At the beginning of the content, you must call Init() to initialize the data by inputting the AppID to register the user information on the VIVEPORT backend server. Once you receive the callback function, the initialization is done.
  2. Call IsReady() to check if server is connected successfully and function APIs are ready.
  3. Then call DownloadStats() to fetch the user’s data from the VIVEPORT backend server, you will receive a callback function when all stats and achievements are downloaded.
  4. You can use GetStat() and GetAchievement() to retrieve the data and initialize content stats and achievements. You can also get the achievement’s unlock time by calling GetAchievementUnlockTime().
  5. When a stat changes, call SetStat(). This call only keeps data in-memory, call UploadStats() to upload the changes on the server. When one or more achievements are unlocked, call SetAchievement() for each unlocked achievement and then call UploadStats() as well to upload the changes.
  6. If you want to check the latest status of stats or achievements, please call DownloadStats() again, and get the status by using above step 4.
  7. Before closing the content, call Shutdown() to teardown stuff. Once you receive the callback function, the teardown is done.

Important

Please see the Error Codes and Trouble Shooting pages to know what to do when you got problems using the VIVEPORT SDK.

Step by step: Stats & Achievements

First, you need to login to VIVEPORT

  1. Before using the developer console, you must install VIVEPORT , and login to your HTC Account.

    _images/ViveLogin.png
  2. Sign in your developers console by your account and password.

    _images/DeveloperLogin.png
  3. On the Developer console, under the My Content -> Manage page, click Add new experience button to create an experiene to obtain a VIVEPORT ID.

    _images/AddNewExperience.png
  4. After creating a new experience, you can get VIVEPORT ID from the screen below :

    _images/AppID.png
  5. Then use Add stat button to create a new stat.

    _images/Add_Stat.png
  6. Fill in all fields for the new stat: ( please refer Glossary in detail )

    _images/Stat.png
  7. Create an achievement by using Add achievement button.

    _images/Add_Achievement.png
  8. Then fill in all fields for the new achievement: ( please refer Glossary in detail )

    _images/Achievement.png

Detailed Example in Unity

using UnityEngine;
using System;
using Viveport;

public class ViveportDemo : MonoBehaviour
{
   private int nInitValue = 0, nResult = 0;
#if UNITY_ANDROID
   private int nWidth = 150, nHeight = 100;
#else
   private int nWidth = 110, nHeight = 40;
#endif
   private int nXStart = 10, nYStart = 35;
   private string stringToEdit = "ID_Stat1";
   private string StatsCount = "80";
   private string achivToEdit = "ID_Achievement1";
   private static bool bInit = true, bIsReady = false;

   static string VIVEPORT_ID = "bd67b286-aafc-449d-8896-bb7e9b351876";

   // Use this for initialization
   void Start ()
   {
       Api.Init(InitStatusHandler, VIVEPORT_ID);
   }

   void OnGUI()
   {
       GUIStyle CustButton = new GUIStyle("button");
   #if UNITY_ANDROID
       CustButton.fontSize = 23;
   #endif
       if (bInit == false)
          GUI.contentColor = Color.white;
       else
          GUI.contentColor = Color.grey;

       // Init function
       if (GUI.Button(new Rect(nXStart, nYStart, nWidth, nHeight), "Init", CustButton))
       {
          if (bInit == false)
             Api.Init(InitStatusHandler, VIVEPORT_ID);
       }

       if (bInit == true)
          GUI.contentColor = Color.white;
       else
          GUI.contentColor = Color.grey;

       // Shutdown function
       if (GUI.Button(new Rect((nXStart + 1 * (nWidth + 10)), nYStart, nWidth, nHeight), "Shutdown", CustButton))
       {
          if (bInit == true)
             Api.Shutdown(ShutdownHandler);
       }

       // IsReady function
       if (GUI.Button(new Rect((nXStart + 2 * (nWidth + 10)), nYStart, nWidth, nHeight), "IsReady", CustButton))
       {
         if (bInit == true)
             UserStats.IsReady(IsReadyHandler);
       }

       /***************************************************************************/
       /*                          Stat sample code                               */
       /***************************************************************************/

       if (bInit == true && bIsReady == true)
          GUI.contentColor = Color.white;
       else
          GUI.contentColor = Color.grey;

       stringToEdit = GUI.TextField(new Rect(10, nWidth + 10, 120, 20), stringToEdit, 50);
       StatsCount = GUI.TextField(new Rect(130, nWidth + 10, 220, 20), StatsCount, 50);

       // DownloadStats function
       if (GUI.Button(new Rect(nXStart , nYStart + nWidth + 10, nWidth, nHeight),"DownloadStat"))
       {
           if (bInit == true && bIsReady == true)
              UserStats.DownloadStats(DownloadStatsHandler);
           else
              Viveport.Core.Logger.Log("Make sure init & isReady are successful.");
       }

       // UploadStats function
       if (GUI.Button(new Rect((nXStart + 1 * (nWidth + 10)), nYStart + nWidth + 10, nWidth, nHeight), "UploadState", CustButton))
       {
           if (bInit == true && bIsReady == true)
              UserStats.UploadStats(UploadStatsHandler);
           else
              Viveport.Core.Logger.Log("Make sure init & isReady are successful.");
       }

       // GetStat function
       if (GUI.Button(new Rect(nXStart + 2*(nWidth + 10), nYStart + nWidth + 10 , nWidth, nHeight), "GetStat", CustButton))
       {
           if (bInit == true && bIsReady == true)
           {
              nResult = UserStats.GetStat(stringToEdit, nInitValue);
              Viveport.Core.Logger.Log("Get " + stringToEdit + " stat name as => " + nResult);
           }
           else
              Viveport.Core.Logger.Log("Make sure init & isReady are successful.");
       }

       // SetStat function
       if (GUI.Button(new Rect(nXStart + 3* (nWidth + 10), nYStart + nWidth + 10, nWidth, nHeight), "SetStat", CustButton))
       {
           if (bInit == true && bIsReady == true)
           {
              Viveport.Core.Logger.Log("MaxStep is => " + int.Parse(StatsCount));
              nResult = int.Parse(StatsCount);
              UserStats.SetStat(stringToEdit, nResult);
              Viveport.Core.Logger.Log("Set" + stringToEdit + " stat name as =>" + nResult);
           }
           else
           {
              Viveport.Core.Logger.Log("Make sure init & isReady are successful.");
           }
       }

       /***************************************************************************/
       /*                          Achievement sample code                        */
       /***************************************************************************/

       achivToEdit = GUI.TextField(new Rect(10, 2* nWidth + 15, 120, 20), achivToEdit, 50);

       // GetAchievement function
       if (GUI.Button(new Rect(nXStart, nYStart + 2 * nWidth + 10, nWidth, nHeight), "GetAchieve", CustButton))
       {
           if (bInit == true && bIsReady == true)
           {
              bool bAchievement = false;
              bAchievement  = UserStats.GetAchievement(achivToEdit);
              Viveport.Core.Logger.Log("Get achievement => " + achivToEdit + " , and value is => "+ bAchievement);
           }
           else
              Viveport.Core.Logger.Log("Make sure init & isReady are successful.");
       }

       // SetAchievement function
       if (GUI.Button(new Rect(nXStart + nWidth + 10, nYStart + 2 * nWidth + 10, nWidth, nHeight), "SetAchieve", CustButton))
       {
           if (bInit == true && bIsReady == true)
           {
              UserStats.SetAchievement(achivToEdit);
              Viveport.Core.Logger.Log("Set achievement => " + achivToEdit);
           }
           else
              Viveport.Core.Logger.Log("Make sure init & isReady are successful.");
       }

       // ClearAchievement function
       if (GUI.Button(new Rect(nXStart + 2 * (nWidth + 10), nYStart + 2 * nWidth + 10, nWidth, nHeight), "ClearAchi", CustButton))
       {
           if (bInit == true && bIsReady == true)
           {
              UserStats.ClearAchievement(achivToEdit);
              Viveport.Core.Logger.Log("Clear achievement => " + achivToEdit);
           }
           else
              Viveport.Core.Logger.Log("Make sure init & isReady are successful.");
       }

       // GetAchievementUnlockTime function
       if (GUI.Button(new Rect(nXStart + 3 * (nWidth + 10), nYStart + 2 * nWidth + 10, nWidth, nHeight), "Achieve&Time", CustButton))
       {
           if (bInit == true && bIsReady == true)
           {
              int nunlockTime = 0;
              nunlockTime = UserStats.GetAchievementUnlockTime(achivToEdit);
              Viveport.Core.Logger.Log("The achievement's unlock time is =>" + nunlockTime);
           }
           else
           {
              Viveport.Core.Logger.Log("Make sure init & isReady are successful.");
           }
       }
   }

   private static void InitStatusHandler(int nResult)
   {
       if (nResult == 0)
       {
          bInit = true;
          bIsReady = false;
          Viveport.Core.Logger.Log("InitStatusHandler is successful");
       }
       else
       {
          bInit = false;
          Viveport.Core.Logger.Log("InitStatusHandler error : " + nResult);
       }
   }

   private static void IsReadyHandler(int nResult)
   {
       if (nResult == 0)
       {
          bIsReady = true;
          Viveport.Core.Logger.Log("IsReadyHandler is successful");
       }
       else
       {
          bIsReady = false;
          Viveport.Core.Logger.Log("IsReadyHandler error: " + nResult);
       }
   }

   private static void ShutdownHandler(int nResult)
   {
       if (nResult == 0)
       {
          bInit = false;
          bIsReady = false;
          Viveport.Core.Logger.Log("ShutdownHandler is successful");
       }
       else
       {
          Viveport.Core.Logger.Log("ShutdownHandler error: " + nResult);
       }
   }

   private static void DownloadStatsHandler(int nResult)
   {
       if (nResult == 0)
          Viveport.Core.Logger.Log("DownloadStatsHandler is successful ");
       else
          Viveport.Core.Logger.Log("DownloadStatsHandler error: " + nResult);
   }

   private static void UploadStatsHandler(int nResult)
   {
       if (nResult == 0)
          Viveport.Core.Logger.Log("UploadStatsHandler is successful");
       else
          Viveport.Core.Logger.Log("UploadStatsHandler error: " + nResult);
   }
}

Detailed Example in Unreal

ViveportUserStatsDemo.h

#include "Components/ActorComponent.h"
#include "ViveportApi.h"
#include "ViveportUserStats.h"
#include "ViveportType.h"

#include "ViveportUserStatsDemo.generated.h"

UCLASS(ClassGroup = (Viveport), meta = (BlueprintSpawnableComponent))
class VIVEPORTSDK_API UViveportUserStatsDemo : public UActorComponent
{
    GENERATED_BODY()

public:
    // Sets default values for this component's properties
    UViveportUserStatsDemo();

    // Called when the game starts
    void BeginPlay() override;

    void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

    // Called every frame
    void TickComponent(float DeltaTime,
                       LevelTick TickType,
                       FActorComponentTickFunction* ThisTickFunction
                      ) override;

    /** The VIVEPORT_ID for auth verify */
    FString VIVEPORT_ID = "bd67b286-aafc-449d-8896-bb7e9b351876";

public:

    class MyInitCallback : public ViveportApiStatus
    {
    public:
        void OnSuccess(
            ) override;
        void OnFailure(int errorCode
            ) override;
    };

    class MyIsReadyStatus : public ViveportApiStatus
    {
    public:
        void OnSuccess(
            ) override;
        void OnFailure(int errorCode
            ) override;
    };

    class MyDownloadStatsStatus : public ViveportApiStatus
    {
    public:
        void OnSuccess(
            ) override;
        void OnFailure(int errorCode
            ) override;
    };

    class MyUploadStatsStatus : public ViveportApiStatus
    {
    public:
        void OnSuccess(
            ) override;
        void OnFailure(int errorCode
            ) override;
    };


    class MyShutdownCallback : public ViveportApiStatus
    {
    public:
        void OnSuccess(
            ) override;
        void OnFailure(int error_code
            ) override;
    };
};

ViveportUserStatsDemo.cpp

#include "ViveportSDKPrivatePCH.h"
#include "ViveportUserStatsDemo.h"

UViveportUserStatsDemo::MyInitCallback myInitCallback;
UViveportUserStatsDemo::MyShutdownCallback myShutdownCallback;
UViveportUserStatsDemo::MyIsReadyStatus myIsReadyStatus;
UViveportUserStatsDemo::MyDownloadStatsStatus myDownloadStatsStatus;
UViveportUserStatsDemo::MyUploadStatsStatus myUploadStatsStatus;

FString stat_key_1 = "ID_Stat1";
FString stat_key_2 = "ID_Stat2";
int stat_key_default_1 = 0;
float stat_key_default_2 = 0.0f;
FString achievement_name = "ID_Achievement1";

// Sets default values for this component's properties
UViveportUserStatsDemo::UViveportUserStatsDemo()
{
    // Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
    // off to improve performance if you don't need them.
    bWantsBeginPlay = true;
    PrimaryComponentTick.bCanEverTick = true;
}

// Called when the game starts
void UViveportUserStatsDemo::BeginPlay()
{
    Super::BeginPlay();
    UViveportApi::Init(&myInitCallback, VIVEPORT_ID);
}

void UViveportUserStatsDemo::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    Super::EndPlay(EndPlayReason);
    UViveportApi::Shutdown(&myShutdownCallback);
}

// Called every frame
void UViveportUserStatsDemo::TickComponent(float DeltaTime,
                                           ELevelTick TickType,
                                           FActorComponentTickFunction* ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}

void UViveportUserStatsDemo::MyInitCallback::OnSuccess()
{
    FString fstring("Init success.");
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);
    UViveportUserStats::IsReady(&myIsReadyStatus);
}

void UViveportUserStatsDemo::MyInitCallback::OnFailure(int error)
{
    FString fstring("Init failure. error code=" + error);
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);
}

void UViveportUserStatsDemo::MyIsReadyStatus::OnSuccess()
{
    FString fstring("IsReady success.");
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);
    UViveportUserStats::DownloadStats(&myDownloadStatsStatus);
}

void UViveportUserStatsDemo::MyIsReadyStatus::OnFailure(int error)
{
    const FString fstring = FString::Printf(TEXT("IsReady failure. error=%d"), error);
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);
}

void UViveportUserStatsDemo::MyDownloadStatsStatus::OnSuccess()
{
    FString fstring("MyDownloadStatsStatus success.");
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);

    int stat_1 = UViveportUserStats::GetStat(stat_key_1, stat_key_default_1);
    float stat_2 = UViveportUserStats::GetStat(stat_key_2, stat_key_default_2);

    fstring = FString::Printf(TEXT("getStats, %s=%d, %s=%f"), *stat_key_1, stat_1, *stat_key_2, stat_2);
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);

    stat_1++; stat_2++;
    UViveportUserStats::SetStat(stat_key_1, stat_1);
    UViveportUserStats::SetStat(stat_key_2, stat_2);

    fstring = FString::Printf(TEXT("setStats, %s=%d, %s=%f"), *stat_key_1, stat_1, *stat_key_2, stat_2);
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);

    bool achieved = UViveportUserStats::GetAchievement(achievement_name);
    fstring = FString::Printf(TEXT("GetAchievement, name=%s, achieved=%s"),
                            *achievement_name,
                            achieved ? *FString("true") : *FString("false"));
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);

    if (achieved)
        UViveportUserStats::ClearAchievement(achievement_name);
    else
        UViveportUserStats::SetAchievement(achievement_name);
    UViveportUserStats::UploadStats(&myUploadStatsStatus);
}

void UViveportUserStatsDemo::MyDownloadStatsStatus::OnFailure(int error)
{
    const FString fstring = FString::Printf(TEXT("DownloadStats failure. error=%d"), error);
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);
}

void UViveportUserStatsDemo::MyUploadStatsStatus::OnSuccess()
{
    FString fstring("UploadStats success.");
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);
}

void UViveportUserStatsDemo::MyUploadStatsStatus::OnFailure(int error)
{
    const FString fstring = FString::Printf(TEXT("UploadStats failure. error=%d"), error);
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);
}

void UViveportUserStatsDemo::MyShutdownCallback::OnSuccess()
{
    FString fstring("Shutdown success.");
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);
}

void UViveportUserStatsDemo::MyShutdownCallback::OnFailure(int error_code)
{
    FString fstring = FString::Printf(TEXT("Shutdown failure. error=%d"), error_code);
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::White, fstring);
}