はじめに
送受信に使うカスタムオブジェクトを定義
自分で定義したクラスを送受信で使いたい場合は次のようにし、共有用のディレクトリに入れる。
[MessagePackObject] public class Player { [Key(0)] public string Name { get; set; } [Key(1)] public Vector3 Position { get; set; } [Key(2)] public Quaternion Rotation { get; set; } }
API通信の場合
全体の流れは以下の通り
- インターフェースを定義(共有ディレクトリ)
- サーバー側でインターフェースを実装する
- クライアント側から呼び出す
インターフェースを定義(共有ディレクトリ)
実装したいメソッドを持つインターフェースを定義する。 その定義が書かれたスクリプトは、サーバーと共有するディレクトリ内に入れる
using Grpc.Core; using MagicOnion; using MagicOnion.Server; using System; public interface IMyFirstService : IService<IMyFirstService> { // Return type must be `UnaryResult<T>` or `Task<UnaryResult<T>>`. // If you can use C# 7.0 or newer, recommend to use `UnaryResult<T>`. UnaryResult<int> SumAsync(int x, int y); }
インターフェースを実装する(サーバー)
定義したインターフェースを実装し、そのスクリプトをサーバー側に置く。
using Grpc.Core; using MagicOnion; using MagicOnion.Server; using System; // implement RPC service to Server Project. // inehrit ServiceBase<interface>, interface public class MyFirstService : ServiceBase<IMyFirstService>, IMyFirstService { // You can use async syntax directly. public async UnaryResult<int> SumAsync(int x, int y) { Logger.Debug($"Received:{x}, {y}"); return x + y; } }
Unity側で呼び出す(クライアント)
// standard gRPC channel var channel = new Channel("localhost", 12345, ChannelCredentials.Insecure); // get MagicOnion dynamic client proxy var client = MagicOnionClient.Create<IMyFirstService>(channel); // call method. var result = await client.SumAsync(100, 200); Console.WriteLine("Client Received:" + result);
(注:メソッドを実行する時はawaitを必ずつける)
リアルタイム通信の場合
全体の流れは以下の通り
- クライアント側のインターフェース(Receiver)を作成
- サーバー側のインターフェース(Hub)を作成
- サーバー側(Hub)を実装
- クライアント側(Receiver)を実装
インターフェースを定義
インターフェースを定義し、共有ディレクトリに入れる。
サーバーが呼び出す、クライアント(Receiver)の定義
public interface ISampleHubReceiver { /// <summary> /// 誰かがゲームに接続したことをクライアントに伝える /// </summary> void OnJoin(string name); /// <summary> /// 誰かがゲームから切断したことをクライアントに伝える /// </summary> void OnLeave(string name); /// <summary> /// 誰かが発言した事をクライアントに伝える /// </summary> void OnSendMessage(string name, string message); /// <summary> /// 誰かが移動した事をクライアントに伝える /// </summary> void OnMovePosition(Player player); }
クライアントが呼び出す、サーバー(Hub)の定義
using MagicOnion; using System.Threading.Tasks; using UnityEngine; public interface ISampleHub : IStreamingHub<ISampleHub, ISampleHubReceiver> { /// <summary> /// ゲームに接続することをサーバに伝える /// </summary> Task JoinAsync(Player player); /// <summary> /// ゲームから切断することをサーバに伝える /// </summary> Task LeaveAsync(); /// <summary> /// メッセージをサーバに伝える /// </summary> Task SendMessageAsync(string message); /// <summary> /// 移動したことをサーバに伝える /// </summary> Task MovePositionAsync(Vector3 position); }
サーバー側(Hub)の実装
using MagicOnion.Server.Hubs; using System.Threading.Tasks; using UnityEngine; public class SampleHub : StreamingHubBase<ISampleHub, ISampleHubReceiver>, ISampleHub { IGroup room; Player me; public async Task JoinAsync(Player player) { //ルームは全員固定 const string roomName = "SampleRoom"; //ルームに参加&ルームを保持 this.room = await this.Group.AddAsync(roomName); //自分の情報も保持 me = player; //参加したことをルームに参加している全メンバーに通知 this.Broadcast(room).OnJoin(me.Name); } public async Task LeaveAsync() { //ルーム内のメンバーから自分を削除 await room.RemoveAsync(this.Context); //退室したことを全メンバーに通知 this.Broadcast(room).OnLeave(me.Name); } public async Task SendMessageAsync(string message) { //発言した内容を全メンバーに通知 this.Broadcast(room).OnSendMessage(me.Name, message); } public async Task MovePositionAsync(Vector3 position) { // サーバー上の情報を更新 me.Position = position; //更新したプレイヤーの情報を全メンバーに通知 this.Broadcast(room).OnMovePosition(me); } protected override ValueTask OnDisconnected() { //nop return CompletedTask; } }
クライアント側(Receiver)の実装
using Grpc.Core; using MagicOnion.Client; using UnityEngine; public class SampleController : MonoBehaviour, ISampleHubReceiver { private Channel channel; private ISampleHub sampleHub; private async void Start() { // gRPCのchannelを作成する // Insecure の場合はポート番号80がデフォルト // Secure の場合はポート番号443がデフォルト this.channel = new Channel("localhost:12345", ChannelCredentials.Insecure); // 使用するgRPC channelと、サーバーからの一斉呼び出し(Broadcast)を受けるReceiverを指定し、 // サーバー側で定義したメソッドを呼び出すための Hub を生成する。 this.sampleHub = StreamingHubClient.Connect<ISampleHub, ISampleHubReceiver>(this.channel, this); // 自分のプレイヤー情報を作ってみる var player = new Player { Name = "Sakai", Position = new Vector3(0, 0, 0), Rotation = new Quaternion(0, 0, 0, 0) }; // ゲームに接続する await this.sampleHub.JoinAsync(player); // チャットで発言してみる await this.sampleHub.SendMessageAsync("こんにちは!"); // 位置情報を更新してみる player.Position = new Vector3(1, 0, 0); await this.sampleHub.MovePositionAsync(player.Position); // ゲームから切断してみる await this.sampleHub.LeaveAsync(); } private async void OnDestroy() { await this.sampleHub.DisposeAsync(); await this.channel.ShutdownAsync(); } public void OnJoin(string name) { Debug.Log($"{name}さんが入室しました"); } public void OnLeave(string name) { Debug.Log($"{name}さんが退室しました"); } public void OnSendMessage(string name, string message) { Debug.Log($"{name}: {message}"); } public void OnMovePosition(Player player) { Debug.Log($"{player.Name}さんが移動しました: {{ x: {player.Position.x}, y: {player.Position.y}, z: {player.Position.z} }}"); } }