Photon PUN2で1スクリプトでロビーからゲーム画面に遷移が簡単に作れるコードをPhotion Fusionになおしてみた(チャット機能追加2024/7/6)
こんにちは!ジェイです。Unityでオンラインゲームを作る時にはPhotonPUN2を使っていた人も多かったと思います。
しかし、PhotonFusionが出てから更新が止まり、これからはPhotonFusion2でオンラインゲームを作っていく流れに現状はなりそうです。
そこで、私がいつもPUN2で1スクリプトでロビーを作れるテンプレートのスクリプトをPhotonFusion2に対応したバーションのスクリプトを紹介します。
Contents
PhotonPUN2でロビーからゲームシーンに遷移
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine.SceneManagement;
public class CTitleManager : MonoBehaviourPunCallbacks
{
private string RoomName = "";
Rect LobbyGuiRect = new Rect(0, 0, 300, 200);
void Update()
{
LobbyGuiRect.x = Screen.width / 4;
LobbyGuiRect.y = Screen.height / 4;
LobbyGuiRect.width = Screen.width / 2;
LobbyGuiRect.height = Screen.height / 4;
}
// 接続したらロビーに入室する
public override void OnConnectedToMaster()
{
PhotonNetwork.JoinLobby();
}
//GetRoomListは一定時間ごとに更新され、その更新のタイミングで実行する処理
//ルームリストに更新があった時(ロビーに入ってるときのみ呼ばれる)
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
RoomList = roomList;
}
List<RoomInfo> RoomList = new List<RoomInfo>();
void RoomNameUIWindow(int window_id)
{
// ルーム名の入力
GUILayout.BeginHorizontal();
GUILayout.Label("RoomName : ");
RoomName = GUILayout.TextField(RoomName, GUILayout.Width(200));
GUILayout.EndHorizontal();
// ルームを作成して入室する
if (GUILayout.Button("Create Room", GUILayout.Width(150)))
{
PhotonNetwork.CreateRoom(RoomName);
}
// ルーム一覧を検索
foreach (var room in RoomList)
{
// ルームパラメータの可視化
string room_param = $"{room.Name}{room.PlayerCount}/{((room.MaxPlayers == 0) ? "-" : room.MaxPlayers.ToString())}";
// ルームを選択して入室する
if (GUILayout.Button("Enter Room : " + room_param))
{
PhotonNetwork.JoinRoom(room.Name);
}
}
}
void PlayerNameUIWindow(int window_id)
{
// プレイヤー名の入力
GUILayout.BeginHorizontal();
GUILayout.Label("PlayerName : ");
PhotonNetwork.NickName = GUILayout.TextField(
(PhotonNetwork.NickName == null) ?
"" :
PhotonNetwork.NickName, GUILayout.Width(200));
GUILayout.EndHorizontal();
// MUNサーバに接続する
if (GUILayout.Button("Connect Server", GUILayout.Width(150)))
{
PhotonNetwork.AutomaticallySyncScene = true;
PhotonNetwork.ConnectUsingSettings();
}
}
public override void OnCreatedRoom()
{
Debug.Log("OnCreatedRoom");
}
void OnGUI()
{
// サーバに接続している場合
if (PhotonNetwork.IsConnected)
{
// ロビーに入室してる場合
if (PhotonNetwork.InLobby)
{
// ルームに入室していない場合
if (!PhotonNetwork.InRoom)
{
GUILayout.Window(1, LobbyGuiRect, RoomNameUIWindow, "");
}
}
else // ロビーに入室してない場合
{
// ルームに入室している場合
if (PhotonNetwork.InRoom)
{
}
}
}
// PUNサーバに接続していない場合
else
{
GUILayout.Window(2, LobbyGuiRect, PlayerNameUIWindow, "");
}
}
}
PhotonFusion2でロビーからゲーム画面に遷移
using System.Collections.Generic;
using UnityEngine;
using Fusion;
using Fusion.Sockets;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class CTitleManager : MonoBehaviour, INetworkRunnerCallbacks
{
private string RoomName = "";
Rect LobbyGuiRect = new Rect(0, 0, 300, 200);
private NetworkRunner _networkRunner;
private Dictionary<string, SessionInfo> RoomList = new Dictionary<string, SessionInfo>();
void Start()
{
_networkRunner = gameObject.AddComponent<NetworkRunner>();
_networkRunner.ProvideInput = true;
_networkRunner.AddCallbacks(this);
}
void Update()
{
LobbyGuiRect.x = Screen.width / 4;
LobbyGuiRect.y = Screen.height / 4;
LobbyGuiRect.width = Screen.width / 2;
LobbyGuiRect.height = Screen.height / 4;
}
public void OnConnectedToServer(NetworkRunner runner)
{
Debug.Log("Connected to Server");
}
public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason)
{
Debug.Log("Connect Failed");
}
public void OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList)
{
RoomList.Clear();
foreach (var session in sessionList)
{
RoomList[session.Name] = session;
}
}
public void OnDisconnectedFromServer(NetworkRunner runner)
{
Debug.Log("Disconnected from Server");
}
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
{
Debug.Log("Player Joined");
}
public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
{
Debug.Log("Player Left");
}
public void OnInput(NetworkRunner runner, NetworkInput input)
{
}
public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input)
{
}
public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason)
{
}
public void OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token)
{
}
public void OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message)
{
}
public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary<string, object> data)
{
}
public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken)
{
}
void RoomNameUIWindow(int window_id)
{
// ルーム名の入力
GUILayout.BeginHorizontal();
GUILayout.Label("RoomName : ");
RoomName = GUILayout.TextField(RoomName, GUILayout.Width(200));
GUILayout.EndHorizontal();
// ルームを作成して入室する
if (GUILayout.Button("Create Room", GUILayout.Width(150)))
{
_networkRunner.StartGame(new StartGameArgs()
{
GameMode = GameMode.Host,
SessionName = RoomName,
Scene = SceneManager.GetActiveScene().buildIndex,
SceneManager = gameObject.AddComponent<NetworkSceneManagerDefault>()
});
}
// ルーム一覧を検索
foreach (var room in RoomList.Values)
{
// ルームパラメータの可視化
string room_param = $"{room.Name}{room.PlayerCount}/{((room.MaxPlayers == 0) ? "-" : room.MaxPlayers.ToString())}";
// ルームを選択して入室する
if (GUILayout.Button("Enter Room : " + room_param))
{
_networkRunner.StartGame(new StartGameArgs()
{
GameMode = GameMode.Client,
SessionName = room.Name,
SceneManager = gameObject.AddComponent<NetworkSceneManagerDefault>()
});
}
}
}
void PlayerNameUIWindow(int window_id)
{
// プレイヤー名の入力
GUILayout.BeginHorizontal();
GUILayout.Label("PlayerName : ");
PhotonNetwork.NickName = GUILayout.TextField(
(PhotonNetwork.NickName == null) ? "" : PhotonNetwork.NickName, GUILayout.Width(200));
GUILayout.EndHorizontal();
// サーバに接続する
if (GUILayout.Button("Connect Server", GUILayout.Width(150)))
{
_networkRunner.StartGame(new StartGameArgs()
{
GameMode = GameMode.Shared,
SessionName = "Lobby",
SceneManager = gameObject.AddComponent<NetworkSceneManagerDefault>()
});
}
}
void OnGUI()
{
// サーバに接続している場合
if (_networkRunner.IsRunning)
{
// ルームに入室していない場合
if (_networkRunner.SessionInfo == null)
{
GUILayout.Window(1, LobbyGuiRect, RoomNameUIWindow, "");
}
}
else
{
GUILayout.Window(2, LobbyGuiRect, PlayerNameUIWindow, "");
}
}
}
PhotonPUN2でロビーからゲームシーンに遷移(チャット機能付き)
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
public class ChatScript : MonoBehaviourPunCallbacks
{
/** ルーム名. */
private string roomName = "";
/** チャット発言文. */
private string chatWord = "";
/** チャット発言ログ. */
List<string> chatLog = new List<string>();
GUIStyle AllChatStyle = new GUIStyle(); //全体チャットStyle
GUIStyleState AllChatStyleState = new GUIStyleState();
Rect RoomGuiRect = new Rect(0, 0, 300, 200); //チャットUIの大きさ設定用
Rect LobbyGuiRect = new Rect(0, 0, 300, 200);
Vector2 scrollPos = new Vector2(0, 10); //スクロールバー位置
bool FocusFlag = false;
void Start()
{
GUI.FocusControl("");
//全体チャットの場合
AllChatStyleState.textColor = Color.white;
//文字がUIからあふれた場合は折り返す設定
AllChatStyle.normal = AllChatStyleState;
AllChatStyle.wordWrap = true;
PhotonNetwork.AutomaticallySyncScene = true;
}
[PunRPC]
void RecvChat(string senderName, string senderWord)
{
chatLog.Add(senderName + " : " + senderWord);
if (chatLog.Count > 10)
{
chatLog.RemoveAt(0);
}
}
void Update()
{
//ChatUIの位置を調整
RoomGuiRect.y = Screen.height - RoomGuiRect.height;
//ChatUIの大きさ調整
RoomGuiRect.width = Screen.width / 3;
RoomGuiRect.height = Screen.height / 3;
LobbyGuiRect.x = Screen.width / 4;
LobbyGuiRect.y = Screen.height / 4;
LobbyGuiRect.width = Screen.width / 2;
LobbyGuiRect.height = Screen.height / 4;
}
void ChatUIWindow(int window_id)
{
//FocusがチャットUIに乗ってるときにEnterを押すとチャット発言が実行される
if (Event.current.type == EventType.KeyDown && (Event.current.keyCode == KeyCode.KeypadEnter || Event.current.keyCode == KeyCode.Return))
{
if (!string.IsNullOrEmpty(chatWord)) //チャット入力欄がNullやEmptyでない場合
{
//チャット送信関数実行
photonView.RPC("RecvChat",
RpcTarget.All,
PhotonNetwork.NickName,
chatWord);
chatWord = "";
return;
}
else
{
GUI.FocusControl("");
FocusFlag = false;
}
}
//垂直のコントロールグループ開始
GUILayout.BeginVertical();
// ルーム内のプレイヤー一覧の表示
GUILayout.BeginHorizontal();
GUILayout.Label("PlayerList : ");
foreach (Player player in PhotonNetwork.PlayerList)
{
GUILayout.Label(player.NickName + " ");
}
GUILayout.EndHorizontal();
//スクロールビュー開始位置
scrollPos = GUILayout.BeginScrollView(scrollPos);
//チャットログ表示用フレキシブルスペース生成
GUILayout.FlexibleSpace();
// チャットログを表示する
for (int i = chatLog.Count - 1; i >= 0; --i)
{
GUILayout.Label(chatLog[i], AllChatStyle);
}
//スクロールビュー終了
GUILayout.EndScrollView();
GUILayout.BeginHorizontal();
//入力テキストフィールド生成、Focusが乗った状態をChatInputと命名
GUI.SetNextControlName("ChatInput");
chatWord = GUILayout.TextField(chatWord, GUILayout.Width(200));
// チャット発言文を送信する
if (GUILayout.Button("Send", GUILayout.ExpandWidth(false)))
{
photonView.RPC("RecvChat",
RpcTarget.All,
PhotonNetwork.NickName,
chatWord);
chatWord = "";
}
// ルームからの退室
if (GUILayout.Button("Room", GUILayout.ExpandWidth(false)))
{
PhotonNetwork.LeaveRoom();
chatLog.Clear();
}
if (!FocusFlag && Input.GetKeyDown(KeyCode.Return))
{
FocusFlag = true;
GUI.FocusControl("ChatInput");
}
GUILayout.EndHorizontal();
//垂直のコントロールグループ終了
GUILayout.EndVertical();
}
//GetRoomListは一定時間ごとに更新され、その更新のタイミングで実行する処理
//ルームリストに更新があった時(ロビーに入ってるときのみ呼ばれる)
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
RoomList = roomList;
}
List<RoomInfo> RoomList = new List<RoomInfo>();
void RoomNameUIWindow(int window_id)
{
// ルーム名の入力
GUILayout.BeginHorizontal();
GUILayout.Label("RoomName : ");
roomName = GUILayout.TextField(roomName, GUILayout.Width(200));
GUILayout.EndHorizontal();
// ルームを作成して入室する
if (GUILayout.Button("Create Room", GUILayout.Width(150)))
{
PhotonNetwork.CreateRoom(roomName);
}
// ルーム一覧を検索
foreach (var room in RoomList)
{
// ルームパラメータの可視化
System.String roomParam =
System.String.Format(
"{0}({1}/{2})",
room.Name,
room.PlayerCount,
((room.MaxPlayers == 0) ? "-" : room.MaxPlayers.ToString())
);
// ルームを選択して入室する
if (GUILayout.Button("Enter Room : " + roomParam))
{
PhotonNetwork.JoinRoom(room.Name);
}
}
}
void PlayerNameUIWindow(int window_id)
{
// プレイヤー名の入力
GUILayout.BeginHorizontal();
GUILayout.Label("PlayerName : ");
PhotonNetwork.NickName = GUILayout.TextField(
(PhotonNetwork.NickName == null) ?
"" :
PhotonNetwork.NickName, GUILayout.Width(200));
GUILayout.EndHorizontal();
// MUNサーバに接続する
if (GUILayout.Button("Connect Server", GUILayout.Width(150)))
{
PhotonNetwork.ConnectUsingSettings();
}
}
// マスターサーバーへの接続が成功した時に呼ばれるコールバック
public override void OnConnectedToMaster()
{
Debug.Log("OnConnectedToMaster");
PhotonNetwork.JoinLobby();
}
public override void OnJoinedRoom()
{
Debug.Log("OnJoinedRoom");
}
public override void OnCreatedRoom()
{
Debug.Log("OnCreatedRoom");
}
void OnGUI()
{
// サーバに接続している場合
if (PhotonNetwork.IsConnected)
{
// ロビーに入っている場合
if (PhotonNetwork.InLobby)
{
// ルームに入室していない場合
if (!PhotonNetwork.InRoom)
{
GUILayout.Window(1, LobbyGuiRect, RoomNameUIWindow, "");
}
}
else // ロビーに入ってない場合
{
// ルームに入室している場合
if (PhotonNetwork.InRoom)
{
GUILayout.Window(0, RoomGuiRect, ChatUIWindow, "");
}
}
}
else // サーバーに接続してない場合
{
GUILayout.Window(2, LobbyGuiRect, PlayerNameUIWindow, "");
}
}
}
PhotonFusion2でロビーからゲーム画面に遷移(チャット機能付き)
using System.Collections.Generic;
using UnityEngine;
using Fusion;
using Fusion.Sockets;
using UnityEngine.SceneManagement;
public class ChatScript : MonoBehaviour, INetworkRunnerCallbacks
{
/** ルーム名. */
private string roomName = "";
/** チャット発言文. */
private string chatWord = "";
/** チャット発言ログ. */
List<string> chatLog = new List<string>();
GUIStyle AllChatStyle = new GUIStyle(); //全体チャットStyle
GUIStyleState AllChatStyleState = new GUIStyleState();
Rect RoomGuiRect = new Rect(0, 0, 300, 200); //チャットUIの大きさ設定用
Rect LobbyGuiRect = new Rect(0, 0, 300, 200);
Vector2 scrollPos = new Vector2(0, 10); //スクロールバー位置
bool FocusFlag = false;
private NetworkRunner runner;
private List<SessionInfo> sessionList = new List<SessionInfo>();
void Start()
{
GUI.FocusControl("");
AllChatStyleState.textColor = Color.white;
AllChatStyle.normal = AllChatStyleState;
AllChatStyle.wordWrap = true;
// Fusion NetworkRunner の初期化
runner = gameObject.AddComponent<NetworkRunner>();
runner.ProvideInput = true;
runner.AddCallbacks(this);
}
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) { }
public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) { }
public void OnInput(NetworkRunner runner, NetworkInput input) { }
public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input) { }
public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { }
public void OnConnectedToServer(NetworkRunner runner) { }
public void OnDisconnectedFromServer(NetworkRunner runner) { }
public void OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token) { }
public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason) { }
public void OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message) { }
public void OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList)
{
this.sessionList = sessionList;
}
public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary<string, object> data) { }
public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken) { }
public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ArraySegment<byte> data)
{
string message = System.Text.Encoding.UTF8.GetString(data.Array, data.Offset, data.Count);
chatLog.Add(message);
if (chatLog.Count > 10)
{
chatLog.RemoveAt(0);
}
}
void Update()
{
RoomGuiRect.y = Screen.height - RoomGuiRect.height;
RoomGuiRect.width = Screen.width / 3;
RoomGuiRect.height = Screen.height / 3;
LobbyGuiRect.x = Screen.width / 4;
LobbyGuiRect.y = Screen.height / 4;
LobbyGuiRect.width = Screen.width / 2;
LobbyGuiRect.height = Screen.height / 4;
}
void ChatUIWindow(int window_id)
{
if (Event.current.type == EventType.KeyDown && (Event.current.keyCode == KeyCode.KeypadEnter || Event.current.keyCode == KeyCode.Return))
{
if (!string.IsNullOrEmpty(chatWord))
{
SendMessageToAll(chatWord);
chatWord = "";
return;
}
else
{
GUI.FocusControl("");
FocusFlag = false;
}
}
GUILayout.BeginVertical();
GUILayout.BeginHorizontal();
GUILayout.Label("PlayerList : ");
foreach (PlayerRef player in runner.ActivePlayers)
{
GUILayout.Label(player.ToString() + " ");
}
GUILayout.EndHorizontal();
scrollPos = GUILayout.BeginScrollView(scrollPos);
GUILayout.FlexibleSpace();
for (int i = chatLog.Count - 1; i >= 0; --i)
{
GUILayout.Label(chatLog[i], AllChatStyle);
}
GUILayout.EndScrollView();
GUILayout.BeginHorizontal();
GUI.SetNextControlName("ChatInput");
chatWord = GUILayout.TextField(chatWord, GUILayout.Width(200));
if (GUILayout.Button("Send", GUILayout.ExpandWidth(false)))
{
SendMessageToAll(chatWord);
chatWord = "";
}
if (GUILayout.Button("Room", GUILayout.ExpandWidth(false)))
{
runner.Shutdown();
chatLog.Clear();
}
if (!FocusFlag && Input.GetKeyDown(KeyCode.Return))
{
FocusFlag = true;
GUI.FocusControl("ChatInput");
}
GUILayout.EndHorizontal();
GUILayout.EndVertical();
}
void RoomNameUIWindow(int window_id)
{
GUILayout.BeginHorizontal();
GUILayout.Label("RoomName : ");
roomName = GUILayout.TextField(roomName, GUILayout.Width(200));
GUILayout.EndHorizontal();
if (GUILayout.Button("Create Room", GUILayout.Width(150)))
{
StartGame(GameMode.Host);
}
foreach (var session in sessionList)
{
string roomParam = $"{session.Name}({session.PlayerCount}/{session.MaxPlayers})";
if (GUILayout.Button("Enter Room : " + roomParam))
{
runner.JoinSession(session);
}
}
}
void PlayerNameUIWindow(int window_id)
{
GUILayout.BeginHorizontal();
GUILayout.Label("PlayerName : ");
string playerName = GUILayout.TextField("", GUILayout.Width(200));
GUILayout.EndHorizontal();
if (GUILayout.Button("Connect Server", GUILayout.Width(150)))
{
runner.StartGame(new StartGameArgs
{
GameMode = GameMode.AutoHostOrClient,
SessionName = "MyGameSession",
SceneManager = gameObject.AddComponent<NetworkSceneManagerDefault>()
});
}
}
void OnGUI()
{
if (runner != null && runner.IsRunning)
{
if (!runner.SessionInfo.IsValid)
{
GUILayout.Window(1, LobbyGuiRect, RoomNameUIWindow, "");
}
else
{
GUILayout.Window(0, RoomGuiRect, ChatUIWindow, "");
}
}
else
{
GUILayout.Window(2, LobbyGuiRect, PlayerNameUIWindow, "");
}
}
private void StartGame(GameMode mode)
{
runner.StartGame(new StartGameArgs
{
GameMode = mode,
SessionName = roomName,
SceneManager = gameObject.AddComponent<NetworkSceneManagerDefault>()
});
}
private void SendMessageToAll(string message)
{
byte[] messageBytes = System.Text.Encoding.UTF8.GetBytes(message);
foreach (var player in runner.ActivePlayers)
{
runner.SendReliableData(player, messageBytes);
}
}
}
まとめ
PhotonPUN2ではルームを作るのにCreateRoomでルームに参加するのはJoinRoomだったのに対して、PhotonFusionでは、どちらもnetworkRunner.StartGameで対応できるようになりました。
短いコードでも機能面だけでなく、使いやすさの点においても良くなってるといえますね!