快速入门:Android

开始使用适用于 Android 的 PlayFab 服务 SDK。 按照以下步骤将库包含在项目中,并尝试基本 PlayFab 功能的示例代码。

本快速入门可帮助你使用 Android SDK 进行第一次 PlayFab API 调用。 在继续操作之前,请确保已完成快速入门:Game Manager中的步骤,这可确保你拥有 PlayFab 帐户并熟悉 PlayFab Game Manager。

要求

项目设置

PlayFab SDK 发布页将 PlayFab Android SDK 下载到项目。

将 PlayFab C SDK 集成到你自己的项目中

这些后续步骤是在假设你已使用 Android Studio 创建新项目的情况下编写的。

向游戏添加二进制文件

需要将二进制文件的两个部分集成到项目中:共享对象文件 (.so) 和 android 存档文件 (.aar)。 可自行生成二进制文件,也可以从“发布”页下载它们。

添加 .so 文件

这些文件使用 CMake 集成到项目中。

  1. 解压缩 PlayFab SDK for Android 版本,并将其内容放在所需的目录中。

  2. 使用 target_include_directories 或其他等效函数添加 PlayFab SDK 版本中“包括”项下的标头:

TARGET_INCLUDE_DIRECTORIES(
    ${PROJECT_NAME}
    "Include"
)
  1. 使用 target_link_libraries 或其他等效函数将 .so 文件的位置链接到项目。

    例如:

set(PLAYFAB_SERVICES_PATH "[LOCATION OF YOUR FILE]/libPlayFabServices.Android.so")

set(PLAYFAB_CORE_PATH "[LOCATION OF YOUR FILE]/libPlayFabCore.Android.so")

set(LIBHTTPCLIENT_PATH "[LOCATION OF YOUR FILE]/libHttpClient.Android.so")

TARGET_LINK_LIBRARIES(
    [YOUR PROJECT NAME]
    ${PLAYFAB_SERVICES_PATH}
    ${PLAYFAB_CORE_PATH}
    ${LIBHTTPCLIENT_PATH}
)

添加 .aar 文件

这些文件使用 Gradle 集成到项目中。

  1. 在应用级别 Android 项目目录中创建 libs 文件夹。 下面是你的项目目录现在的显示效果:

项目目录

  1. 将 .aar 文件复制到 libs 文件夹中。

  2. 在应用级别 build.gradle 文件中(该文件与 libs 文件夹位于同一目录),将这些行添加到“依赖项”部分。 第二行需要作为 libHttpClient 的依赖项。

implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation 'com.squareup.okhttp3:okhttp:4.9.1'

Init 和 Logging in

现在,你的项目已完全设置为使用适用于 Android 的 PlayFab 服务 SDK,请按照后续步骤操作,使一些示例调用能够正常工作。

初始设置

首先需要将应用程序设置为具有 Android 活动的实例。 还需要设置一个小型 C/C++ 应用程序,以将 NDK 与 JNI(Java 本地接口)配合使用。 下面是小型示例可供参考:https://github.com/android/ndk-samples/tree/android-mk/hello-jni

该示例包含一个返回 jstring 的本机方法:

jstring Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz)

本机方法可用于获取 Java VM 和应用程序上下文,这是初始化 PFServices 所需的两项内容。 可创建一个类似的方法来实现初始化:

void Java_com_example_hellojni_HelloJni_InitializeApp(JNIEnv* env, jobject appContext)

然后,可以使用 JNIEnv 变量检索 Java VM。

    JavaVM* javaVM = nullptr;
    jint res = env->GetJavaVM(&javaVM);
    if (res != JNI_OK)
    {
        // error handling
    }

接下来,可以使用 jobject 参数获取应用程序上下文。

    applicationContext = env->NewGlobalRef(appContext);

现在你已经存储了这两个变量,我们可以开始调用了。

标头

包括 PFServices.h 以访问所有包含的 PlayFab 功能:

#include <playfab/services/PFServices.h>

初始化

PlayFab 初始化需要两个函数调用: PFServicesInitializePFServiceConfigCreateHandle。 此初始化的结果是 PFServiceConfigHandle。 将此句柄提供给后续登录调用,将调用定向到 PlayFab 后端中的正确游戏。

    HCInitArgs initArgs;
    // Use the Java VM and application context from earlier
    initArgs.javaVM = javaVm;
    initArgs.applicationContext = applicationContext;

    HRESULT hr = PFServicesInitialize(nullptr, &initArgs); // Add your own error handling when FAILED(hr) == true

    PFServiceConfigHandle serviceConfigHandle{ nullptr };

    hr = PFServiceConfigCreateHandle(
            "https://ABCDEF.playfabapi.com",    // PlayFab API endpoint - obtained in the Game Manager
            "ABCDEF",                           // PlayFab Title id - obtained in the Game Manager
            &serviceConfigHandle);

登录

拥有 PFServiceConfigHandle后,可以使用它进行玩家登录呼叫。 在 SDK 中使用 PFAuthenticationLoginWith*Async 方法,例如 PFAuthenticationLoginWithCustomIDAsync。 通过此函数可使用自定义 ID 将玩家登录到 PlayFab。

进行登录调用后,可以使用 XAsyncGetStatus 检查调用的状态。 状态以 E_PENDING 开始,并在调用成功完成后更改为 S_OK。 如果由于某种原因调用失败,状态将反映该失败。 对所有 PlayFab Services 调用的错误处理都以这种方式工作。

除了 S_OK 结果,还可返回 PFEntityHandle。 使用此句柄作为登录玩家进行后续 PlayFab 调用。 它包括以该玩家身份向 PlayFab 服务进行身份验证所需的任何材料。

    PFAuthenticationLoginWithCustomIDRequest request{};
    request.createAccount = true;
    request.customId = "player1";

    XAsyncBlock async{};
    HRESULT hr = PFAuthenticationLoginWithCustomIDAsync(serviceConfigHandle, &request, &async); // Add your own error handling when FAILED(hr) == true
    hr = XAsyncGetStatus(&async, true); // This is doing a blocking wait for completion, but you can use the XAsyncBlock to set a callback instead for async style usage

    std::vector<char> loginResultBuffer;
    PFAuthenticationLoginResult const* loginResult;
    size_t bufferSize;
    hr = PFAuthenticationLoginWithCustomIDGetResultSize(&async, &bufferSize);
    loginResultBuffer.resize(bufferSize);

    PFEntityHandle entityHandle{ nullptr };
    hr = PFAuthenticationLoginWithCustomIDGetResult(&async, &entityHandle, loginResultBuffer.size(), loginResultBuffer.data(), &loginResult, nullptr);

服务调用。

登录玩家后,现在可以调用 PlayFab 后端。 下面是调用以获取当前玩家的 PlayFab 中存储的文件的示例。

获取 EntityKey

对 PlayFab 的某些调用有用的一点是了解玩家的 PFEntityKey。 拥有 PFEntityToken后,可以使用 PFEntityGetEntityKey 检索 PFEntityKey

    PFEntityKey const* pEntityKey{};
    std::vector<char> entityKeyBuffer;
    size_t size{};
    HRESULT hr = PFEntityGetEntityKeySize(entityHandle, &size); // Add your own error handling when FAILED(hr) == true

    entityKeyBuffer.resize(size);
    hr = PFEntityGetEntityKey(entityHandle, entityKeyBuffer.size(), entityKeyBuffer.data(), &pEntityKey, nullptr);

调用 GetFiles

所有 PlayFab 调用都遵循类似的模式,即准备请求对象、进行调用(使用 PFEntityHandle 从登录名)、创建对象以接收响应,然后调用 GetResult 函数来填充新创建的容器。

    XAsyncBlock async{};
    PFDataGetFilesRequest requestFiles{};
    requestFiles.entity = pEntityKey;

    HRESULT hr = PFDataGetFilesAsync(entityHandle, &requestFiles, &async); // Add your own error handling when FAILED(hr) == true
    hr = XAsyncGetStatus(&async, true); // This is doing a blocking wait for completion, but you can use the XAsyncBlock to set a callback instead for async style usage

    size_t resultSize;
    hr = PFDataGetFilesGetResultSize(&async, &resultSize);

    std::vector<char> getFilesResultBuffer(resultSize);
    PFDataGetFilesResponse* getFilesResponseResult{ nullptr };
    hr = PFDataGetFilesGetResult(&async, getFilesResultBuffer.size(), getFilesResultBuffer.data(), &getFilesResponseResult, nullptr);

清理

当游戏已准备好关闭或需要出于其他某种原因清理 PlayFab 时,请确保关闭所有打开的句柄并调用 PFServicesUninitializeAsync

    PFEntityCloseHandle(entityHandle);
    entityHandle = nullptr;

    PFServiceConfigCloseHandle(serviceConfigHandle);
    serviceConfigHandle = nullptr;

    XAsyncBlock async{};
    HRESULT hr = PFServicesUninitializeAsync(&async); // Add your own error handling when FAILED(hr) == true
    hr = XAsyncGetStatus(&async, true); // This is doing a blocking wait for completion, but you can use the XAsyncBlock to set a callback instead for async style usage

异步 API 模式

PlayFab 服务 SDK 遵循 GDK 中实现的异步编程模型。 此编程模型涉及使用由 XAsync 库提供的任务和任务队列。 此模型与其他 GDK 函数和扩展(如 Xbox 服务 API)一致。 虽然它确实带来了一些复杂性,但也对异步操作具有高度的控制。

此示例演示如何对 PFDataGetFilesAsync进行异步调用。

    auto async = std::make_unique<XAsyncBlock>();
    async->callback = [](XAsyncBlock* async)
    {
        std::unique_ptr<XAsyncBlock> asyncBlockPtr{ async }; // take ownership of XAsyncBlock

        size_t resultSize;
        HRESULT hr = PFDataGetFilesGetResultSize(async, &resultSize);
        if (SUCCEEDED(hr))
        {
            std::vector<char> getFilesResultBuffer(resultSize);
            PFDataGetFilesResponse* getFilesResponseResult{ nullptr };
            PFDataGetFilesGetResult(async, getFilesResultBuffer.size(), getFilesResultBuffer.data(), &getFilesResponseResult, nullptr);
        }
    };

    PFDataGetFilesRequest requestFiles{};
    requestFiles.entity = m_pEntityKey;
    HRESULT hr = PFDataGetFilesAsync(m_entityHandle, &requestFiles, async.get());
    if (SUCCEEDED(hr))
    {
        async.release(); // at this point, the callback will be called so release the unique ptr
    }

错误处理

已完成 XAsync 操作返回 HTTP 状态代码。 错误状态代码 HRESULT(例如调用 XAsyncGetStatus()PF*Get() API 之一时的 HTTP_E_STATUS_NOT_FOUND)中显示为失败。

若要查看服务返回的详细错误消息,请参阅有关调试的下一部分。 这些详细的错误消息在开发过程中非常有用,以便更好地了解 PlayFab 服务如何响应来自客户端的请求。

调试

在 PlayFab Services SDK 中查看结果和调试任何调用的最简单方法是启用 调试跟踪。 启用调试跟踪可让你在调试器输出窗口中查看结果,并将结果挂钩到游戏自己的日志中。

参考

API 参考文档