In this article, we’ll learn to enable player matchmaking for the Unity game project so that the game clients can connect to servers automatically assigned by PlayFab.
Here, we’ll use PlayFab’s matchmaking API and implement the match ticket flow required for the matchmaking process.
Let’s continue with our Unity game project from the previous article and turn it into server code that our game can connect to and play multiplayer matches.
Requirements
Ensure you have a PlayFab account and the following software to follow this tutorial:
Mission Briefing
PlayFab provides us with a lot of very complex multiplayer features that would otherwise be tough and expensive to build and scale. One of their best and nicest features is their Matchmaking API.
Matchmaking allows players to look for and find other players to join a game session based on any custom factors you define such as region, skill level, or game type. Combined with PlayFab’s automatic multiplayer server management and allocation, we can build a scalable multiplayer server backend and launch it starting on the free tier as we grow the game and player base.
We’re going to replace the manual game server request function with PlayFab’s matchmaking to enable players to find each other and join the server.
Creating a Matchmaking Queue
To start with Matchmaking, we first need to create a queue on the Dashboard.
We open the Dashboard to Build > Multiplayer and then select the Matchmaking tab.
Next, we click New Queue and fill in the options for the queue as follows:
- Queue Name: TestQueue
- Match Size Min: 2
- Match Size Max: 4
- Enable server allocation: Yes
- Build for multiplayer server: Select your uploaded build
This example uses a simple regional selection rule, but we can add any own custom set of rules from the selection. In this case, we add a rule with these options:
- Rule name: Region
- Weight: 1
- Type: Region selection rule
- Attribute path: latencies
- Match tickets with a maximum latency of: 200
Now we click Create queue to save the configuration.
Matchmaking on the Client
We’re ready to integrate the Matchmaking API into the game client.
The matchmaking process happens in three stages:
- Creating a ticket: This starts the matchmaking request on the server and gets a ticket.
- Wait for a match: This checks the ticket status approximately every six seconds to see if it’s found a match, timed out, or been canceled.
- Join a match: This retrieves the match server’s details to connect if it finds a match.
This process, for obvious reasons, must be done before the gameplay starts. So, we’re going to add this code as part of the login process for this sample.
We open the PlayFabCode.cs script file in the Assets/Scripts folder where we implemented PlayFab login earlier in the series and at the top, insert another using statement:
using PlayFab.MultiplayerModels;
Next, we add the following code to the class to handle the matchmaking process. A latency value has been hardcoded into this request for the sample, but we could also compute the latency value by pinging the PlayFab QoS servers. We must also ensure that the Queue Name matches the name we used when creating the Matchmaking queue.
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";
requestData.GiveUpAfterSeconds = 120;
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";
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";
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 );
}
private void OnGetMatchError( PlayFabError error )
{
Debug.Log( error.ErrorMessage );
}
Next, we implement a timer within the Update
method to check the status of the matchmaking ticket:
void Update()
{
if( ticketTimer > 0 && matchmakingTicket != "" )
{
ticketTimer -= Time.deltaTime;
if( ticketTimer <= 0.0f )
{
CheckMatchmakingTicket( matchmakingTicket );
}
}
}
Inside both OnRegisterSuccess
and OnLoginSuccess
, we replace the line of code that loads the MainScene
with a call to RequestMatchmaking
to begin the process.
For example, OnLoginSuccess
now looks like this:
private void OnLoginSuccess(LoginResult result)
{
ErrorMessage.text = "";
RequestMatchmaking();
}
Then we need to update the Client script to get the matching server information. We add these two variables to the class that the PlayFab login script sets to pass on the information:
static public string matchAddress = "";
static public ushort matchPort = 0;
And finally, we change the Start
method to connect to the game session with this server information as follows:
void Start()
{
Debug.Log( "Starting Client" );
if( RunLocal )
{
connectToServer( "127.0.0.1", 7777 );
}
else
{
connectToServer( matchAddress, matchPort );
}
}
We must keep in mind that in a production-ready multiplayer game, we should implement this process with much more robustness and better error handling.
For reference, you can find the full PlayFabCode.cs script here.
Now we can build the game client executable and run multiple instances to try matchmaking with our PlayFab server.
Next Steps
In this article, we saw how easy and effective it is to integrate PlayFab’s multiplayer features.
Continue to the final part of this series, where we finish by exploring PlayFab’s leaderboard support using player statistics. Don’t miss it!
To learn more about Azure PlayFab Analytics, and get overviews of features, quickstart guides, and tutorials, check out Azure PlayFab Analytics documentation.