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秒