匹配 SDK 快速入门

本快速入门指南将指导你完成使用 PlayFab 多人游戏 SDK 将匹配添加到游戏的整个过程。

本教程说明如何将票证提交到特定队列,以便查找游戏。 队列可能映射到一个游戏模式或多个游戏模式(例如:同一队列中的夺旗模式和山丘之王模式)。

匹配服务处理队列中的票证之间查找匹配。 找到匹配后,游戏必须将玩家连接在一起来玩游戏。

注意

PlayFab 多人游戏 SDK 还为 PlayFab Lobbies 提供 API。

先决条件

需要 PlayFab 帐户 才能使用 PlayFab 匹配。 有关创建帐户的说明,请参阅 快速入门:Game Manager

在 Game Manager 中配置匹配队列

该库将用户与为 Game Manager 中配置的队列创建票证的用户匹配。 有关如何设置匹配队列的详细信息,请参阅 配置匹配队列

下载并设置 PlayFab 多人游戏 SDK

下载适用于平台的 C/C++ SDK ,并将提供程序标头和库文件集成到生成中。

注意

本快速入门重点介绍如何使用 C/C++ SDK。 有关 Unity 和 Unreal 接口,请参阅以下文章:

登录 PlayFab 实体

若要使用 PlayFab 大厅 SDK,需要使用 PlayFab 实体密钥和实体令牌对客户端进行身份验证。 通过使用 LoginWithCustomId REST API 登录来获取 PlayFab 实体密钥和令牌对。 此 API 还可通过 PlayFab REST SDK提供 C/C++ 投影。

注意

LoginWithCustomId 是开始使用 PlayFab 功能的一种快速方法,但不旨在成为你随附的登录机制。 有关登录指南,请参阅 登录基础知识和最佳做法

初始化 PlayFab 多人游戏 SDK

按照以下基本步骤初始化 PlayFab 多人游戏 SDK:

  1. 通过调用 PFMultiplayerInitialize 初始化 SDK
  2. 通过调用 PFMultiplayerSetEntityToken,设置库代表玩家使用的实体密钥和令牌。
static PFMultiplayerHandle g_pfmHandle = nullptr;
...
...
HRESULT hr = S_OK;

// Initialize the PFMultiplayer library.
hr = PFMultiplayerInitialize(titleId, &g_pfmHandle);
if (FAILED(hr))
{
    // handle initialize failure
}

// Set an entity token for a local user. The token is used to authenticate PlayFab operations on behalf of this user. 
// Tokens can expire, and this API token should be called again when this token is refreshed.
hr = PFMultiplayerSetEntityToken(g_pfmHandle, localUserEntity, entityToken);
if (FAILED(hr))
{
    // handle set entity token failure
}

创建匹配票证

使用 PFMultiplayerCreateMatchmakingTicket 创建匹配票证,在其中指定应属于匹配的所有本地用户以及要与这些用户关联的任何属性。

此函数还采用 PFMatchmakingTicketConfiguration,用于指定票证所在的队列、票证超时以及要与此票证匹配的任何远程用户。

与单个本地用户匹配

可以通过调用 PFMultiplayerCreateMatchmakingTicket来为单个本地用户启动匹配。

const char* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.

PFMatchmakingTicketConfiguration configuration{};
configuration.timeoutInSeconds = 120;
configuration.queueName = yourQueueName;

const char* attributes = "\"{\"color\":\"blue\", \"role\":\"tank\"}\"";

const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
    g_pfmHandle,
    1, // number of local users
    localUserEntity,
    &attributes,
    &configuration,
    nullptr, // optional asyncContext
    &ticket);
RETURN_IF_FAILED(hr);

与一组远程用户进行匹配

若要开始与远程用户进行组匹配,将一个客户端视为领导者会很有帮助。 让领导使用 PFMultiplayerCreateMatchmakingTicket创建票证,并通过 配置 参数指定组中的其他用户。 创建票证后,调用 GetTicketId 以获取票证 ID。 通过外部机制(例如网络网格或共享 PlayFab 大厅)将此 ID 发送给其他用户,并让每个客户端使用票证 ID 调用 PFMultiplayerJoinMatchmakingTicketFromId 以加入匹配票证。 票证状态为 PFMatchmakingTicketStatus::WaitingForPlayers ,等待指定的玩家加入,并在所有玩家加入票证后更改为 PFMatchmakingTicketStatus::WaitingForMatch

// Creating the ticket on the leader's client

const char* remoteMemberEntityId1 = ...;
const char* remoteMemberEntityId2 = ...;

std::vector<PFEntityKey> remoteMatchMemberEntityKeys;
remoteMatchMemberEntityKeys.push_back({ remoteMemberEntityId1, "title_player_account" });
remoteMatchMemberEntityKeys.push_back({ remoteMemberEntityId2, "title_player_account" });

const char* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.

PFMatchmakingTicketConfiguration configuration{};
configuration.timeoutInSeconds = 120;
configuration.queueName = yourQueueName;
configuration.membersToMatchWithCount = 2; // number of remote members to match with
configuration.membersToMatchWith = remoteMatchMemberEntityKeys.data();

const char* attributes = "\"{\"color\":\"blue\", \"role\":\"tank\"}\"";

const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
    g_pfmHandle,
    1, // number of local users
    localUserEntity,
    &attributes,
    &configuration,
    nullptr, // optional asyncContext
    &ticket);
RETURN_IF_FAILED(hr);

// Getting the ticket ID

PCSTR ticketId;
hr = PFMatchmakingTicketGetTicketId(ticket, &ticketId);
RETURN_IF_FAILED(hr);
// Joining the ticket on the other players' clients

const char* attributes = "\"{\"color\":\"blue\", \"role\":\"healer\"}\"";
const char* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.

const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerJoinMatchmakingTicketFromId(
    g_pfmHandle,
    1, // number of local users
    localUserEntity,
    &attributes,
    ticketId,
    yourQueueName,
    nullptr, // optional asyncContext
    &ticket);

与多个本地用户进行匹配

与多个本地用户进行匹配时,需要传入密钥列表,而不是将一个 PFEntityKey 传递给 PFMultiplayerCreateMatchmakingTicketPFMultiplayerJoinMatchmakingTicketFromId 函数。 同样,需要为每个用户传入属性列表。 每个列表条目位置应彼此对应。 这意味着属性列表中的第一个条目应是 PFEntityKey 列表中第一个玩家的属性。

const char* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.

PFMatchmakingTicketConfiguration configuration{};
configuration.timeoutInSeconds = 120;
configuration.queueName = queueName;

std::vector<PFEntityKey> localMatchMemberEntityKeys{ ... };
std::vector<PCSTR> localMatchMemberAttributes{ ... };

const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
    g_pfmHandle,
    static_cast<uint32_t>(localMatchMemberEntityKeys.size())
    localMatchMemberEntityKeys.data(),
    localMatchMemberAttributes.data(),
    &configuration,
    nullptr, // optional asyncContext
    &ticket);
RETURN_IF_FAILED(hr);

检查匹配票证的状态

必须通过调用 PFMultiplayerStartProcessingMatchmakingStateChanges 来接收状态更改,然后在处理完这些状态更改后调用 PFMultiplayerFinishProcessingMatchmakingStateChanges 来检查票证的更新。

每当票证状态更改时,SDK 将返回 TicketStatusChanged 状态更改,并在匹配完成时返回 TicketCompleted 状态更改。

使用匹配客户端 SDK 的示例

HRESULT hrTicketError = S_OK;

uint32_t stateChangeCount;
const PFMatchmakingStateChange * const * stateChanges;
hr = PFMultiplayerStartProcessingMatchmakingStateChanges(g_pfmHandle, &stateChangeCount, &stateChanges);
RETURN_IF_FAILED(hr);

for (uint32_t i = 0; i < stateChangeCount; ++i)
{
    const PFMatchmakingStateChange& stateChange = *stateChanges[i];

    switch (stateChange.stateChangeType)
    {
        case PFMatchmakingStateChangeType::TicketStatusChanged:
        {
            const auto& ticketStatusChanged = static_cast<const PFMatchmakingTicketStatusChangedStateChange&>(stateChange);

            PFMatchmakingTicketStatus status;
            if (SUCCEEDED(PFMatchmakingTicketGetStatus(ticketStatusChanged.ticket, &status)))
            {
                printf("Ticket status is now: %i.\n", status);
            }

            break;
        }
        case PFMatchmakingStateChangeType::TicketCompleted:
        {
            const auto& ticketCompleted = static_cast<const PFMatchmakingTicketCompletedStateChange&>(stateChange);

            printf("PFMatchmaking completed with Result 0x%08x.\n", ticketCompleted.result);

            if (FAILED(ticketCompleted.result))
            {
                // On failure, we must record the HRESULT so we can return the state change(s) and then bail
                // out of this function.
                hrTicketError = ticketCompleted.result;
            }

            break;
        }
    }
}

hr = PFMultiplayerFinishProcessingMatchmakingStateChanges(g_pfmHandle, stateChangeCount, stateChanges);
RETURN_IF_FAILED(hr);

// Now that we've returned the state change(s), bail out if we detected ticket failure.
RETURN_IF_FAILED(hrTicketError);

获得匹配

收到 PFMatchmakingStateChangeType::TicketCompleted 状态更改后,调用 PFMatchmakingTicketGetMatch 以获取匹配的详细信息。 这些详细信息将包含匹配 ID、已匹配的用户以及匹配的首选区域,以及与匹配关联的大厅的排列字符串。

PFMatchmakingMatchDetails 结构中检索到所需的任何信息后,应使用 PFMultiplayerDestketMatchmakingTicket销毁票证。

使用匹配客户端 SDK 的示例

const PFMatchmakingMatchDetails* match;
HREULT hr = PFMatchmakingTicketGetMatch(ticket, &match);
RETURN_IF_FAILED(hr);

std::string matchId = match->matchId;
std::string lobbyArrangementString = match->lobbyArrangementString;

PFMultiplayerDestroyMatchmakingTicket(g_pfmHandle, ticket);

取消匹配票证

如果由于某种原因,客户端想要在 PFMatchmakingTicketConfiguration中设置超时之前取消匹配过程,请使用票证句柄调用 PFMatchmakingTicketCancel

调用此 API 并不能保证将取消票证。 在处理取消之前,票证仍然可以完成,或者取消请求可能会由于网络或服务错误而失败。 仍应处理匹配状态更改以获取票证结果。

使用匹配客户端 SDK 的示例

HRESULT hr = PFMatchmakingTicketCancel(ticket);

(可选)将玩家连接到大厅

玩家匹配后,他们可以在大厅中一起加入。 匹配票证中的 PFMatchmakingMatchDetails 包含一个 lobbyArrangementString 字段,该字段可用于将用户加入同一大厅。

有关大厅和匹配如何一起交互的详细信息,请参阅 使用大厅和匹配

有关 PlayFab Lobbies 的详细信息,请参阅 PlayFab 大厅概述

使用匹配客户端 SDK 的示例

const PFMatchmakingMatchDetails* match;
HREULT hr = PFMatchmakingTicketGetMatch(ticket, &match);
RETURN_IF_FAILED(hr);

std::string matchId = match->matchId;
std::string lobbyArrangementString = match->lobbyArrangementString;

PFMultiplayerDestroyMatchmakingTicket(g_pfmHandle, ticket);

PFLobbyHandle lobby;
RETURN_IF_FAILED_HR(PFMultiplayerJoinArrangedLobby(
    m_pfmHandle,
    &joiningUser,
    lobbyArrangementString,
    &joinConfig,
    nullptr, // optional asyncContext
    &lobby));

总结

通过使用本快速入门,您的游戏现在已有了成功的匹配流程。 此外,您还应该考虑:

  • 您的游戏如何处理组形成。
  • 在用户等待匹配时您的游戏显示的内容。
  • 如何处理失败和重试。

另请参阅