【Unity】Photon2でチームマッチメイキングをする方法【Photon2】
制作中のゲーム『Play the Fox』でマッチングの機能を作った。
今回はその実装方法を書きます。
実装した内容は以下
完全ランダムなマッチメイキング
1vs1及び2vs2及び8人乱闘のゲームモードに対応
2vs2ではランダムにチームを組んでマッチング
今回は、UnityのPhoton2を使う。...無料だから。
仕組みを解説
雑な図解。
これは2vs2だけど、他もほぼ同じ仕組み。
最初に味方を見つけるためのマッチングをする。
必要人数見方が見つかったらそれをチームとする。
そのチームのリーダーが相手を見つけるマッチングをする。
プレイヤーが必要人数揃ったらゲーム開始
といった流れ。
実際のスクリプト
using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; using ExitGames.Client.Photon; using Photon.Realtime; using Photon.Pun; using Enums; using UnityEditor; public class PTFNetworkManager : MonoBehaviourPunCallbacks { private static PTFNetworkState state = PTFNetworkState.Idle; public string leaderUserID; private string[] teamUserIDs; public static PTFNetworkState State { get { return state; } } void Update() { switch (state) { case PTFNetworkState.FindAlly: if (PhotonNetwork.InRoom) { if (PhotonNetwork.CurrentRoom.PlayerCount == PhotonNetwork.CurrentRoom.MaxPlayers && PhotonNetwork.MasterClient.UserId != null) { //人数が揃ったらリーダーとチームメンバーを設定 leaderUserID = PhotonNetwork.MasterClient.UserId; SetTeamUserIDsWithCurrentRoom(); //準備完了フラグを立てる var properties = new ExitGames.Client.Photon.Hashtable(); properties.Add( "foundAlly", true ); PhotonNetwork.LocalPlayer.SetCustomProperties(properties); //ステート切り替え state = PTFNetworkState.FoundAlly; } } break; case PTFNetworkState.FoundAlly: //全員が設定を終えたら退室 if (CheckAllPlayerFoundAlly()) { Debug.Log("Found Ally!"); PhotonNetwork.LeaveRoom(); PhotonNetwork.JoinLobby(); state = PTFNetworkState.FindOpponent; } break; case PTFNetworkState.FindOpponent: //リーダージャナイバアイ if (leaderUserID != PhotonNetwork.LocalPlayer.UserId) { PhotonNetwork.FindFriends(new string[1]{ leaderUserID }); } if (PhotonNetwork.InRoom) { //部屋に入ったら準備完了 Debug.Log("Ready! Waiting others or start!"); state = PTFNetworkState.Ready; } break; case PTFNetworkState.Ready: //人数が揃ったら開始するとか。 break; } } public override void OnPlayerPropertiesUpdate(Player target, ExitGames.Client.Photon.Hashtable changedProps) { Debug.Log("target is " + target + "\n changed props are" + changedProps); } /// <summary> /// 現在の設定でマッチメイキングを開始 /// </summary> public static void StartMatchMaking() { //ステートは味方を探す state = PTFNetworkState.FindAlly; ConnectToMaster(); } private static void ConnectToMaster(){ string gameMode = PlayerPrefs.GetString("GameMode", "Skirmish"); if (!PhotonNetwork.IsConnected) { PhotonNetwork.ConnectUsingSettings(); Debug.Log("Connecting to master..."); }else{ PhotonNetwork.JoinLobby(); } } public override void OnConnectedToMaster(){ PhotonNetwork.JoinLobby(); Debug.Log("Joining Lobby..."); } /// <summary> /// ロビーに入ったらとりあえず見方を探す /// </summary> public override void OnJoinedLobby() { Debug.Log("Connected Master Server!"); if (state == PTFNetworkState.FindAlly) { FindAlly(); }else if (state == PTFNetworkState.FindOpponent) { FindOpponent(); } } private void FindAlly() { string gameMode = PlayerPrefs.GetString("GameMode", "Skirmish"); //ここから見方を探す //味方のプレイヤーの人数 byte expectedMaxPlayers = 1; //想定プレイヤー //GameMode(gm)とFindType(ft)でソート ExitGames.Client.Photon.Hashtable expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", "ally"} }; switch (gameMode) { case "Skirmish": expectedMaxPlayers = 1; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", "ally"} }; break; case "Solo": expectedMaxPlayers = 1; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "solo" }, {"ft", "ally"} }; break; case "Duo": expectedMaxPlayers = 2; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "duo" }, {"ft", "ally"} }; break; } PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers); } /// <summary> ///相手を探す /// リーダーのみ利用。 /// </summary> private void FindOpponent() { Debug.Log("leaderUserID : " + leaderUserID); Debug.Log("myUserID : " + PhotonNetwork.LocalPlayer.UserId); if (leaderUserID != PhotonNetwork.LocalPlayer.UserId) return; Debug.Log("I am leader! I start finding opponent!!"); string gameMode = PlayerPrefs.GetString("GameMode", "Skirmish"); //ここから相手を探す //全体のプレイヤーの人数 byte expectedMaxPlayers = 1; //想定プレイヤー //GameMode(gm)とFindType(ft)でソート ExitGames.Client.Photon.Hashtable expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", "opponent"} }; switch (gameMode) { case "Skirmish": expectedMaxPlayers = 8; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", "opponent"} }; break; case "Solo": expectedMaxPlayers = 2; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "solo" }, {"ft", "opponent"} }; break; case "Duo": expectedMaxPlayers = 4; expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "duo" }, {"ft", "opponent"} }; break; } //ランダムマッチメイキング PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers, MatchmakingMode.FillRoom, TypedLobby.Default, null, teamUserIDs); //ステートは相手を探す state = PTFNetworkState.FindOpponent; } /// <summary> /// ルーム入室失敗時、ステートにあった部屋を作成する /// </summary> /// <param name="returnCode"></param> /// <param name="message"></param> public override void OnJoinRandomFailed(short returnCode, string message){ Debug.Log("Faild to Join Room!"); Debug.Log(message); string gameMode = PlayerPrefs.GetString("GameMode", "Skirmish"); //ルームオプションの設定 string findType = ""; if (state == PTFNetworkState.FindAlly) { findType = "ally"; }else if (state == PTFNetworkState.FindOpponent) { findType = "opponent"; } byte expectedMaxPlayers = 1; ExitGames.Client.Photon.Hashtable expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" } }; switch (gameMode) { case "Skirmish": if (state == PTFNetworkState.FindAlly) { expectedMaxPlayers = 1; }else if (state == PTFNetworkState.FindOpponent) { expectedMaxPlayers =8; } expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "skirmish" }, {"ft", findType} }; break; case "Solo": if (state == PTFNetworkState.FindAlly) { expectedMaxPlayers = 1; }else if (state == PTFNetworkState.FindOpponent) { expectedMaxPlayers =2; } expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "solo" }, {"ft", findType} }; break; case "Duo": if (state == PTFNetworkState.FindAlly) { expectedMaxPlayers = 2; }else if (state == PTFNetworkState.FindOpponent) { expectedMaxPlayers =4; } expectedCustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "gm", "duo" }, {"ft", findType} }; break; } RoomOptions roomOptions = new RoomOptions(); roomOptions.CustomRoomPropertiesForLobby = new string[]{"gm", "ft"}; roomOptions.CustomRoomProperties = expectedCustomRoomProperties; roomOptions.MaxPlayers = expectedMaxPlayers; roomOptions.PublishUserId = true; Debug.Log(roomOptions.CustomRoomProperties); //ルームの作成 PhotonNetwork.CreateRoom("", roomOptions, null, teamUserIDs); Debug.Log("Created Room!"); } public override void OnJoinedRoom() { Debug.Log("Joined Room!"); Debug.Log(PhotonNetwork.CurrentRoom.CustomProperties); if (state == PTFNetworkState.FindAlly) { var properties = new ExitGames.Client.Photon.Hashtable(); properties.Add( "foundAlly", false ); PhotonNetwork.SetPlayerCustomProperties(properties); } } private void SetTeamUserIDsWithCurrentRoom() { teamUserIDs = new string[PhotonNetwork.PlayerListOthers.Length]; for (int i=0; i<PhotonNetwork.PlayerListOthers.Length; i++) { teamUserIDs[i] = PhotonNetwork.PlayerListOthers[i].UserId; } } public override void OnFriendListUpdate(List<FriendInfo> friendList) { foreach (FriendInfo friendInfo in friendList) { if (friendInfo.UserId == leaderUserID && state == PTFNetworkState.FindOpponent) { PhotonNetwork.JoinRoom(friendInfo.Room); } } } private bool CheckAllPlayerFoundAlly() { Player[] playerList = PhotonNetwork.PlayerList; foreach (Player player in playerList) { if (player.CustomProperties["foundAlly"] == null) { return false; } if (!(bool)player.CustomProperties["foundAlly"]) { return false; } } return true; } }
雑なコード解説(需要あれば書き足します)
StartMatchMaking()を呼び出すとマッチングが開始する。
Photonに関してだけ使っている順番に書くと、
PhotonNetwork.ConnectUsingSettings(); PhotonNetwork.JoinLobby();
ゲームモードが同じ、見方探し中のルームを探す。
PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers);
参加可能なルームが無い場合、
PhotonNetwork.CreateRoom("", roomOptions, null, teamUserIDs);
味方が全員揃ったら、
PhotonNetwork.LeaveRoom(); PhotonNetwork.JoinLobby();
ゲームモードが同じ、相手探し中のルームを探す。
PhotonNetwork.JoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers, MatchmakingMode.FillRoom, TypedLobby.Default, null, teamUserIDs);
参加可能なルームが無い場合、
PhotonNetwork.CreateRoom("", roomOptions, null, teamUserIDs);
ルームに入室したらステートをReadyに変えてマッチング完了。
後は人が揃うのを待ったり、強制的にゲーム始めたりお好きにどうぞ。
あとがき
これは完全にランダムでしかない。
今後、フレンドとチームを組んだり出来るようにする予定。
その辺に関しては先にフレンド関係のシステムを作ってからやるのでもう少し先になりそう。
やり方としては、最初の味方探しのJoinRandomRoomに条件を加えるだけのはず。