📚

[Unity6]Photon Fusionのサーバーモードでエアホッケー的な物を作ってみたい(導入〜オブジェクト生成編1/3)

2025/03/03に公開

背景

前々からPUN2で簡単にオンラインゲームを作成できる所が気になっていましたが、PUN2の後継であるPhoton Fusionがいつの間にかでていたので触ってみました。
Photon Fusionについての記事

環境

  • Unity6000.0.23f1
  • Photon Fusion2.0.4

事前準備

  • サーバーのプロジェクトとクライアントのプロジェクトを用意します。
  • Photon Fusionのアカウントを作成してアプリケーションを作成してApp IDをコピーしておきます。
  • UnityにPhoton Fusionをアセットストアからインポートします。
  • インポート時に出てくるウィンドウにコピーしたApp IDをFusion App IDに貼り付けます。
    (ツール > Fusion > Fuion Hub)

サーバー側

接続を待ち受けるのみのサーバーのコードです。
接続を待ち受けるのみのですが、サーバーはこれで大丈夫です。
StartGameArgsの設定内のGameModeGameMode.Server以外にするとホストモードや共有モードにできます。

// using省略
public class ServerLauncher : MonoBehaviour, INetworkRunnerCallbacks
{
    // ネットワークランナー
    // ネットワークの管理をする
    private NetworkRunner runner;

    async void Start()
    {
        // ネットワークランナーを追加して取得
        runner = gameObject.AddComponent<NetworkRunner>();

        // ネットワークで同期して管理するシーンを設定
        NetworkSceneInfo sceneInfo = default(NetworkSceneInfo);

        // サーバーモードで起動
        var startGameArgs = new StartGameArgs()
        {
            GameMode = GameMode.Server, // サーバーモードで起動
            SessionName = "DedicatedServer", // セッション名(任意)
            Scene = sceneInfo, // 作成したNetworkSceneInfoを設定
            // 他にもパラメータを設定可能
        };

        // サーバーモードでゲームを開始
        var result = await runner.StartGame(startGameArgs);

        // 起動に失敗したら
        if (!result.Ok)
        {
            Debug.LogError($"サーバー起動に失敗: {result.ShutdownReason}");
        }
        // 起動に成功したら
        else
        {
            Debug.Log("サーバーが正常に起動しました。");
        }
    }
    // INetworkRunnerCallbacksの関数群を実装(多いので省略)
}

クライアント側

接続を待ち受けているサーバーに接続だけできるクライアントのコードです。
クライアント側もサーバーと同じようなコードで大丈夫です。
StartGameArgsの設定内のGameModeGameMode.Clientにする事とSessionNameをサーバーで設定したのと同じ名前にしてください。

// using省略
public class Runner : MonoBehaviour
{
    // ネットワークランナー
    // ネットワークの管理をする
    private NetworkRunner runner;

    async void Start()
    {
        // ネットワークランナーを追加して取得
        runner = gameObject.AddComponent<NetworkRunner>();

        // クライアントとして接続する場合
        var startGameArgs = new StartGameArgs()
        {
            GameMode = GameMode.Client, // クライアントモード
            SessionName = "DedicatedServer" // サーバーのセッション名と一致させる
            // 他にもパラメータを設定可能
        };

        // クライアントを開始
        await runner.StartGame(startGameArgs);
        Debug.Log("Client Start");
    }
}

同期Prefab作成

PrefabにNetwork Objectコンポネントが付いてIsSpawnableがtrueなら大丈夫です。

下記のScriptをサーバー側、クライアント側に作成し、Prefabにアタッチします。
サーバー側にはCanvasが無いのでnullチェックをしています。

// using省略
public class Player : NetworkBehaviour
{
    private RectTransform rectTransform;

    void Start()
    {
        rectTransform = GetComponent<RectTransform>(); // RectTransformを取得

        // Canvasを見つける
        GameObject canvasObject = GameObject.Find("Canvas");
        if (canvasObject != null)
        {
            // Canvasが見つかった場合、親を設定
            rectTransform.SetParent(canvasObject.transform);
        }
        else
        {
            Debug.LogError("Canvasが見つかりませんでした。シーンにCanvasオブジェクトが存在するか確認してください。");
        }

        rectTransform.localScale = new Vector3(1.0f, 1.0f, 1.0f);
        rectTransform.anchoredPosition = new Vector2(25.0f, 0.0f);
    }
}

Prefabオブジェクトを生成

「サーバーに接続するとPrefabを生成する」をする場合

// using省略
public class ServerLauncher : MonoBehaviour, INetworkRunnerCallbacks
{
    // プレイヤーのプレハブを用意
    public NetworkObject playerPrefab;
    // Startの下
    // INetworkRunnerCallbacksのプレイヤーが接続した時
    public void OnPlayerJoined(NetworkRunner networkRunner, PlayerRef playerRef)
    {
        Debug.Log("OnPlayerJoined");
        // 現在のプレイヤー数を取得
        int playerCount = networkRunner.ActivePlayers.Count();

        Debug.Log("playerCount" + playerCount.ToString());

        // ネットワークランナーのSpawnで同期オブジェクトができる
        // サーバーでSpawnさせるが引数にplayerRefを指定してクライアントに所有権を移す
        // NetworkBehaviour.HasInputAuthorityで所有者なのかどうかを判定可能
        networkRunner.Spawn(playerPrefab, new Vector3(0, 0, 0), Quaternion.identity, playerRef);
    }
    // 他のコールバック関数郡(多いので省略)
}

動作確認

  • Network Objectが付いたPrefab(サーバーにもクライアントにも用意)
  • ServerLauncherをMainCamera等のオブジェクトにアタッチをしたサーバー
  • RunnerをMainCamera等のオブジェクトにアタッチしたクライアント
    上記準備をし、サーバーの起動を確認してからクライアントを起動するとサーバーに接続した時にクライアント側でNetwork Objectが付いたPrefabが生成されます。
    クライアント側
    ヒエラルキー

    壁を用意

    表示したいPrefab
    (確認ができれば何でも3DでもSpriteでも良いです)

    サーバー側

実行結果

成功例

クライアント


サーバー

失敗例

クライアント

サーバー

ここまででわかった事

・今回サーバー側とクライアント側でプロジェクトを分けましたが、サーバー側でもPrefabを使ったりするので同じプロジェクトにしてシーンで分けてからビルドでサーバーシーンのみビルドがやりやすそうです。
・サーバーもクライアントも実装が簡単でした、共有モード/ホストモードの場合クライアントだけで済むので更に簡単そうです。
・所有権を持っているかのフラグがあるので所有者かどうかで処理分けができ、色々な対応ができると思います。

次回は移動/2人目のプレイヤー、できたら当たり判定までを実装したいです。

ファースト・スクラッチTech Blog

Discussion