[[キャラクタの同期(Demoのmonsterprefab)|unity_photon_cloud_char]] > [[RPCで突発的にイベントを送る|unity_photon_cloud_rpc]] ---- !!![Photon Cloud] キャラクタの同期(位置と回転のみ) 「[[キャラクタの同期(Demoのmonsterprefab)|unity_photon_cloud_char]]」では、Demoについているmonsterprefabでの説明でした。 ここではもう少し噛み砕いて、単純なキャラクタをランダムな位置に配置して回転させる、というのを同期させる例を記載します。 ここでは「'''OnPhotonSerializeView'''」を使った同期処理を行います。 !!概要 ランダムな位置に指定の形状(ここではCube)を配置。 複数のアプリを起動すると、ランダムな位置に複数のCubeが反映される、という簡単な例です。 Resources内のGameObject(prefab)をシーンに配置し、複数の実行で位置と回転を同期させます。 この場合に、Componentとして何を割り当てるか、などを説明します。 !!カラのGameObjectを配置 Photonの制御を行うためのカラのGameObjectをシーンの配置し、以下のスクリプトを割り当て。 PhotonCore.cs using UnityEngine; using System.Collections; public class PhotonCore : Photon.MonoBehaviour { private PhotonView m_photonView; // Use this for initialization void Start () { PhotonNetwork.ConnectUsingSettings("0.1"); if (string.IsNullOrEmpty(PhotonNetwork.playerName)) { PhotonNetwork.playerName = "hoehoe"; } } // Update is called once per frame void Update () { } /** * Lobbyに入ったときに呼ばれる. */ void OnJoinedLobby() { // RoomにJoinする. PhotonNetwork.JoinRandomRoom(); Debug.Log("inRoom = " + PhotonNetwork.inRoom); } void OnPhotonRandomJoinFailed() { PhotonNetwork.CreateRoom("room1"); // Roomの生成. } /** * RoomへのJoinに成功. */ void OnJoinedRoom() { Debug.Log("inRoom = " + PhotonNetwork.inRoom); Debug.Log("OnJoinedRoom ! " + PhotonNetwork.room.name); Debug.Log("countOfPlayersInRooms = " + PhotonNetwork.countOfPlayersInRooms); // Resources内の「Cube」を作成し、シーンに配置. GameObject cubeG = PhotonNetwork.Instantiate("Cube", Vector3.zero, Quaternion.identity, 0); if (cubeG == null) { Debug.Log("Cube = null"); return; } // 位置をランダムに指定. System.Random rand = new System.Random(); float fx = (float)(rand.NextDouble() * 8.0f) - 4.0f; float fz = (float)(rand.NextDouble() * 8.0f) - 4.0f; cubeG.transform.localPosition = new Vector3(fx, 3.0f, fz); m_photonView = this.GetComponent(); } void OnGUI() { if (GUILayout.Button("Quit")) { Application.Quit(); return; } GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString()); if (PhotonNetwork.room != null) GUILayout.Label("Room : " + PhotonNetwork.room.name); GUILayout.Label("Player : " + PhotonNetwork.playerName); } } Lobbyへの参加(Join)と、Roomへの参加は「PhotonNetwork.JoinRandomRoom」を使用して行い、Room名は「room1」としました。 Roomに参加した直後に呼ばれる「OnJoinedRoom」関数で、 GameObject cubeG = PhotonNetwork.Instantiate("Cube", Vector3.zero, Quaternion.identity, 0); で、Resourcesフォルダ内に存在する「Cube」を複製してシーンに配置します。 System.Random rand = new System.Random(); float fx = (float)(rand.NextDouble() * 8.0f) - 4.0f; float fz = (float)(rand.NextDouble() * 8.0f) - 4.0f; cubeG.transform.localPosition = new Vector3(fx, 3.0f, fz); でランダムな位置に形状を移動させています。 これにより、起動したアプリごとに異なる位置にCubeの形状が存在する状態にしています。 !!共有できるCubeを作成する 最終的には、以下のようにResources/Cubeのprefabを配置し、 Componentとして「PhotonView」と、自作した「MyNetworkCharacter」のスクリプトを割り当てて、PhotonViewにMyNetworkCharacterを結び付けます。 これらの手順を解説。 {{ref_image unity_photon_view_01.png}} !シーンにCubeを配置 シーンにGameObjectとしてCubeを配置します。これは、標準で用意されている形状だけではなくて、fbxでインポートしたキャラクタでももちろんOKです。 !CubeにPhotonViewをComponentとして追加 CubeのGameObjectに対し、Inspectorウィンドウの「Add Component」を押して「PhotonView」で検索。 これをComponentとして追加します。 追加した段階では、ObserveはNoneになっています。 {{ref_image unity_photon_view_02.png}} !同期用のスクリプトを作成 キャラクタの位置と回転を同期させますので、それ用のスクリプトを記述します。 「MyNetworkCharacter.cs」としました。 using UnityEngine; using System.Collections; public class MyNetworkCharacter : Photon.MonoBehaviour { private Vector3 m_correctPlayerPosition; // 位置情報. private Quaternion m_m_correctPlayerRotation; // 回転情報. // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (!photonView.isMine) { // photonViewが自分自身ではない場合、位置と回転を反映. transform.position = Vector3.Lerp(transform.position, this.m_correctPlayerPosition, Time.deltaTime * 5); transform.rotation = Quaternion.Lerp(transform.rotation, this.m_m_correctPlayerRotation, Time.deltaTime * 5); } } /** * プレイヤー同士の位置/回転情報の同期をとる. * 自分のキャラクタの位置と回転を送信、自分以外のキャラクタの位置と回転を受信. */ void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { if (stream.isWriting) { // 自分のプレイヤー情報を送信. stream.SendNext(transform.position); stream.SendNext(transform.rotation); } else { // 他のプレイヤー情報を受信. this.m_correctPlayerPosition = (Vector3)stream.ReceiveNext(); this.m_m_correctPlayerRotation = (Quaternion)stream.ReceiveNext(); } } } アプリ内に表示されているマルチプレイの他のキャラクタもGameObjectとしてシーンに配置され、それぞれで「PhotonView」と「MyNetworkCharacter」を持った状態になります。 「Photon.MonoBehaviour」派生クラスとしています。 このクラスでは、マルチプレイの場合の複数のキャラクタの情報をコールバックとして送信/受信します。 privateとして、m_correctPlayerPositionで位置、m_m_correctPlayerRotationで回転を保持します。 Update関数の if (!photonView.isMine) { // photonViewが自分自身ではない場合、位置と回転を反映. transform.position = Vector3.Lerp(transform.position, this.m_correctPlayerPosition, Time.deltaTime * 5); transform.rotation = Quaternion.Lerp(transform.rotation, this.m_m_correctPlayerRotation, Time.deltaTime * 5); } にて、「photonView.isMine」は自分自身かどうかをboolで取得します。 自分自身の位置と回転は既知であるので、OnPhotonSerializeView関数であらかじめ受け取ったそれ以外の位置と回転情報をシーン内のthisのGameObjectに反映します。 このときに、現在位置と目標位置を時間により徐々に近づけることで、急激に値が変化するのを防ぐようにしてます。 50fpsの場合は「Time.deltaTime」はTime.deltaTimeは0.02。それの5倍なので、0.02 * 5 = 0.1 でtransform.position→this.m_correctPlayerPosition、transform.rotation→this.m_m_correctPlayerRotation、に徐々に移行させます。 「OnPhotonSerializeView」関数は、自分以外に自分の情報を送る場合、他の情報を自分が受け取った場合にコールバックされます。 void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { if (stream.isWriting) { // 自分のプレイヤー情報を送信. stream.SendNext(transform.position); stream.SendNext(transform.rotation); } else { // 他のプレイヤー情報を受信. this.m_correctPlayerPosition = (Vector3)stream.ReceiveNext(); this.m_m_correctPlayerRotation = (Quaternion)stream.ReceiveNext(); } } 「stream.isWriting」がtrueの場合は自分から他に情報を送ります。 stream.SendNext関数で、位置と回転を送信。 SendNextは、Vector2/Vector3/Quaternion/int/float/bool/stringなどを指定できます。 「stream.isWriting」がfalseの場合は他から情報を受け取ります。 stream.ReceiveNext関数で、SendNextで送信した順番で取得していきます。 !同期用スクリプトをCubeに割り当て 作成した「MyNetworkCharacter.cs」をシーンに配置しているCubeにドラッグしてComponentとして割り当てます。 その後、Inspectorウィンドウ上で「MyNetworkCharacter」を「PhotonView」のObserveにドラッグして関連付けします。 {{ref_image unity_photon_view_03.png}} !CubeをResourcesディレクトリにドラッグ ProjectにResourcesディレクトリを作成。 シーン上のCubeをResourcesディレクトリ内にドラッグします(Prefabとして格納)。 後は、シーン上のCubeは不要なので削除。 これでアプリとして実行するとCube形状がランダムな位置に配置され、 複数起動するごとにランダムな位置は同期されて表示されるのを確認できます。 以上がOnPhotonSerializeViewを使った同期処理になります。 {{ref_image unity_photon_view_04.png}} ---- [[キャラクタの同期(Demoのmonsterprefab)|unity_photon_cloud_char]] > [[RPCで突発的にイベントを送る|unity_photon_cloud_rpc]] ---- {{lastmodified}}