LoginSignup
4
3

More than 1 year has passed since last update.

PhotonFusionセットアップ備忘録

Last updated at Posted at 2022-05-15

概要

  • 新しいPhotonSDK PhotonFusionを試してみる

目的

  • Photon FusionをUnityProjectに取り込んでみて動作させるまでの手順をメモとして残しておく

環境

  • Unity 2021.3.0f1
    ※PhotonFusionはUnity 2020.3以降である必要があるので注意

PhotonFusionとは

UnityProjectを作成する

image.png

UnityProjectを設定する

  • Unity 2020.3以降;
  • アセットシリアライゼーションは 必ず テキストに設定してください(プロジェクト設定(Project Settings)>エディタ(Editor)>アセットシリアライゼーション(Asset Serialization)と移動し、モードをForce textに設定) 
  • Mono.Cecil (com.unity.nuget.mono-cecilパッケージがプロジェクトにない場合は、パッケージマネージャを使って手動で追加できます。)

UnityEditorのメニューから
Window/PackageManager/左上の「+」ボタン」/Add package add git URL/下のURLを入力

com.unity.nuget.mono-cecil@1.10

PhotonFusion入手する

  • 事前にアカウント登録が必要
  • Donwnloadはこちらから→リンク

image.png

image.png

PhotonFisionに使用するアプリケーションIDを発行する

image.png

アプリケーションIDをメモしておく

  • 見つからない場合はダッシュボードに行くと見つかる→リンク

※FusionではなくRealtimeのアプリケーションIDだった場合は設定ミスになるので注意

image.png

PhotonFusionを導入

  • 全てのファイルを追加でOK

image.png

アプリケーションIDを入力

  • プロジェクト内の Assets/Photon/Fusion/Resources/PhotonAppSettingsAppIdFusionの項目にIDを入力

赤色のエリアに入力
キャプチャ.PNG

公式のドキュメントを元に作業を行う

作業フローは下

  • スクリプトBasicSpawner.csNetworkInputData.csPlayer.csを作成

動作するまでのスクリプト全容(公式ドキュメントに部分ごとの説明があるので、本ドキュメントでは割愛)

BasicSpawner.cs
using Fusion;
using Fusion.Sockets;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class BasicSpawner : MonoBehaviour, INetworkRunnerCallbacks
{
    private NetworkRunner _runner;

    [SerializeField] private NetworkPrefabRef _playerPrefab;
    private Dictionary<PlayerRef, NetworkObject> _spawnedCharacters = new Dictionary<PlayerRef, NetworkObject>();

    public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
    {
        // Create a unique position for the player
        Vector3 spawnPosition = new Vector3((player.RawEncoded % runner.Config.Simulation.DefaultPlayers) * 3, 1, 0);
        NetworkObject networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);
        // Keep track of the player avatars so we can remove it when they disconnect
        _spawnedCharacters.Add(player, networkPlayerObject);
    }

    public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
    {
        // Find and remove the players avatar
        if (_spawnedCharacters.TryGetValue(player, out NetworkObject networkObject))
        {
            runner.Despawn(networkObject);
            _spawnedCharacters.Remove(player);
        }
    }

    public void OnInput(NetworkRunner runner, NetworkInput input)
    {
        var data = new NetworkInputData();

        if (Input.GetKey(KeyCode.W))
            data.direction += Vector3.forward;

        if (Input.GetKey(KeyCode.S))
            data.direction += Vector3.back;

        if (Input.GetKey(KeyCode.A))
            data.direction += Vector3.left;

        if (Input.GetKey(KeyCode.D))
            data.direction += Vector3.right;

        input.Set(data);
    }

    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) { }
    public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary<string, object> data) { }
    public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ArraySegment<byte> data) { }
    public void OnSceneLoadDone(NetworkRunner runner) { }
    public void OnSceneLoadStart(NetworkRunner runner) { }

    private void OnGUI()
    {
        if (_runner == null)
        {
            if (GUI.Button(new Rect(0, 0, 200, 40), "Host"))
            {
                StartGame(GameMode.Host);
            }
            if (GUI.Button(new Rect(0, 40, 200, 40), "Join"))
            {
                StartGame(GameMode.Client);
            }
        }
    }

    async void StartGame(GameMode mode)
    {
        // Create the Fusion runner and let it know that we will be providing user input
        _runner = gameObject.AddComponent<NetworkRunner>();
        _runner.ProvideInput = true;

        // Start or join (depends on gamemode) a session with a specific name
        await _runner.StartGame(new StartGameArgs()
        {
            GameMode = mode,
            SessionName = "TestRoom",
            Scene = SceneManager.GetActiveScene().buildIndex,
            SceneObjectProvider = gameObject.AddComponent<NetworkSceneManagerBase>()
        });
    }

    public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken)
    {
        Debug.Log("On Host Migration");
    }
}
NetworkInputData.cs
using Fusion;
using UnityEngine;

public struct NetworkInputData : INetworkInput
{
    public Vector3 direction;
}
Player.cs
using Fusion;

public class Player : NetworkBehaviour
{
    private NetworkCharacterController _cc;

    private void Awake()
    {
        _cc = GetComponent<NetworkCharacterController>();
    }

    public override void FixedUpdateNetwork()
    {
        if (GetInput(out NetworkInputData data))
        {
            data.direction.Normalize();
            _cc.Move(5 * data.direction * Runner.DeltaTime);
        }
    }
}
  • プレイヤー用のPrefabを作成する

Prefab概要

PlayerPrefab → スクリプトをアタッチ
    - Body → モデル(メッシュ) Cubeを配置している
    - Collision → あたり判定 BoxColliderを設定

image.png

PlayerPrefab
image.png

Body
image.png

Collision
image.png

シーンにSpawnオブジェクトを配置

  • Spawnオブジェクトを生成し、BasicSpawner.csをアタッチする
  • BasicSpawnerPlayerPrefabをアタッチする

image.png

  • 地面判定用にPlaneを配置しています

完了

  • 正しく設定できていればこれでマルチプレイが行えるはずです
  1. Hostを押す
  2. 別クライアントでJoinを押す
    ※1PCでマルチプレイを試す場合は、事前にビルドしてexe化してから「UnityEditor+ビルドしたexe」で行うとやりやすいです

image.png

  • もし、正しく動作しない場合は下記のものを見てみると良いかもしれません(作業していてミスしそうなだなと思った工程を列挙しています)

「アプリケーションIDはFusionのものか?」
「アセットシリアライゼーションのテキストはForceTextに設定しているか?」
「パッケージマネージャーにMono.Cecilを導入しているか?」
「間違えてFusiionのSDKではなくRealtimeのSDKを導入していないか?」
 

  • それでも動作しない場合は公式ドキュメントを見て設定しなおしてみる

環境構築

最小構成のサンプル

ステップアップ(公式ドキュメントやサンプルを確認する)

公式ドキュメントをすすめる

公式サンプルのTankプロジェクト ガッツリつくりこんである印象 中身を覗いてみると良いかもしれません

VR版サンプル

4
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3