さて、今回も前回に引き続きRPC()
関数についてです。
RPC()
関数で呼ぶことが出来る引数の型には少し制限があるため、使う際には以下の内容に気を付けてください。
この記事にはUnity5.4.1f1及びPUN1.75を使用しています。
RPC()で呼ぶ引数について
PhotonView.RPC()
を使うことで関数をRPCとして呼ぶことができ、ルーム内の他のプレイヤーのゲームに同期させることが出来ます。
前回PhotonTargets
を調べる際に呼んだ際には、引数のない関数を呼んでいました。
引数として渡せる数について
では、引数のある関数をPhotonView.RPC()
を使って呼ぶにはどうすればよいのでしょうか。
それは第三引数に渡すパラメータがRPCで呼び出す関数の引数として使用されるのです。
using UnityEngine; public class DemoRPC : MonoBehaviour { public void SendMethod() { PhotonView photonView = GetComponent<PhotonView>(); photonView.RPC( "MethodRPC", PhotonTargets.All, 10 ); } [PunRPC] private void MethodRPC( int i_value ) { Debug.LogFormat( "MethodRPC value={0}", i_value ); } } // class DemoRPC
上記のphotonView.RPC( "MethodRPC", PhotonTargets.All, 10 );
のように使うことでRPCで呼ぶ関数に引数を渡すことが出来ます。
引数が複数個ある関数の場合は、第三引数以降に渡していけばOKです。
using UnityEngine; public class DemoRPC : MonoBehaviour { public void SendMethod() { PhotonView photonView = GetComponent<PhotonView>(); photonView.RPC( "MethodRPC", PhotonTargets.All, 10, "Hello", 3.14f ); } [PunRPC] private void MethodRPC( int i_valueA, string i_valueB, float i_valueC ) { Debug.LogFormat( "MethodRPC A={0}, B={1}, C={2}", i_valueA, i_valueB, i_valueC ); } } // class DemoRPC
ちなみに引数を間違えようものなら、容赦なくエラーが発生します。
using UnityEngine; public class DemoRPC : MonoBehaviour { public void SendMethod() { PhotonView photonView = GetComponent< PhotonView >(); photonView.RPC( "MethodRPC", PhotonTargets.All, 10 ); } [PunRPC] private void MethodRPC( int i_valueA, string i_valueB, float i_valueC ) { Debug.LogFormat( "MethodRPC A={0}, B={1}, C={2}", i_valueA, i_valueB, i_valueC ); } } // class DemoRPC
PhotonView with ID 1001 has no method "MethodRPC" that takes 1 argument(s): Int32
引数で使える型について
では引数として渡せる型はどうなのでしょうか。なんでも使えるのでしょうか。
試しにColor
型の引数を渡してみましょう。
using UnityEngine; public class DemoRPC : MonoBehaviour { public void SendMethod() { PhotonView photonView = GetComponent< PhotonView >(); photonView.RPC( "MethodRPC", PhotonTargets.All, Color.red ); } [PunRPC] private void MethodRPC( Color i_color ) { Debug.LogFormat( "MethodRPC value={0}", i_color ); } } // class DemoRPC
Exception: cannot serialize(): UnityEngine.Color
残念なことにエラーが出ました。
引数として渡せる型は、デフォルトでは以下のドキュメントに記述されたものしか渡せません。
それ以外の型はシリアライズの対応が行われていないため渡すことが出来ないのです。
これはOnPhotonSerializeView()
でも同様です。
ただ、シリアライズは自作することが出来ます。
自分でシリアライズ、デシリアライズ関数を作成することで、どんな型でもRPC()
関数の引数として使用することが出来ます。
試しにColor
のシリアライズを自作してみましょう。
ColorSerializer.cs
using UnityEngine; public static class ColorSerializer { public static void Register() { ExitGames.Client.Photon.PhotonPeer.RegisterType( typeof( Color ), (byte)'C', SerializeColor, DeserializeColor ); } private static byte[] SerializeColor( object i_customobject ) { Color color = (Color)i_customobject; var bytes = new byte[ 4 * sizeof( float ) ]; int index = 0; ExitGames.Client.Photon.Protocol.Serialize( color.r, bytes, ref index ); ExitGames.Client.Photon.Protocol.Serialize( color.g, bytes, ref index ); ExitGames.Client.Photon.Protocol.Serialize( color.b, bytes, ref index ); ExitGames.Client.Photon.Protocol.Serialize( color.a, bytes, ref index ); return bytes; } private static object DeserializeColor( byte[] i_bytes ) { var color = new Color(); int index = 0; ExitGames.Client.Photon.Protocol.Deserialize( out color.r, i_bytes, ref index ); ExitGames.Client.Photon.Protocol.Deserialize( out color.g, i_bytes, ref index ); ExitGames.Client.Photon.Protocol.Deserialize( out color.b, i_bytes, ref index ); ExitGames.Client.Photon.Protocol.Deserialize( out color.a, i_bytes, ref index ); return color; } } // class ColorSerializer
DemoRPC.cs
using UnityEngine; public class DemoRPC : MonoBehaviour { void Awake() { // 適当な箇所が無かったため、ここでColorのシリアライズの登録しています。 // 本来はプロジェクトの初期化を行う箇所でシリアライズの登録をしたほうが望ましいです。 // PhotonPeer.RegisterType()は、登録してあるものを再度登録してもエラーになることはないようです。 ColorSerializer.Register(); } void OnMouseDown() { SendMethod(); } public void SendMethod() { PhotonView photonView = GetComponent< PhotonView >(); var randColor = new Color( Random.value, Random.value, Random.value, 1.0f ); photonView.RPC( "MethodRPC", PhotonTargets.All, randColor ); } [PunRPC] private void MethodRPC( Color i_color ) { Renderer render = GetComponent<Renderer>(); render.material.color = i_color; } } // class DemoRPC
Color
を引数として渡すことが出来ました。
Color
はサイズが固定の構造体なので比較的に簡単にシリアライズを実装出来ました。
これがList
などを使った可変長のデータを持つクラスをシリアライズだとしたら自作はなかなか大変そうですね……。
おまけ 継承時
[PunRPC]
アトリビュートをつけた関数を持つクラスを以下のように継承するとします。
そして継承したクラスでPhotonView.RPC()
を使って継承元の関数を呼びます。
DemoRPCBase.cs
using UnityEngine; public class DemoRPCBase : MonoBehaviour { [PunRPC] private void MethodRPC( Color i_color ) { Renderer render = GetComponent<Renderer>(); render.material.color = i_color; } } // class DemoBase
DemoRPC.cs
using UnityEngine; public class DemoRPC : DemoRPCBase { void Awake() { // 適当な箇所が無かったため、ここでColorのシリアライズの登録しています。 // 本来はプロジェクトの初期化を行う箇所でシリアライズの登録をしたほうが望ましいです。 // PhotonPeer.RegisterType()は、登録してあるものを再度登録してもエラーになることはないようです。 ColorSerializer.Register(); } void OnMouseDown() { SendMethod(); } public void SendMethod() { PhotonView photonView = GetComponent< PhotonView >(); var randColor = new Color( Random.value, Random.value, Random.value, 1.0f ); photonView.RPC( "MethodRPC", PhotonTargets.All, randColor ); } } // class DemoRPC
上記のやり方はエラーになります。
PhotonView with ID 1001 has no method "MethodRPC" marked with the PunRPC or @PunRPC(JS) property! Args: Color
なぜかというと、呼ばれる関数MethodRPC()
のアクセス修飾子がPrivate
だからです。
継承元のクラスのPrivate関数は呼べなくて当然なのですが、PhotonView.RPC()
が文字列で関数を呼んでいるためSendMessage()
のような感覚に陥りがちです。
呼べないので注意しましょう。
上記のスクリプトはアクセス修飾子を変えることで呼び出すことが可能になります。
using UnityEngine; public class DemoRPCBase : MonoBehaviour { [PunRPC] protected void MethodRPC( Color i_color ) { Renderer render = GetComponent<Renderer>(); render.material.color = i_color; } } // class DemoBase
二回にわたってRPCを調べてきました。
便利なものですが、使い方を間違えると思わぬエラーやバグが発生するので扱いには気を付ける必要がありますね。