猫でもわかるUnity入門(第22回 Photon PUN2を使ったマルチプレイゲームチュートリアル 最終回)

f:id:enia:20210228230354p:plain えにあです。 前回に続きUnit + Photon Pun2を使ったオンラインマルチプレイゲーム作成のチュートリアルを進めていきます。 二つのアプリで

スクリプトのアタッチ

シーン上に作成した空のオブジェクト「PhotonController」に、前回作成したスクリプト「RandomMatchMaker」をアタッチします。 アタッチの方法はスクリプトをオブジェクトにドラッグアンドドロップすればOKです。

PhotonControllerをインスペクターで表示します。 RandomMatchMakerスクリプトコンポーネントとして追加されています。 スクリプトで定義したPhotonObjectプロパティが表示されていますね。 ここに、Unityちゃんのプレハブをアタッチします。プレハブの方をアタッチしますので、プロジェクトビューから「Assets」->「Demos」->「Resources」->「unitychan_dynamic_locomotion」を選択してアタッチしてください。 f:id:enia:20210312084122p:plain

この状態で一度実行してみましょう。 コンソールに大量のエラーが表示されると思います。 f:id:enia:20210312084754p:plain

NullReferenceException: Object reference not set to an instance of an object
UnityChan.ThirdPersonCamera.setCameraPositionNormalView () (at Assets/unity-chan!/Unity-chan! Model/Scripts/ThirdPersonCamera.cs:57)
UnityChan.ThirdPersonCamera.FixedUpdate () (at Assets/unity-chan!/Unity-chan! Model/Scripts/ThirdPersonCamera.cs:49)

このエラーをクリックして、該当のソースを表示します。 ThirdPersoncCamera.csの、setCameraPositionNormalViewメソッドで、starndardPosがnullになっていることが原因のようです。

void setCameraPositionNormalView ()
{
    if (bQuickSwitch == false) {
        // the camera to standard position and direction
        // ★starndardPosがnullになっている。
        transform.position = Vector3.Lerp (transform.position, standardPos.position, Time.fixedDeltaTime * smooth); 
        transform.forward = Vector3.Lerp (transform.forward, standardPos.forward, Time.fixedDeltaTime * smooth);
    } else {
        // the camera to standard position and direction / Quick Change
        transform.position = standardPos.position;  
        transform.forward = standardPos.forward;
        bQuickSwitch = false;
    }
}

standardPosを設定している箇所を見るとstartメソッドの中でGameObject.Findしていることが分かります。 Unityちゃんをプレハブ化したことで、CamPosが見つからなくなったことが原因のようです。

void Start ()
{
    // 各参照の初期化
    standardPos = GameObject.Find ("CamPos").transform;

    if (GameObject.Find ("FrontPos"))
        frontPos = GameObject.Find ("FrontPos").transform;

    if (GameObject.Find ("JumpPos"))
        jumpPos = GameObject.Find ("JumpPos").transform;

    //カメラをスタートする
    transform.position = standardPos.position;  
    transform.forward = standardPos.forward;    
}

このThirdPersonCameraスクリプトはMain Cameraにアタッチされていて、起動するとすぐにStartメソッドがコールされ、CamPosオブジェクトを探しに行ってしまいます。

今回はサーバに接続されたタイミングでUnityちゃんのインスタンスを作成しているので、そのタイミングでThirdPersonCameraを有効にすればこのエラーは防げそうです。

まず、Main Cameraをインスペクターで開いてThridPersonCameraをオフにします。 これで実行後すぐにこのスクリプトが呼ばれることを防げます。 f:id:enia:20210312085653p:plain

次に、Unityちゃんのインスタンスを作成したタイミングでThridPersonCameraをオンにします。
前回作成した、RandomMatchMakerスクリプトの、OnJoinedRoomコールバックを再度確認してみましょう。 実は、既に入室が完了したタイミングでUnityちゃんのインスタンスを作成し、カメラをオンにするように実装されています。

    public override void OnJoinedRoom()
    {
        PhotonNetwork.Instantiate(
            PhotonObject.name,
            new Vector3(0f, 1f, 0f),
            Quaternion.identity,
            0
        );
        GameObject mainCamera = GameObject.FindWithTag("MainCamera");
        mainCamera.GetComponent<UnityChan.ThirdPersonCamera>().enabled = true;
    }

もう一度実行して、エラーが発生しないことを確認しておきましょう。

マルチプレイ準備

マルチプレイのテストをするためには、最低でも二つの環境から同時にアクセスする必要があります。 PC上でアプリを二つ起動するのが最も簡単な方法なのでPC向けにビルドしておきます。 ビルド対象のシーンが追加されていることと、PC向けになっていることを確認して、ビルドボタンを押します。 f:id:enia:20210312090527p:plain

保存場所は任意です私は「プロジェクトルート」->「Builds」->「PUN2_pc」というフォルダを作成して指定しました。 f:id:enia:20210312090946p:plain

ビルドは完了しましたが、コンソールを見ると大量に警告が出ています。 しかし、文を読んでみるとエラーではなく「deprecated」による警告だけのようですので、ここでは無視します。 f:id:enia:20210312091401p:plain

NOTE
チュートリアルではAutoBlink.csで以下のエラーが発生しています。

error CS0234 The tyep or namespace name 'Policy' does not exist in the namespace 'System.Security' 私の環境ではバージョン差異によるものだと思いますがこのエラーは発生しませんでした。
もし発生した場合は公式のyoutubeチュートリアル22:40あたりを見ると解決策が載っています。

それではアプリを二つ起動していきます。 1つは通常通りUnityから実行します。 この時点ではUnityちゃんは一人しか表示されていませんね。

f:id:enia:20210312092301p:plain

次に、エクスプローラで先ほどBuild結果を格納したフォルダに移動し、PUN2.exeを実行します。 二人目のUnityちゃんが登場しましたね! f:id:enia:20210312092637p:plain

オプション

矢印キーで操作できるので少し動かしてみましょう。 プレイヤー1の方で操作をすると、プレイヤー2のUnityちゃんが微妙に動いているように見えます。 次にこの動きを修正していきます。

プロジェクトウィンドウの検索ボックスに「UnityChanControl」と入力し、表示されたUnityChanControlScriptWithRgidBody.csを修正していきます。 f:id:enia:20210312093102p:plain

まず、継承するクラスをMonoBehaiviourから、Photon.Pun.MonoBehaviourPunに変更します。

public class UnityChanControlScriptWithRgidBody : Photon.Pun.MonoBehaviourPun // ★継承クラスの変更

次にFixedUpdateを修正し、プレイヤーの入力が、他のプレイヤーのUnityちゃんに影響を及ぼさないようにしていきます。 修正は先頭に1行追加するだけです。自身の操作の場合だけFixedUpdateが実行されるようになります。

void FixedUpdate ()
{
    if(photonView.IsMine) return;   // ★追加

さあ、再度Buildして試してみましょう。 確かに他のプレイヤーの影響は受けなくなったように見えますが、私の環境では逆に挙動がおかしくなったような気がします。

この3回で、プレイヤー間でネットワークを経由して位置情報などを共有する方法について学ぶことができました。チュートリアルに含まれなかったロビーの使い方や、部屋を指定して入室する方法などは今後実際にアプリを作りながら学んでいこうと思います。 今回はここまで!