Azure PlayFab 上的 Unity 第 5 部分:玩家匹配以寻找游戏会话





1.00/5 (3投票s)
在本文中,我们将 PlayFab 的匹配功能集成到游戏中,以便玩家可以自动匹配并加入同一服务器一起玩。
在本文中,我们将学习如何为 Unity 游戏项目启用玩家匹配,以便游戏客户端可以自动连接到 PlayFab 分配的服务器。
在这里,我们将使用 PlayFab 的匹配 API 并实现匹配过程所需的匹配票证流程。
让我们从 上一篇文章 继续我们的 Unity 游戏项目,并将其转换为我们的游戏可以连接和玩多人匹配的服务器代码。
要求
请确保您拥有一个 PlayFab 帐户 和以下软件来学习本教程
- Unity 游戏引擎
- Microsoft Visual Studio
- 来自上一篇文章的 Unity游戏项目
任务简报
PlayFab 为我们提供了许多非常复杂的多人功能,否则这些功能将难以构建和扩展。他们最好也是最 nice 的功能之一是他们的匹配 API。
匹配功能允许玩家根据您定义的任何自定义因素(例如区域、技能水平或游戏类型)查找并找到其他玩家加入游戏会话。结合 PlayFab 的自动多人服务器管理和分配,我们可以构建可扩展的多人服务器后端,并在游戏和玩家群增长时从免费层开始启动。
我们将用 PlayFab 的匹配功能替换手动游戏服务器请求功能,以使玩家能够找到彼此并加入服务器。
创建匹配队列
要开始匹配,我们首先需要在仪表板上创建一个队列。
我们打开仪表板到 **Build** > **Multiplayer**,然后选择 **Matchmaking** 选项卡。
接下来,我们单击 **New Queue** 并按如下方式填写队列选项
- 队列名称:TestQueue
- 最小匹配人数:2
- 最大匹配人数:4
- 启用服务器分配:是
- 多人游戏服务器的构建:选择您上传的构建
此示例使用简单的区域选择规则,但我们可以从选择中添加任何我们自己的自定义规则集。在这种情况下,我们添加一个具有以下选项的规则
- 规则名称:Region
- 权重:1
- 类型:Region selection rule
- Attribute path: latencies
- 最大延迟为:200 的匹配票证
现在我们单击 **Create queue** 来保存配置。
客户端上的匹配
我们已准备好将匹配 API 集成到游戏客户端中。
匹配过程分为三个阶段
- 创建票证:这会在服务器上启动匹配请求并获取一个票证。
- 等待匹配:这大约每六秒检查一次票证状态,以查看它是否找到匹配项、超时或已取消。
- 加入匹配:如果找到匹配项,这将检索匹配服务器的详细信息以进行连接。
显然,此过程必须在游戏开始之前完成。因此,在本示例中,我们将此代码添加为登录过程的一部分。
我们在系列开头 实现 PlayFab 登录 的地方打开 `Assets/Scripts` 文件夹中的 `PlayFabCode.cs` 脚本文件,并在顶部插入另一个 using 语句
using PlayFab.MultiplayerModels;
接下来,我们将以下代码添加到类中以处理匹配过程。此请求中已为示例硬编码了延迟值,但我们也可以通过 ping PlayFab QoS 服务器 来计算延迟值。我们还必须确保队列名称与我们在创建匹配队列时使用的名称匹配。
private string matchmakingTicket = "";
private float ticketTimer = 0;
private void RequestMatchmaking()
{
CreateMatchmakingTicketRequest requestData = new CreateMatchmakingTicketRequest();
requestData.Creator = new MatchmakingPlayer {
Entity = new PlayFab.MultiplayerModels.EntityKey {
Id = PlayFabSettings.staticPlayer.EntityId,
Type = PlayFabSettings.staticPlayer.EntityType,
},
Attributes = new MatchmakingPlayerAttributes {
DataObject = new {
latencies = new object[] {
new {
region = "EastUs",
latency = 100,
},
},
},
},
};
requestData.QueueName = "TestQueue"; // Matchmaking Queue Name
requestData.GiveUpAfterSeconds = 120; // 120 seconds
PlayFabMultiplayerAPI.CreateMatchmakingTicket( requestData, OnCreateMatchmakingTicketResult, OnCreateMatchmakingTicketError );
}
private void OnCreateMatchmakingTicketResult( CreateMatchmakingTicketResult response )
{
CheckMatchmakingTicket( response.TicketId );
}
private void OnCreateMatchmakingTicketError( PlayFabError error )
{
Debug.Log( error.ErrorMessage );
}
private void CheckMatchmakingTicket( string ticketId )
{
Debug.Log( "Checking ticket " + ticketId );
matchmakingTicket = ticketId;
GetMatchmakingTicketRequest requestData = new GetMatchmakingTicketRequest();
requestData.QueueName = "TestQueue"; // Matchmaking Queue Name
requestData.TicketId = ticketId;
PlayFabMultiplayerAPI.GetMatchmakingTicket( requestData, OnCheckMatchmakingTicketResult, OnCheckMatchmakingTicketError );
}
private void OnCheckMatchmakingTicketResult( GetMatchmakingTicketResult response )
{
bool queueTicketCheck = false;
switch( response.Status )
{
case "Matched":
ErrorMessage.text = "Found Match!";
Debug.Log( "Found Match " + response.MatchId );
matchmakingTicket = "";
JoinMatch( response.MatchId );
break;
case "WaitingForMatch":
ErrorMessage.text = "Waiting for match";
Debug.Log( "Waiting for match..." );
queueTicketCheck = true;
break;
case "WaitingForPlayers":
ErrorMessage.text = "Waiting for players";
Debug.Log( "Waiting for players..." );
queueTicketCheck = true;
break;
case "WaitingForServer":
ErrorMessage.text = "Waiting for server";
Debug.Log( "Waiting for server..." );
queueTicketCheck = true;
break;
case "Canceled":
ErrorMessage.text = "Canceled";
Debug.Log( "Canceled..." );
matchmakingTicket = "";
break;
default:
break;
}
if( queueTicketCheck )
{
ticketTimer = 6.0f;
}
}
private void OnCheckMatchmakingTicketError( PlayFabError error )
{
Debug.Log( error.ErrorMessage );
}
private void JoinMatch( string matchId )
{
Debug.Log( "Joining Match..." );
GetMatchRequest requestData = new GetMatchRequest();
requestData.QueueName = "TestQueue"; // Matchmaking Queue Name
requestData.MatchId = matchId;
PlayFabMultiplayerAPI.GetMatch( requestData, OnGetMatchResult, OnGetMatchError );
}
private void OnGetMatchResult( GetMatchResult response )
{
Client.matchAddress = response.ServerDetails.IPV4Address;
Client.matchPort = (ushort)response.ServerDetails.Ports[ 0 ].Num;
SceneManager.LoadScene( SceneName ); // Load Main Scene
}
private void OnGetMatchError( PlayFabError error )
{
Debug.Log( error.ErrorMessage );
}
接下来,我们在 `Update` 方法中实现一个计时器来检查匹配票证的状态
void Update()
{
// Update matchmaking ticket check
if( ticketTimer > 0 && matchmakingTicket != "" )
{
ticketTimer -= Time.deltaTime;
if( ticketTimer <= 0.0f )
{
CheckMatchmakingTicket( matchmakingTicket );
}
}
}
在 `OnRegisterSuccess` 和 `OnLoginSuccess` 中,我们将加载 `MainScene` 的代码行替换为对 `RequestMatchmaking` 的调用以开始该过程。
例如,`OnLoginSuccess` 现在看起来像这样
private void OnLoginSuccess(LoginResult result)
{
ErrorMessage.text = "";
RequestMatchmaking();
}
然后我们需要更新客户端脚本以获取匹配的服务器信息。我们将这两个变量添加到 PlayFab 登录脚本设置的类中,以传递信息
static public string matchAddress = "";
static public ushort matchPort = 0;
最后,我们按照以下方式更改 `Start` 方法以使用此服务器信息连接到游戏会话
void Start()
{
Debug.Log( "Starting Client" );
if( RunLocal )
{
connectToServer( "127.0.0.1", 7777 );
}
else
{
connectToServer( matchAddress, matchPort );
}
}
我们必须牢记,在生产级的多人游戏中,我们应该以更强的健壮性和更好的错误处理来实现此过程。
供参考,您可以在 此处 找到完整的 `PlayFabCode.cs` 脚本。
现在我们可以构建游戏客户端可执行文件并运行多个实例,以尝试与我们的 PlayFab 服务器进行匹配。
后续步骤
在本文中,我们看到了集成 PlayFab 的多人功能是多么简单有效。
继续本系列的 最后一部分,我们将通过探索使用玩家统计信息的 PlayFab 排行榜支持来结束。不要错过!
要了解有关 Azure PlayFab Analytics 的更多信息,并获得功能概述、快速入门指南和教程,请查看 Azure PlayFab Analytics 文档。