チュートリアル: Pong - メッセージの送受信
ARDKの機能を使用して作った、ARマルチプレイヤー版『Pong』のUnityプロジェクト例です。このチュートリアルでは、プロジェクトを正しく機能させるためのUnity上での各ステップやC#スクリプトの使用法をご確認いただけます。この使用例では低レベルメッセージを使ってプレイヤー間のデータ送信を行っています。このプロジェクトの別バージョンでは、プレイヤー同期用のメッセージ送受信プロセスを能率化する高レベルAPIオブジェクト(HLAPI)のセットアップおよび使用例が示されています (こちら) 。
MessagingManagerによるメッセージの送受信
各スクリプトがそれぞれメッセージを受信して処理することも可能ですが、単体のメッセージ処理マネージャを用いれば多数のクラス間で整理が行えます。MessagingManager
は Monobehaviour
ではありません。その代わり、ネットワークイベントおよびメソッド呼び出しによってすべてのメソッドを処理します。
セットアップと参考情報
MessagingManager
のリファレンスは主にネットワーク関連であり、 MessagingManager
はローカルの GameController
と BallBehaviour
へのリファレンスを保持して、メッセージが受信されたときにイベントへ通知します。
/// A manager that handles outgoing and incoming messages public class MessagingManager { // Reference to the networking object private IMultipeerNetworking _networking; // References to the local game controller and ball private GameController _controller; private BallBehaviour _ball; private readonly MemoryStream _builderMemoryStream = new MemoryStream(24);
MessagingManagerの初期化
MessagingManager
は GameController
によって初期化され、関連する情報を渡します。サブスクライブしているネットワークイベントは IARNetworking.Networking.PeerDataReceived
のみです。メッセージタグの一貫性を保つために列挙型が使用されます。
// Enums for the various message types private enum _MessageType: uint { BallHitMessage = 1, GoalScoredMessage, BallPositionMessage, SpawnGameObjectsMessage } // Initialize manager with relevant data and references internal void InitializeMessagingManager ( IMultipeerNetworking networking, GameController controller ) { _networking = networking; _controller = controller; _networking.PeerDataReceived += OnDidReceiveDataFromPeer; } // After the game is created, give a reference to the ball internal void SetBallReference(BallBehaviour ball) { _ball = ball; }
送信メッセージ
ローカルプレイヤーが他のプレイヤーにメッセージを送信する際、これらのメソッドの1つが呼び出されます。各メッセージにはタグが含まれているため、受信側のプレーヤーはデータの解析方法を知ることができます。BroadcastData
はネットワークセッション内の他のすべてのピアにデータを送信し、 SendDataToPeer
は1つのピアにデータを送ります。 ヘルパーメソッド SerializeVector3
は Vector3``を ``byte[]
に変換するために使用されます。
// Signal to host that a non-host has hit the ball, host should handle logic internal void BallHitByPlayer(IPeer host, Vector3 direction) { _networking.SendDataToPeer ( (uint)_MessageType.BallHitMessage, SerializeVector3(direction), host, TransportType.UnreliableUnordered ); } // Signal to non-hosts that a goal has been scored, reset the ball and update score internal void GoalScored(String color) { var message = new byte[1]; if (color == "red") message[0] = 0; else message[0] = 1; _networking.BroadcastData ( (uint)_MessageType.GoalScoredMessage, message, TransportType.ReliableUnordered ); } // Signal to non-hosts the ball's position every frame internal void BroadcastBallPosition(Vector3 position) { _networking.BroadcastData ( (uint)_MessageType.BallPositionMessage, SerializeVector3(position), TransportType.UnreliableUnordered ); } // Spawn game objects with a position and rotation internal void SpawnGameObjects(Vector3 position) { _networking.BroadcastData ( (uint)_MessageType.SpawnGameObjectsMessage, SerializeVector3(position), TransportType.ReliableUnordered ); }
メッセージの受信
一方、ネットワークからメッセージが受信されるたびにコールバック OnDidReceiveDataFromPeer
が呼び出されます。このメソッドはタグに応じてメッセージを処理し、 GameController
と BallBehaviour
へのリファレンスを使用してゲームロジックを適切に処理します。SerializeVector3
と同様、 byte[]
を Vector3
にコンバートするヘルパーメソッド DeserializeVector3
があります。
private void OnDidReceiveDataFromPeer(PeerDataReceivedArgs args) { var data = args.CopyData(); switch ((_MessageType)args.Tag) { case _MessageType.BallHitMessage: _ball.Hit(DeserializeVector3(data)); break; case _MessageType.GoalScoredMessage: if (data[0] == 0) { Debug.Log("Point scored for team blue"); _controller.BlueScore += 1; } else { Debug.Log("Point scored for team red"); _controller.RedScore += 1; } _controller.score.text = string.Format ( "Score: {0} - {1}", _controller.RedScore, _controller.BlueScore ); break; case _MessageType.BallPositionMessage: _controller.SetBallLocation(DeserializeVector3(data)); break; case _MessageType.SpawnGameObjectsMessage: Debug.Log("Creating game objects"); _controller.InstantiateObjects(DeserializeVector3(data)); break; default: throw new ArgumentException("Received unknown tag from message"); } }
削除
このオブジェクトを削除する際は、ネットワークイベントからサブスクライブ解除を行います。
// Remove callback from networking object on destruction internal void Destroy() { _networking.PeerDataReceived -= OnDidReceiveDataFromPeer; }
シリアライズ
ネットワークへのデータ送信には byte[]
のデータが必要です。ARDKでは様々なオブジェクトタイプをシリアライズおよびデシリアライズしてメッセージ送信を円滑化するためのヘルパーを利用できます。このプロジェクトは位置データに関して主に Vector3
を使用するため、 Vector3
のシリアライズおよびデシリアライズには2つのヘルパーが存在します。
// Helper to serialize a Vector3 into a byte[] to be passed over the network private byte[] SerializeVector3(Vector3 vector) { _builderMemoryStream.Position = 0; _builderMemoryStream.SetLength(0); using (var binarySerializer = new BinarySerializer(_builderMemoryStream)) Vector3Serializer.Instance.Serialize(binarySerializer, vector); return _builderMemoryStream.ToArray(); } // Helper to deserialize a byte[] received from the network into a Vector3 private Vector3 DeserializeVector3(byte[] data) { using(var readingStream = new MemoryStream(data)) using (var binaryDeserializer = new BinaryDeserializer(readingStream)) return Vector3Serializer.Instance.Deserialize(binaryDeserializer); }