Click here to Skip to main content
15,893,401 members
Articles / Game Development / Unity
Article

Unity on Azure PlayFab Part 5: Matchmaking with Players to Find Game Sessions

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
18 Feb 2022CPOL4 min read 5.2K  
In this article, we integrate PlayFab’s Matchmaking feature into the game so that players can automatically match with each other to join on the same server and play together.

This article is a sponsored article. Articles such as these are intended to provide you with information on products and services that we consider useful and of value to developers

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.

Image 1

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.

Image 2

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

Image 3

Image 4

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:

C#
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.

C#
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 );
}

Next, we implement a timer within the Update method to check the status of the matchmaking ticket:

C#
void Update()
{
    // Update matchmaking ticket check
    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:

C#
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:

C#
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:

C#
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.

Image 5

Image 6

Image 7

Image 8

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.

This article is part of the series 'Unity On Azure PlayFab View All

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Raphael Mun is a tech entrepreneur and educator who has been developing software professionally for over 20 years. He currently runs Lemmino, Inc and teaches and entertains through his Instafluff livestreams on Twitch building open source projects with his community.

Comments and Discussions

 
-- There are no messages in this forum --