- 追加された行はこのように表示されます。
- 削除された行は
このように表示されます。
[[Roomの作成または参加|unity_photon_cloud_room_create_join]] > [[キャラクタの同期(位置と回転のみ)|unity_photon_cloud_char_pos_rot]]
----
!!![Photon Cloud] キャラクタの同期(Demoのmonsterprefab)
PlayerがLobby/Roomへの接続と、サンプルにあるmonsterprefabのキャラクタのマルチプレイの同期は確認できました。
次に「monsterprefab」はどうやって同期を取ってるのだろう?というのを調査。
Inspectorを見てみると、いくつかScriptが割り当てられています。
{{ref_image unity_photon_char.png}}
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にドラッグして割り当て
{{ref_image unity_photon_serialize_drag.png}}
を行い、
カラのGameObjectでPhoton.MonoBehaviour 派生クラスをComponentとして追加。
OnJoinedRoomでRoomに参加したときに、
GameObject monster = PhotonNetwork.Instantiate("monsterprefab", Vector3.zero, Quaternion.identity, 0);
としてprefabをInstantiateで実体化することにより、キャラクタの位置と回転が同期しました。
!!同期を取るための通信手段
*OnPhotonSerializeView関数コールバックを使う
*PhotonViewのRPCを使う
*[[OnPhotonSerializeView関数コールバックを使う|unity_photon_cloud_char_pos_rot]]
*[[PhotonViewのRPCを使う|unity_photon_cloud_rpc]]
の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の作成または参加|unity_photon_cloud_room_create_join]] > [[キャラクタの同期(位置と回転のみ)|unity_photon_cloud_char_pos_rot]]
----
{{lastmodified}}