猫でもわかるUnity入門(第21回 Photon PUN2を使ったマルチプレイゲーム作成 その2)

f:id:enia:20210228230354p:plain えにあです。 Unity + Photonを使ったオンラインマルチプレイゲームのサンプル作成の第2回を進めていきます。

**目次

Unityちゃんのプレハブ化

まず、Unityちゃんをプレハブ化しておきます。
ヒエラルキービューで「unitychan_dynamic_locomotion」をドラッグし、プロジェクトビューの「Assets」->「Demos」->「Resources」にドロップします。

プレハブの種類は原型プレハブを選択します。
f:id:enia:20210311192922p:plain

ここまでの手順で、Resourcesの下と、Scenesの下にファイルが作られています。
f:id:enia:20210311193026p:plain

プレハブ化したので、シーン上のUnityちゃんは削除します。
f:id:enia:20210311193133p:plain

ここで一度シーンを保存しましょう。
f:id:enia:20210311193320p:plain

UnityちゃんプレハブへのPhotonコンポーネント追加

プロジェクトビューで、プレハブ化した「unitychan_dynamic_locomotion」を選択し、インスペクターで「プレハブを開く」を選択します。
f:id:enia:20210311220632p:plain

コンポーネントを追加」から以下3つのコンポーネントを追加していきます。

  • PhotonView
  • PhotonTransformView
  • PhotonAnimatorView

f:id:enia:20210311221257p:plain

PhotonVIewを追加します。
f:id:enia:20210311221212p:plain

PhotonTransformViewを追加します。
f:id:enia:20210311221117p:plain

PhotonAnimatorViewを追加します。
f:id:enia:20210312080219p:plain

PhotonViewコンポーネントをUnityちゃんにアタッチすることで、Unityちゃんの位置情報などをネットワーク経由での同期対象に含めることが可能になります。

PhotonViewコンポーネントのObservedComponentプロパティを割り当てることでどの情報を同期するかを指定することができます。 Photon TransformView、Photon AnimatorViewが設定されていることを確認してください。Observable SearchがAuto Find Allになっていれば自動で設定されているはずです。 f:id:enia:20210311224004p:plain

PhotonTransformViewコンポーネントをObservedComponentに割り当てることで、transform(位置情報や回転)の同期が可能になります。

PhotonAnimatorViewコンポーネントをObservedComponntに割り当てることで、animatorのパラメータの同期が可能になります。

インスペクターで「Photon Transform View」を開きます。 「Syncrhonize Options」はどのTransform項目をプレイヤー間で同期するかを表しているようです。
「Position」と「Rotation」にチェックが入っていることを確認します。 おそらく、今回のサンプルではスケールは動的に変更されることがないので同期不要ということでしょう。 f:id:enia:20210311224312p:plain

インスペクターで「Photon Animator View」を開きます。 AnimatorViewでは、項目ごとに3つの同期方法を選択できます。

  1. disable(同期しない)
  2. discrete(非連続的 = 一定間隔で同期)
  3. continuous(連続的 = 常に同期)

ここではdiscreteとcontinuousを使って、Synchronize Parametersを以下のように変更します。 f:id:enia:20210311224712p:plain

オンラインマルチプレイスクリプト作成

Photonを使ったオンラインマルチプレイ用のスクリプトを作成していきます。

シーン上に接続スクリプトをアタッチするための空のオブジェクトを作成します。 トップメニューから「ゲームオブジェクト」->「空のオブジェクトを作成」を選択します。「PhotonControlelr」という名前に変更します。
f:id:enia:20210312072125p:plain

作成したオブジェクトにアタッチするためのスクリプトを作成します。 「Assets」->「Demos」->「Scripts」の下に、RandomMatchMakerという名前でC#スクリプトを作成します。
f:id:enia:20210312072753p:plain

次にスクリプトを開いて編集していきます。 今回のサンプルでは部屋は1つしかありません。 プレイヤーがランダムに部屋に入室するメソッドを用いるのですが、部屋が1つしかないため、全員が1つの部屋に入室することになります。

まずusingにPhoton関連のパッケージを追加します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun; // ★追加
using Photon.Realtime; // ★追加

次に、継承元クラスであるMonoBehaiviourをMonoBehaiviourPunCallbacksに変更します。 「イベントが発生した時」というのは、例えば「サーバに接続した時」、「部屋への入室が完了した時」といったものです。

public class RandomMatchMaker : MonoBehaviourPunCallbacks // ★変更

次に、ゲームオブジェクトを設定するためのフィールドを作成しておきます。 このフィールドは後でインスペクターから設定されます。

    // インスペクターから設定
    public GameObject PhotonObject;

次にStartメソッドを修正します。ConnectUsingSettingsメソッドを使うことで、先ほど設定した「PhotonServerSettings」を用いてサーバに接続することができます。

   void Start()
    {
        PhotonNetwork.ConnectUsingSettings();
    }

Updateメソッドは利用しないので削除します。※コメントアウトにしていますが、削除でOKです。

    // void Update()
    // {
        
    // }

次に、MonoBehaviourPunCallbacksクラスが提供する4つのコールバックメソッドをオーバーライドしていきます。

  1. OnConnectedToMaster
  2. OnJoinedLobby
  3. OnJoinRandomFailed
  4. OnJoinedRoom

ではまず、OnConnectedToMasterメソッドをオーバーライドします。このメソッドは、サーバへの接続が完了すると呼ばれるコールバックです。サーバへの接続が完了したら、ランダムで部屋に入室するように実装します。

    public override void OnConnectedToMaster()
    {
        PhotonNetwork.JoinRandomRoom();
    }

次に、OnJoinedLobbyメソッドをオーバーライドします。このメソッドはロビーへの入室が完了した時に呼ばれるコールバックです。今回のサンプルではロビーは使いませんが、ロビーに入室したら即座に部屋に入室するように実装しています。

    public override void OnJoinedLobby()
    {
        PhotonNetwork.JoinRandomRoom();
    }

次に、OnJoinRandomFailedをオーバーライドします。このメソッドは入室に失敗した時に呼ばれるコールバックです。一人目は部屋がないので、確実に入室に失敗します。そのため、このメソッドの中で部屋を作成しています。

部屋を作成するメソッドはCreateRoomです。第1引数にはルーム名を指定します。ルーム名は部屋を識別する場合に使いますが、今回は部屋が1つしかないのでnullを指定しています。

第2引数にはRoomOptionsを渡します。MaxPlayersを指定することで1部屋の最大に員数を制限することができます。

    // 入室に失敗した場合に呼ばれるコールバック
    // 1人目は部屋がないため必ず失敗するので部屋を作成する
    public override void OnJoinRandomFailed(short returnCode, string message)
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.MaxPlayers = 8; // 最大8人まで入室可能
        PhotonNetwork.CreateRoom(null, roomOptions); //第一引数はルーム名
    }

最後に、OnJoinedRoomメソッドをオーバーライドします。このメソッドは入室が完了した時に呼ばれるコールバックです。この後でインスペクターで設定するPhotonObjectプロパティに指定したオブジェクトの名前と、初期位置と回転を渡すことで、サーバ側にオブジェクトを同期します。

後半のカメラについては後ほど説明します。

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;
}

スクリプトの修正は以上です。 今回はここまで!

最終的なスクリプトは以下に記載しておきます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;

public class RandomMatchMaker : MonoBehaviourPunCallbacks
{
    // インスペクターから設定
    public GameObject PhotonObject;

    void Start()
    {
        PhotonNetwork.ConnectUsingSettings();
    }

    public override void OnConnectedToMaster()
    {
        PhotonNetwork.JoinRandomRoom();
    }

    public override void OnJoinedLobby()
    {
        PhotonNetwork.JoinRandomRoom();
    }

    public override void OnJoinRandomFailed(short returnCode, string message)
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.MaxPlayers = 8;
        PhotonNetwork.CreateRoom(null, roomOptions);
    }

    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;
    }
}