PUNを使ってネットワーク同期を行う -Vol.1-

photon_realtime_turquoise

Photon運営事務局です。
今回はPhotonとUnityを使って作る際に一番最初に読んで頂きたい記事となっています。UnityのCubeを使って簡単なネットワークの同期を行います。少しボリュームが大きくなっていますが、最後まで読んで頂ければ幸いです。

はじめに

Photon Realtimeとは、クラウド形式のリアルタイム同期に特化したネットワークエンジンのサービスです。
これを利用することによって、今流行りの同時対戦ゲームがサーバを用意せず、容易(ダジャレではないです)に作ることが可能です。
またAndroid/iOSネイティブだけじゃなく、UnityやMarmaladeといったゲームエンジンにも対応しています。
PhotonとUnityを用いてネットワーク同期の基礎を解説します。

UntiyでPhoton Realtimeを利用するにはPhoton SDKとPhoton Unity Networking(通称PUN)の2種類のSDKがあります。
UnityでPhotonを使う場合ですと、PUNが取っ付き易いのでおすすめです。つきまして、ここではPUNを使ってオブジェクトを同期する方法について説明します。
なお、Photon SDKとPUNの違いはこちらの記事をご参照ください。

Photon SDKとPhoton Unity Networking(PUN) FreeとPUN+の違いについて
http://blog.photoncloud.jp/photon-sdk-vs-pun/

App IDを取得する

Photon Realtimeは無料のアカウントを取得するとすぐに利用できます。
アカウントの取得は下記URLからメールアドレスを登録するだけです。
https://www-jp.exitgames.com/ja/Realtime

完了すると、登録したメールアドレスにURLが記載されていますので、そちらを押下し登録を完了してください。
ダッシュボードからAppIDを取得します。1アプリケーションに対して1つAppIDを発行することが出来ます。
https://www-jp.exitgames.com/ja/Realtime/Dashboard

PUNを用意する

まず、Unityで適当なプロジェクトを作成します(例:photon-demo-01)。
PUNはUnity Asset Storeからダウンロードできます。
UnityでAsset Storeを開き、検索ウィンドウから「Photon Unity Networking Free」と入力し検索を行ってください。
何種類か表示されると思いますが、ここでは無償版の下記のエメラルドグリーンの物を利用します。

001-1

Asset Storeからインポートを行います。
この際、はじめから入っているデモは使わないので、あらかじめPhoton Unity Networking/Demosのチェックは外して、インポートを行わないようにしてください。
インポートが完了すると、Projectビューに「Photon Unity Networking」のフォルダが作成されていることを確認してください。

Photonとの接続を行う

PUNを用いて、もっとも簡単なPhotonネットワークの接続方法は、PUN Wizardを用いる方法です。スクリプトで接続情報を記述することも可能ですが、PUN Wizardを使うと、GUIで直感的に接続設定が可能です。
それでは設定を行いましょう。
Unityのメニューの[Window]→[Photon Unity Networking]→[PUN Wizard]を選択するとPUN Wizardの小窓が表示されます。

008-1

これらのウィンドウについては下記の通りです。

PUN Wizard
ボタン名 詳細
Settings / Setup Photonの接続先に関するセットアップウィザードを表示します
Settings File / Locate settings asset ProjectビューにPhotonの設定ファイルの場所をハイライト表示します
Converter – Start Unity標準のUnity NetworkingをPhoton Unity Networkingに変換します
Documentation APIのドキュメントやフォーラムなどを表示します(英語)

接続先の設定を行うので、ここではSettingsの横の[Setup]を押下してください。
画面が変わり、画像のような画面になると思います。
この画面ではPhotonネットワークに接続するための必要な情報を設定します。
今回の場合、設定する内容は下記の2つです。

  • App ID
  • クラウドリージョン

Your AppIdに、はじめに取得したAppIDを入力します。
Cloud Regionはここでは[best]を選択します。
完了すると[Save]ボタンを押下してください。

002-1

[Success]の確認ダイアログが出れば完了です。

Photonネットワークに接続するスクリプトを作る

Photonを利用し、オブジェクトの同期を行うためにはルームに入室し、
ルーム内でPhotonが用意しているメソッドを用いて、インスタンスの生成を行う必要があります。
最も簡単な例として一つの部屋に全員が入る仕組みで説明いたします。

Hierarchyに「Photon Manager」という名前の空のオブジェクトを作成し、下記のスクリプトを付与してください。

using UnityEngine;
using System.Collections;

public class PhotonManager : Photon.MonoBehaviour
{
    public string objectName;

    void Start()
    {
        // Photonへの接続を行う
        PhotonNetwork.ConnectUsingSettings("0.1");

        // PhotonNetworkの更新回数のセット
        PhotonNetwork.sendRate = 30;
    }

    /// <summary>
    /// ロビーに接続すると呼ばれるメソッド
    /// </summary>
    void OnJoinedLobby()
    {
        // ランダムでルームに入室する
        PhotonNetwork.JoinRandomRoom();
    }

    /// <summary>
    /// ランダムで部屋に入室できなかった場合呼ばれるメソッド
    /// </summary>
    void OnPhotonRandomJoinFailed()
    {
        // ルームを作成、部屋名は今回はnullに設定
        PhotonNetwork.CreateRoom(null);
    }

    /// <summary>
    /// ルームに入室成功した場合呼ばれるメソッド
    /// </summary>
    void OnJoinedRoom()
    {
        GameObject cube = PhotonNetwork.Instantiate(objectName, Vector3.zero, Quaternion.identity, 0);
    }

    /// <summary>
    /// UnityのGameウィンドウに表示させる
    /// </summary>
    void OnGUI()
    {
        // Photonのステータスをラベルで表示させています
        GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString());
    }
}

スクリプトの解説を行います。
4行目はPhoton.MonoBehaviourを継承しています。PhotonのMonoBehaviourを継承することによって、オーバーライドされたメソッドを呼び出すことが可能となります。

11行目でPhotonNetwork.ConnectUsingSettings(“0.1″);を利用してPhotonに接続を行っています。PhotonNetwork.ConnectUsingSettingsはPUN Wizardで設定された接続情報を元にPhotonに接続します。
厳密には、PUN Wizardを作成すると、UnityのProjectウィンドウの「Photon Unity Networking/Resources/PhotonServerSettings」というファイルが作成されます。ConnectUsingSettingsではこの設定ファイルを元に接続を行うメソッドとなっています。
また、引数にてゲームバージョンを指定することが可能です。同一のバージョン間でのみ接続が可能になっています。バージョンはstring文字列で設定できます。

003-1

14行目でSendRateの設定を行っています。
SendRateの設定が無い状態ですとカクついて見えるため、今回は秒間30回としてセットしています。

20行目からはロビーに接続すると呼ばれるオーバーライドメソッドです。
今回のサンプルでは、Photonに接続するとまずはじめにこのメソッドが呼ばれます。
23行目では、PhotonNetwork.JoinRandomRoom()を用いてランダムでルームに入室しています。今回のサンプルではルームが1つしかないため、この方法で入室しています。

29行目からは、ランダムで部屋に入室できなかった場合のオーバーライドメソッドです。1人目は必ず入室に失敗するので、ルームを作成するようにしています。
32行目でルームを作成しています。CreateRoomメソッドではルーム名を設定することが可能です。入室時に一意するために利用可能ですが、今回は利用しません。

38行目からは、ルームの入室に成功した場合のオーバーライドメソッドです。ルームに入室すると、40行目のPhotonNetwork.Instantiateメソッドを用いて実際にCubeオブジェクトを生成しています。第1引数でインスタンス化するPrefabの名前をしています。この時、場所はどこでも構わないのですが、Resourcesフォルダ直下にPrefabを配置してやる必要があります。第2引数ではVector3型のposition、第3引数ではQuaternion型のrotationを指定します。第4引数ではPhotonViewのグループ番号を設定しますが、ここでは0としておきます。

また、49行目では現在のPhotonのステータスを表示しています。
ゲームを実行するとGameウィンドウの左上に表示されると思います。

なお、ここまでのオーバーライドメソッドを整理すると下記の通りです。

Photonのオーバーライドメソッド
メソッド名 内容
OnJoinedLobby() ロビーに接続すると呼ばれるメソッド
OnPhotonRandomJoinFailed() ランダムで部屋に入室できなかった場合呼ばれるメソッド
OnPhotonRandomJoinFailed() ルームに入室成功した場合呼ばれるメソッド

その他のPhotonで利用できるオーバーライドメソッドについては、別のブログ記事にて解説したいと思います。
これらのメソッドやPhotonNetworkの詳細については、PUNに包含されているAPIリファレンスをご覧下さい。

デモシーンの作成

ここで一度、デモシーンを作るための操作を行います。
はじめにHierarchyからPlaneを作成します。
PlaneのTransformは下記のように設定しておいてください。

005-1

つぎに、Directional lightを適当な位置に追加しておいてください。

最後にCubeを作成します。CubeのTransformは下記の通り設定してください。
設定が完了するとCubeをResourcesフォルダ直下にPrefabとして置いておいてください。Prefab化が完了すると、Hierarchy内のCubeは削除しても構いません。

006-1

Cubeは操作できるようにするので、下記のコードを作成します。
スクリプト名はGameControllerと設定しておいてください。
なお、このスクリプトの説明は今回は割愛します(次回以降説明予定)。

using UnityEngine;
using System.Collections;

public class GameController : MonoBehaviour
{

    private PhotonView photonView;

    void Start()
    {
        photonView = PhotonView.Get(this);
    }

    void Update()
    {
        if (photonView.isMine)
        {
            float x = Input.GetAxis("Horizontal");
            float z = Input.GetAxis("Vertical");
            transform.Translate(x * 0.2f, 0, z * 0.2f);
        }
    }
}

GameControllerのスクリプトはPrefab化したCubeオブジェクトに付与しておいてください。これで、CubeをWASDキーで操作することが可能となります。

ここまでのHierarchyは画像のようになっているかと思います。

007-1

PhotonViewの設定を行う

PhotonViewスクリプトとは、UnityのNetworkViewを元にしたPhotonのコンポーネントです。
Photonでオブジェクトの同期を行うには、Photonのネットワーク上で同期を行うオブジェクトにPhotonViewスクリプトを付与してやる必要があります。
これを利用すると、オブジェクトの位置や傾き、キャラクターのアニメーションなどがそれぞれの画面で同一の動作を行えるようになります。

004-1

それでは、実際にCubeのPrefabにPhotonViewスクリプトを付与してみましょう。
すると画像のように表示されるかと思います。

PhotonViewのパラメータ
パラメータ名 説明
Owner Prefabを所有しているPlayer
View ID PhotonViewのID番号
Observe 同期させたいオブジェクト

これらのパラメータは下記の通りです。
実行前の状態だと、OwnerとView IDはSet at runtimeと表示されているかと思います。これらは実行時にパラメータが設定されます。

Observeに同期させたい対象をドラッグ&ドロップすると、その対象がネットワーク上で同期されるようになります。例えば位置を同期させたい場合はTransformをドラッグ&ドロップします。
また、スクリプトを同期させることも可能です。この場合は一旦オブジェクトにスクリプトを付与し、その付与されたスクリプトをさらにObserveにドラッグ&ドロップすると完了です。

ここではTransformの値をドラッグ&ドロップしてみましょう。
すると、ObserveのにTransfromのアイコンとCubeと表示されたかと思います。
またその下にOnserve optionとSerializationが追加されているのを確認してください。

PhotonViewのパラメータ
パラメータ名 説明
Observe option 同期モードの設定を行います
Serialization シリアライズを行う対象

Observe optionの詳細については下記の通りです。Photonでの信頼性レベルについて設定が可能です。
すばやく頻繁な更新を行うため、パケットロスを行わない場合はUnreliable、またはUnreliable On Changeを使用することをおすすめします。逆にデータが頻繁に変更されない場合はReliable Delta Compressedをご利用ください。

Observe optionについて
パラメータ名 説明
Off 同期は行われません。RPCだけを利用したい時に最適です
Reliable Delta Compressed 最後の状態と現在の状態の差が送信され、変更が無い場合は何も送信されません
Unreliable すべて送信されます。転送量が大きくなりますが、パケット損失の機会が減ります。
Unreliable On Change Unreliableですが、変更があった場合のみ送信します

Serializationの詳細については下記の通りです。
例として、Transformコンポーネントをシリアライズする場合、位置・回転・スケールをネットワークを通して渡すことが可能です。

Serializationについて
パラメータ名 説明
Only Position Positionのみシリアライズを行います
Only Rotation Rotationのみシリアライズを行います
Only Scale Scaleのみシリアライズを行います
Position and Rotation PositionとRotationをシリアライズします
All Transformのすべてをシリアライズします

以上で設定は完了です。
実行するとCubeのPrefabが表示されていると思います。
WASDで操作可能ですので、複数ウィンドウで実行しCubeの位置が正しく同期されていることを確認してみてください。

ここまでの作ったサンプルプロジェクトは下記にあります。
サンプルプロジェクトですが、Asset StoreからPUNをインポートし、AppIdの設定後、当サンプルプロジェクトをインポートしてください。
Photon Team Japan/ScenesのフォルダのMainシーンが今回の記事と同等の内容になっていますので、是非一度ご覧になってお試しください。

サンプルプロジェクト