トップ 差分 一覧 ソース 検索 ヘルプ RSS ログイン

unity_photon_cloud_char

Roomの作成または参加 > キャラクタの同期(位置と回転のみ)


[Photon Cloud] キャラクタの同期(Demoのmonsterprefab)


PlayerがLobby/Roomへの接続と、サンプルにあるmonsterprefabのキャラクタのマルチプレイの同期は確認できました。
次に「monsterprefab」はどうやって同期を取ってるのだろう?というのを調査。
Inspectorを見てみると、いくつかScriptが割り当てられています。

myThirdPersonController/PhotonView/NetworkCharacter/ClickDetector/AudioRpcのスクリプトが存在。
このうち「PhotonView」はPhotonで提供されているスクリプト(Photon Unity Networking/Plugins/PhotonNetwork内)。
それ以外が、Demoプロジェクトのスクリプト。

 monsterprefabで使っているScript

myThirdPersonController

Assetの「Character Controller」の「ThirdPersonController」を派生させたクラス。
idle/walk/run/jumpのアニメーションを割り当て、キー操作で移動制御する汎用のスクリプトです。
PC用で、モバイルでは操作できず。

NetworkCharacter

Photon.MonoBehaviour派生クラス。
キャラクタの位置と回転を保持している。
OnPhotonSerializeView関数のコールバックで、他のプレイヤーとキャラクタデータを送受信している模様。
この実装がないと、他のプレイヤーと位置や回転の同期ができない。
徐々に目標の位置と回転に移行させることで、いきなりワープしてくるといった挙動を抑えている。

ClickDetector

MonoBehaviour派生クラス。
キャラクタの攻撃時の判定を行う?
なくても動作。

AudioRpc

Photon.MonoBehaviour派生クラス。
音データをRPCを使って他のプレイヤーで再生するクラス。

 同期処理に最低限必要な実装


Photonでの最小限の同期を行うためには、

  • Resourcesフォルダにキャラクタのprefabを配置する
  • prefabに、ComponentとしてPhotonViewを追加
  • prefabに、ComponentとしてNetworkCharacter(キャラクタ情報の送受信)を追加
  • prefabのInspector上で、NetworkCharacterをPhotonViewのObserveにドラッグして割り当て

を行い、
カラのGameObjectでPhoton.MonoBehaviour 派生クラスをComponentとして追加。
OnJoinedRoomでRoomに参加したときに、

GameObject monster = PhotonNetwork.Instantiate("monsterprefab", Vector3.zero, Quaternion.identity, 0);

としてprefabをInstantiateで実体化することにより、キャラクタの位置と回転が同期しました。

 同期を取るための通信手段


の2つが代表的。他にもあるようですが、とりあえずこの2つの手段でキャラクタの同期はなんとかなりそうです。

OnPhotonSerializeView関数コールバックを使う

DemoでのNetworkCharacter.csは以下のようになっています(日本語コメントを追加)。

using UnityEngine;

public class NetworkCharacter : Photon.MonoBehaviour {
    private Vector3 correctPlayerPos = Vector3.zero;            // We lerp towards this
    private Quaternion correctPlayerRot = Quaternion.identity;  // We lerp towards this
    // Update is called once per frame
    void Update() {
        // photonViewが自分自身ではない場合、位置と回転を反映.
        if (!photonView.isMine) {
            transform.position = Vector3.Lerp(transform.position, this.correctPlayerPos, Time.deltaTime * 5);
            transform.rotation = Quaternion.Lerp(transform.rotation, this.correctPlayerRot, Time.deltaTime * 5);
        }
    }

    /**
     * プレイヤー同士の位置/回転情報の同期をとる.
     * 自分のキャラクタの位置と回転を送信、自分以外のキャラクタの位置と回転を受信.
     */
    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) {
        if (stream.isWriting) {
            // We own this player: send the others our data
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);

            myThirdPersonController myC = GetComponent<myThirdPersonController>();
            stream.SendNext((int)myC._characterState);
        } else {
            // Network player, receive data
            this.correctPlayerPos = (Vector3)stream.ReceiveNext();
            this.correctPlayerRot = (Quaternion)stream.ReceiveNext();

            myThirdPersonController myC = GetComponent<myThirdPersonController>();
            myC._characterState = (CharacterState)stream.ReceiveNext();
        }
    }
}

Update関数内で「!photonView.isMine」により自分自身でない場合、位置と回転を徐々に目標の値に移行していってます。
OnPhotonSerializeViewコールバック関数は定期的に呼ばれ、
「stream.isWriting」がtrueの場合は、自分自身の位置と回転を他のプレイヤーに対して送信。
falseの場合は、他のプレイヤーの情報を受信し、privateに保持している位置と回転の値を更新。

PhotonViewのRPCを使う

音を鳴らすのにRPCを使ってイベントを送信。
カラのGameObjectにRandomMatchmaker(Photon.MonoBehaviour派生クラス)をComponentに追加して、OnJoinedRoom関数内でmyPhotonViewとしてPhotonViewの参照を保持。
OnGUI関数内で、

myPhotonView.RPC("Marco", PhotonTargets.All);

としている箇所でRPCを使っています。
第一引数の"Marco"は、prefabのComponentに追加されているAudioRpcスクリプト(Photon.MonoBehaviour派生クラス)で記載。

   [RPC]
   void Marco() {
       if (!this.enabled) return;

       Debug.Log("Marco");

       this.audio.clip = marco;
       this.audio.Play();
   }


Roomの作成または参加 > キャラクタの同期(位置と回転のみ)


最終更新時間:2014年05月27日 15時07分53秒