Dev Story ‘Flip Football’: Friends Matchmaking with Facebook
This guest post was written by Raúl Pérez Bernabé, Lead Unity developer at From The Bench.
From The Bench is one of the biggest sports games development companies in the world, with the biggest official franchise of football clubs including Real Madrid, Chelsea, AC Milan, Borussia Dortmund and with an official license of the NBA.
So if you feel like playing with your favourite soccer stars: From the Bench’s games are available on iOS & Android with ‘Flip Football’ being their first game with synchronous turn-based multiplayer. Flip Football is a strategy card game where you can build your card deck with the world’s most famous players such as Ronaldo or Ibrahimovic and compete against real opponents from all over the world.
In this post, Raúl shares some bits of how they set up their Photon multiplayer environment including friends matches via Facebook friends.
About the game
In Flip Football we have a 3×4 board where two players place their cards turn-by-turn. Each card represents licensed real life football stars that each have four power values. When you place a card with greater power than its adjacent card, it gets flipped and you score a point. The game ends once the board is filled with cards and the player with the highest score at the end of the game wins.
Real time PvP. Are you sure?
That is what we asked ourselves when we first came up with the game concept. We’ve been developing manager games for several years now, but Flip Football is our first game with synchronous real-time matches. In our games we have in-house managing of the full stack, but since we are not very experienced with this kind of technology we decided that an external cloud should be the best way to get started.
It’s a turn based game, it can’t be that hard, right?
A quick look at Photon’s turn based tutorial [https://doc.photonengine.com/en/turnbased/current/getting-started/turnbased-intro] will give you almost everything you need. Let’s get to how we are doing things.
Flip Football is a 1vs1 game, so we set up a room for each match.
public void CreateTurnbasedRoom(){
string newRoomName = this.NickName + "-" +Random.Range(0,1000).ToString("D4");
ExitGames.Client.Photon.LoadBalancing.RoomOptions newRoomOptions = new ExitGames.Client.Photon.LoadBalancing.RoomOptions();
newRoomOptions.IsOpen = true;
newRoomOptions.IsVisible = true;
newRoomOptions.MaxPlayers = NetworkManager.MaxPlayers;
newRoomOptions.CleanupCacheOnLeave = true;
newRoomOptions.EmptyRoomTtl = 1;
newRoomOptions.PlayerTtl = 15000;
// C0 holds the user league, this is for matchmaking
newRoomOptions.CustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "C0", User.league } };
newRoomOptions.CustomRoomPropertiesForLobby = new string[] { "C0" };
// this makes "C0" available in the lobby
// let's create this room in SqlLobby "myLobby" explicitly
ExitGames.Client.Photon.LoadBalancing.TypedLobby sqlLobby = new ExitGames.Client.Photon.LoadBalancing.TypedLobby("myLobby",
ExitGames.Client.Photon.LoadBalancing.LobbyType.SqlLobby);
OpCreateRoom(newRoomName, newRoomOptions, sqlLobby);
}
Once the room is set up we just need to wait for another player to join it. We have a “Looking for an opponent” screen that just waits for someone to join the room we have created.
Matchmaking
Our game is based on playing matches vs other players and we wanted it to be as simple for the user as pressing the “Play” button. When pressing the button the system will look for an opponent that is similar to him.
Our matchmaking will try and pair two players on the same division, and if it can’t find a good match, it will try again with more permissive constraints.
For example, if a user is on division 5, we want him to play against another user on division 5, if this fails to happen, we want to match him with players on divisions 6 to 4.
This is how we are doing it:
public void JoinRandomRoom(bool secondTry = false){
string sqlLobbyFilter = "C0 = " + User.league;
if(secondTry){
sqlLobbyFilter = "C0 = " + User.league + " OR (" + "C0 <= " + (User.league + Config.matchMakingDivision).ToString() +
" AND " + "C0 >= " + (User.league - Config.matchMakingDivision).ToString() + " )";
}
ExitGames.Client.Photon.LoadBalancing.TypedLobby sqlLobby = new ExitGames.Client.Photon.LoadBalancing.TypedLobby("myLobby",
ExitGames.Client.Photon.LoadBalancing.LobbyType.SqlLobby);
this.OpJoinRandomRoom(null, 2, ExitGames.Client.Photon.LoadBalancing.MatchmakingMode.FillRoom, sqlLobby, sqlLobbyFilter);
}
Now that rooms are set up and players have joined them we can actually start playing the game.
Event Handling
When a card is played we just need to tell the other client which card and where it was played. We do this with OpRaiseEvent() and the other player listens with OnEvent(). Note that we don’t have any random elements in Flip Football so both clients will end up with the same state after the event is sent and received.
public void SendCardPlayedEv(Card card, Cell cell){
Hashtable content = new Hashtable();
content[(byte)1] = card.getCardData().Id;
content[(byte)2] = cell.i;
content[(byte)3] = cell.j;
this.loadBalancingPeer.OpRaiseEvent(EvCardPlayed, content, true, null);
}
You may have noticed we didn’t do anything fancy, we simply didn’t feel the need to. I would like to mention that we did implement a Facade pattern around Photon so that it is a lot simpler to do Photon related operations and change its behaviour without other classes noticing.
Friend vs Friend matches.
This kind of logic is not built into Photon, but you can come up with several solutions easily. We do this with Facebook and native iOS/Android push notifications. Let’s explain how.
We use the official Facebook Plugin from the asset store to get our friends and their info:
public static void GetFriends (){
if(FB.IsLoggedIn){
string queryString = "/me/friends?fields=id,name,picture.width(128).height(128)&limit=100";
FB.API(queryString, HttpMethod.GET, GetFriendsCallback);
}
}
Now we need to find out if our friends are online, we use Photon for that:
public bool GetFriendsFacebook(string[] friendArray){
return this.OpFindFriends(friendArray);
}
When we receive the OperationCode.FindFriends event we populate our FriendList and call GetFriendsInfo to update each friend’s in-game status.
public void GetFriendsInfo(){
if(this.FriendList != null){
List friendInfoList = this.FriendList;
int status = 0;
foreach(ExitGames.Client.Photon.LoadBalancing.FriendInfo friend in friendInfoList){
if(friend.IsOnline){
status = 1;
if(friend.IsInRoom && friend.Room != "" && friend.Room != null){
status = 2;
}else{
if(friend.IsInRoom){
status = 2;
}
}
}else{
status = 0;
}
FacebookFacade.SetFriendStatus(friend.Name,status);
}
}
}
Now we have all our friends and their info, clients can now challenge their friends.
When a client wants to challenge a friend to a match, we create a room named [my_facebook_id]-[friend_facebook_id] and send a push notification to my friend. When my friend gets the alert and accepts, he knows that the room should be created (and also knows which friend sent him the request, so he knows how the room should be named) and tries to join it. When both players are in the room we load the game scene and the game starts.
That’s it.
All of this might seem obvious to experienced developers but if you are starting out full of doubts I hope it can help you, it certainly would have helped me.
If you have any questions or feedback feel free to contact me on twitter: @ruloii or by e-mail raul.perez@fromthebenchgames.com