Unity 版 Client

Unity によるクライアント実装例を紹介します。。

このチュートリアルでは、Unityについての基本的な知識を持つユーザーを前提条件としているため、Unityの説明は割愛します。

Unity 版クライアントの仕様

Unity 版クライアントの大まかな機能は、次のとおりです。

  • ルーム一覧の取得
  • ルームを選択して入室
  • ルームの他の人の発言を見るビューワー(メッセージの購読)
  • ルームに発言
  • ルームから退室

開発環境の説明

MAGELLAN Client SDK for Unityを使用するには、以下の環境が必要となります。

Unity4のエディタ上で実行する場合のみUnity Proライセンスが必要となります。

サンプルアプリのダウンロード

Unityで簡単にお試しいただけるように、サンプルアプリのソースコードを用意しました。サンプルアプリでは、Unity4.6から搭載されているUIシステムを使用しています。

サンプルアプリソースコード一式

サンプルアプリのソースコード一式は、GitHub で公開しています。

MAGELLAN Client SDK for Unity の導入手順

MAGELLAN Client SDK for Unity の導入手順については以下のドキュメントを参照してください。

接続情報の確認

MAGELLAN にアクセスするためには、 consumer_keyconsumer_secret が必要となります。これらの情報は、magellan-cliproject list で確認できます。

$ magellan-cli project list
+---+----+--------------+-----------+----------+-----------------+----------------------------------+
| * | id | organization |   name    | icon_url |  consumer_key   |         consumer_secret          |
+---+----+--------------+-----------+----------+-----------------+----------------------------------+
| * | 3  | MyOrg        | MyProject |          | MyOrg.MyProject | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+---+----+--------------+-----------+----------+-----------------+----------------------------------+

Total: 1

ビルド設定

サンプルアプリでは、Sceneの移動を行うため、Build Settings にSceneを追加します。

Unityメニューの File > Build Settings... を選択すると、ビルド設定のウィンドウが開きます。 ビルドの際に含めるScene の編集リストがポップアップ表示されます。

プロジェクトビューからRooms.unityRoomChat.unityUsers.unity をドラッグしてきて Scene In Build に入れます。

structure

接続情報の設定

MAGELLAN にアクセスするための情報を定義します。

サンプルアプリでは、Assets/Scripts/MagellanSettings.cs に定数として定義しています。

using Magellan;

public class MagellanSettings
{
  // 接続先
  public const string HTTP_SERVER_HOST = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
  // 接続先ポート
  public const int HTTP_SERVER_PORT = XXX;
  // MAGELLANのConsumer Key
  public const string CONSUMER_KEY = "XXXXXXXXX";
  // MAGELLANのConsumer Secret
  public const string CONSUMER_SECRET = "XXXXXXXXXX";
  // MQTT接続先
  public const string MQTT_SERVER_HOST = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
  // MQTT接続先ポート
  public const int MQTT_SERVER_PORT = XXXX;
  // PubSub KeepAlive設定時間
  public const int PUBSUB_KEEP_ALIVE = 30;
  // WorkerのURIスキーム
  public const BaseRequest.SchemeType SCHEME_TYPE = BaseRequest.SchemeType.HTTPS;
  // クライアントバージョン
  public const string CLIENT_VERSION = "0.0.1";
}

MAGELLAN にアクセスするときのサーバー情報(ホスト・ポート)については、「サーバー情報」を参照して設定してください。また、Consumer key、Consumer Secret については、「接続情報の確認」で確認した接続先を設定してください。

ルーム一覧の取得

Worker で実装したルーム一覧取得 API /rooms を呼び出してルーム一覧を取得します。
ルームの一覧を表示するSceneは Assets/Scenes/Rooms.unity となります。
Rooms.unityを開くと、Hierarchyビューにゲームオブジェクトが展開されます。

この中のScriptオブジェクトにアタッチされているRoomList.csを開きます。

Magellan システムを構成する各種クラスは Magellan名前空間に定義されています。usingでMagellanを指定します。

using Magellan;

MagellanSettings.csで定義した接続先のホストポートConsumer KeyConsumer Secretを指定して、MAGELLANのクライアントハンドルを生成します。
クライアントハンドル生成は、Managerクラスの CreateClient メソッドで行われます。 ここでは、Start() でクライアントハンドル生成を行っています。 クライアントハンドルが生成されると、返り値としてクライアントハンドルが得られます。クライアントハンドルは、後の処理で使うので、メンバ変数に格納しておきます。

// クライアントの作成
workerHandle = Manager.Instance.CreateClient(MagellanSettings.HTTP_SERVER_HOST, MagellanSettings.HTTP_SERVER_PORT, MagellanSettings.CONSUMER_KEY, MagellanSettings.CONSUMER_SECRET, false);

Workerへアクセスする場合は初めに、ManagerクラスInitializeWorker メソッドから初期化を実行する必要があります。 引数には、クライアントハンドルとアクセスするWorkerのクライアントバージョンを指定します。

// Workerの初期化
Manager.Instance.InitializeWorker (workerHandle, MagellanSettings.CLIENT_VERSION);

それでは、Workerからルームの一覧を取得します。
SetRoomList メソッドで、Workerからデータを取得し、画面に表示するという一連の処理を行っています。

void SetRoomList ()
{
  // GET /rooms/ (ルームの一覧)へのリクエスト用インスタンスの生成
  Worker worker = new Worker("rooms/", BaseRequest.MethodType.Get, BaseRequest.ProtocolType.General, MagellanSettings.scheme);

  // レスポンスコールバック
  worker.ResponseCallback = (_worker) =>
  {
    // 結果の取得
    string responseData = System.Text.Encoding.UTF8.GetString(_worker.Response);
    if (! String.IsNullOrEmpty(responseData)) {
      IList roomList = (IList)Json.Deserialize (responseData);

      Dictionary<int, string> rooms = new Dictionary<int, string>();
      foreach(IDictionary room in roomList)
      {
        rooms.Add(int.Parse(room["id"].ToString()), (string)room["name"]);
      }
      if (rooms.Count > 0)
        SetContent(rooms);
    }
  }

  // エラーコールバック
  worker.ErrorCallback = (req) =>
  {
    Debug.Log(String.Format("Request Error. Error Code: {0}.", req.ErrorCode.ToString()));
  }

  // Worker リクエスト送信
  Manager.Instance.SendWorkerRequest (workerHandle, worker);

}

SetRoomList メソッドで行っている、Workerへアクセスする際の手順を解説していきます。

Workerへアクセスするには、Workerクラスを使用します。 APIのURLディレクトリ部リクエストメソッドプロトコルスキームを指定して、リクエスト用の Workerインスタンス を作成します。

Worker worker = new Worker("rooms/", BaseRequest.MethodType.Get, BaseRequest.ProtocolType.General, MagellanSettings.scheme);

Workerインスタンスには、レスポンス受信時に実行されるコールバックをデリゲートとして設定出来ます。 サンプルアプリでは、ResponseCallbackが受け取る引数_workerから、結果を取得し、データを画面へ表示するSetContent関数へ渡すというコールバック処理をデリゲートとして設定しています。

worker.ResponseCallback = (_worker) =>
{
  // 結果の取得
  string responseData = System.Text.Encoding.UTF8.GetString(_worker.Response);
  .
  .
  .
}

また、エラーが発生した際のコールバックもデリゲートして設定出来ます。 Workerインスタンスの ErrorCallback が受け取る引数reqから、エラーコードが取得出来ます。

worker.ErrorCallback = (req) =>
{
  Debug.Log(String.Format("Request Error. Error Code: {0}.", req.ErrorCode.ToString()));
}

リクエストの送信は、Manegerクラスを通じて行われます。Managerクラスの SendWorkerRequest メソッドに、引数としてWorkerインスタンスを指定します。

Manager.Instance.SendWorkerRequest (workerHandle, worker);

これで、MAGELLANにアクセスしてルームの一覧が取得できました。

structure

ルーム選択と入室

入室 API /rooms/:room_id/users はルーム ID と入室ユーザの名前を指定して、POST でリクエストを送信します。 入室処理は JoinRoom メソッドで行っています。

void JoinRoom()
{
  // POST: /rooms/:room_id/users (ルームへの入室)へのリクエスト用インスタンスの生成
  Worker worker = new Worker(String.Format("rooms/{0}/users", PlayerPrefs.GetInt("SelectRoomId")), BaseRequest.MethodType.Post, BaseRequest.ProtocolType.General, MagellanSettings.SCHEME_TYPE);

  // ユーザー名をPostデータに指定
  worker.AddPostData ("name", PlayerPrefs.GetString("Username"));

  // レスポンスコールバック
  worker.ResponseCallback = (_worker) =>
  {
    string responseDate = System.Text.Encoding.UTF8.GetString(_worker.Response);
    if (! String.IsNullOrEmpty(responseDate)) {

      Dictionary<string, object> result = Json.Deserialize (responseDate) as Dictionary<string, object>;

      if ((bool)result["result"]) {

        Application.LoadLevel("RoomChat");

      } else {
        Debug.Log(String.Format("Could not Join Room. error: {0}", result["error"].ToString()));
      }
    }
  };

  // エラーコールバック
  worker.ErrorCallback = (req) =>
  {
    Debug.Log(String.Format("Request Error. Error Code: {0}.", req.ErrorCode.ToString()));
  };

  // Worker リクエスト送信
  Manager.Instance.SendWorkerRequest (workerHandle, worker);

}

簡易なバリデーションチェックを行なった後、必要なパラメータ(name)を POST で送信します。 POSTデータは、Workerインスタンスへ付加することが出来ます。

worker.AddPostData ("name", PlayerPrefs.GetString("Username"));

レスポンスをチェックして、reuslttrue の場合は正常に入室できているので、チャット画面(RoomChat.unity)へ遷移します。 resultfalse のときは、入室に失敗しているのでその旨を表示します。

Subscribe の開始

入室に成功すると、チャット画面に遷移できました。

structure

チャット画面のSceneは、Assets/Scenes/RoomChat.unityとなります。 RoomChat.unityを開いてみましょう。 ヒエラルキーに表示される内容は、Rooms.unityと同じ構成となっているため、説明は割愛します。

入室したという情報だけでは、実際にそのルームのメッセージを受け取ることはできません。 入室に成功したら、PubSubへの接続処理とメッセージの購読(Subscribe)を開始するようにします。 ScriptオブジェクトにアタッチされているChat.csを見てみましょう。

PubSubへの接続開始は BeginPubSubConnect メソッドで行っています。

void BeginPubSubConnect()
{
  // PubSub用のクライアントの作成
  pubsubHandle = Manager.Instance.CreateClient(MagellanSettings.MQTT_SERVER_HOST, MagellanSettings.MQTT_SERVER_PORT, MagellanSettings.CONSUMER_KEY, MagellanSettings.CONSUMER_SECRET, false);

  // PubSubを初期化
  Manager.Instance.InitializePubSub(pubsubHandle, MagellanSettings.CLIENT_VERSION);

  // PubSub接続
  Manager.Instance.ConnectPubSub(pubsubHandle, MagellanSettings.PUBSUB_KEEP_ALIVE, OnPubSubConnect, OnPubSubDisconnect);

  topic = String.Format (topic, PlayerPrefs.GetString("RoomName"));
}

BeginPubSubConnect メソッドで行っている、Subscribeを開始する手順を解説していきます。

PubSub用のクライアントハンドル生成は、Managerクラスの CreateClient メソッドで行われます。 引数には、MagellanSettings.csで定義したMQTT_SERVER_HOSTMQTT_SERVER_PORTを指定します。 返り値として得たクライアントハンドルをメンバ変数に格納しておきます。

pubsubHandle = Manager.Instance.CreateClient(MagellanSettings.MQTT_SERVER_HOST, MagellanSettings.MQTT_SERVER_PORT, MagellanSettings.CONSUMER_KEY, MagellanSettings.CONSUMER_SECRET, false);

PubSubを使用する場合は初めに、Managerクラスの InitializePubSub メソッドで初期化を実行する必要があります。 引数には、クライアントハンドル、MagellanSettings.csで定義したCLIENT_VERSIONを指定します。

Manager.Instance.InitializePubSub(pubsubHandle, MagellanSettings.CLIENT_VERSION);

初期化処理を実行し、Managerクラスの ConnectPubSub メソッドでサーバーへ接続します。 引数には、クライアントハンドル、MagellanSettings.csで定義したPUBSUB_KEEP_ALIVEを指定します。サンプルアプリでは、続く引数に接続時、切断時に実行されるコールバックを指定しています。

Manager.Instance.ConnectPubSub(pubsubHandle, MagellanSettings.PUBSUB_KEEP_ALIVE, OnPubSubConnect, OnPubSubDisconnect);

ConnectPubSub で指定されているコールバックを見てみましょう。

PubSub接続時のコールバック OnPubSubConnect では、クライアントハンドルと接続ステータスを引数に取ります。 接続が成功した場合、Subscriberを登録する RegisterSubscriber メソッドをコールします。

// PubSub接続コールバック
void OnPubSubConnect(ClientHandle clientHandle, Manager.PubSubConnectStatus status)
{
  if (Manager.PubSubConnectStatus.Success == status) {
    RegisterSubscriber();
  }
}

PubSub切断時のコールバックOnPubSubDisconnect も、接続時のコールバックと同様に、クライアントハンドルと切断ステータスを引数に取ります。

// PubSub切断コールバック
void OnPubSubDisconnect(ClientHandle clientHandle, Manager.PubSubDisconnectStatus status)
{
  if (Manager.PubSubDisconnectStatus.Expected == status) {
    if (transition.ContainsKey(pushObject.name)){
      Application.LoadLevel (transition[pushObject.name]);
    }
  }
}

次に、メッセージの購読(Subscribe)を開始します。 Subscriberを登録している RegisterSubscriber メソッドを見てみましょう。

// Subscriber登録要求
void RegisterSubscriber()
{
  // インスタンス生成
  PubSub_Subscriber sub = new PubSub_Subscriber(topic, PubSubQos.FireAndForget);

  // メッセージ受信時のコールバック
  sub.SubscribeCallback = (_sub, _topic, _message) =>
  {
    SetContent(System.Text.Encoding.UTF8.GetString(_message));
  };

  // 登録解除完了時に実行
  sub.UnRegisterCallback = (_sub) =>
  {
    // PubSub切断
    Manager.Instance.DisconnectPubSub(pubsubHandle);
  };

  // 登録完了時に実行
  sub.RegisterCallback = (_sub) =>
  {
    Debug.Log("Register Success Subscribe");
  };

  // 登録
  subscribeHandle = Manager.Instance.RegisterPubSub_Subscriber(pubsubHandle, sub);
}

メッセージをSubscribeするには、PubSub_Subscriberクラス を使用します。 Subscribeする TopicQoS を指定して PubSub_Subscriberインスタンス を生成します。

PubSub_Subscriber sub = new PubSub_Subscriber(topic, PubSubQos.FireAndForget);

PubSub_Subscriberインスタンスには、以下のコールバックをデリゲートとして設定出来ます。

  • RegisterCallback() Subscriberの登録完了時のコールバック
  • SubscribeCallback() メッセージ受信時のコールバック
  • UnRegisterCallback() Subscriber登録解除時のコールバック

サンプルアプリでは、SubscribeCallbackで、引数に取ってきた_messageを、SetContent メソッドに渡し、メッセージを画面に表示します。

sub.SubscribeCallback = (_sub, _topic, _message) =>
{
  SetContent(System.Text.Encoding.UTF8.GetString(_message));
}

PubSub_Subscriberの登録は、Manegerクラスを通じて行われます。 RegisterPubSub_Subscriber メソッドに、生成したPubSub_Subscriberインスタンスを渡すことで、Subscriberが登録されます。

subscribeHandle = Manager.Instance.RegisterPubSub_Subscriber(pubsubHandle, sub);

これで、Subscriberの登録が行われ、メッセージが受信出来るようになりました。

ルームに発言する

Subscribeが開始されたので、次はルームに発言(publish)してみましょう。 ルームへの発言はChat.cs内の PublishMessage で行っています。

// Publishリクエスト
void PublishMessage()
{
  var data = BuildMessageData();

  if (String.IsNullOrEmpty(data)) {
    return;
  }

  //インスタンス生成
  PubSub_Publish req = new PubSub_Publish(topic, PubSubQos.FireAndForget, System.Text.Encoding.UTF8.GetBytes(data));

  //リクエスト送信
  Manager.Instance.SendPubSub_PublishRequest(pubsubHandle, req);
}

string BuildMessageData()
{
  InputField inputField = inputMessage.GetComponent<InputField> ();
  string Message = String.Empty;

  if (! String.IsNullOrEmpty (inputField.text)) {
    Dictionary<string, string> dict = new Dictionary<string, string>();
    dict["user"] = PlayerPrefs.GetString("Username");
    dict["message"] = inputField.text;
    dict["date"] = DateTime.Now.ToString("MM/dd HH:mm");

    Message = Json.Serialize(dict);
  }

  inputField.text = String.Empty;
  return Message;
}

BuildMessageData から、画面のテキストフォームに入力されたメッセージを取得します。 サンプルアプリでは、ユーザ名、発言時刻も表示するため、JSONデータに変換しています。

メッセージをPublishするには、PubSub_Publishクラス を使用します。 Publish 対象の TopicQoSPublish するメッセージを指定して PubSub_Publishインスタンス を生成します。

PubSub_Publish req = new PubSub_Publish(topic, PubSubQos.FireAndForget, System.Text.Encoding.UTF8.GetBytes(data));

Publishの送信は、Manager クラスを通じて行われます。 SendPubSub_PublishRequest メソッドに、PubSub_Publishインスタンスを指定し、リクエストを送信します。

Manager.Instance.SendPubSub_PublishRequest(req);

テキストフォームにメッセージを入力して、送信してみましょう。 structure

ルームから退出する

次にルームから退出する処理を見てみましょう。 ルームの退出処理は、Chat.cs内の LeaveRoom メソッドで行っています。

// ルーム退出
void LeaveRoom()
{
  // クライアントの作成
  ClientHandle workerHandle = Manager.Instance.CreateClient(MagellanSettings.HTTP_SERVER_HOST, MagellanSettings.HTTP_SERVER_PORT, MagellanSettings.CONSUMER_KEY, MagellanSettings.CONSUMER_SECRET, false);

  // Workerの初期化
  Manager.Instance.InitializeWorker (workerHandle, MagellanSettings.CLIENT_VERSION);

  // Worker リクエスト用のインスタンスの生成
  Worker worker = new Worker(String.Format("rooms/{0}/users/{1}", PlayerPrefs.GetInt("SelectRoomId"), PlayerPrefs.GetString("Username")),
      BaseRequest.MethodType.Delete,
      BaseRequest.ProtocolType.General,
      MagellanSettings.SCHEME_TYPE);

  // POSTデータのセット
  worker.AddPostData ("name", PlayerPrefs.GetString("Username"));

  // レスポンスコールバック
  worker.ResponseCallback = (_worker) =>
  {
    string responseData = System.Text.Encoding.UTF8.GetString(_worker.Response);
    if (! String.IsNullOrEmpty(responseData)){
      Dictionary<string, object> result = Json.Deserialize (responseData) as Dictionary<string, object>;

      if ((bool)result["result"]) {
          // Subscriberの登録解除
          Manager.Instance.UnregisterPubSub_Subscriber(pubsubHandle, sub);

      } else {
        Debug.Log(String.Format("Could not Exit. error: {0}", result["error"].ToString()));
      }

    }
  };

  // エラーコールバック
  worker.ErrorCallback = (req) =>
  {
    Debug.Log(String.Format("Request Error. Error Code: {0}.", req.ErrorCode.ToString()));
  };

  // Worker リクエスト送信
  Manager.Instance.SendWorkerRequest (workerHandle, worker);

}

退出処理では、必要なパラメータ(name)をDELETEで送信します。 レスポンスをチェックし、resultがtrueの場合は、PubSubの切断処理を行い、ルーム一覧(Rooms.unity)へ遷移します。

PubSubの切断処理は以下のように行っています。

まずSubscriberの登録を解除を行います。 Subscriberの登録解除は、Managerクラスの UnregisterPubSub_Subscriber メソッドを使用します。 引数に、PubSub接続時に指定したクライアントハンドルとPubSub_Subscriberインスタンスを渡します。

Manager.Instance.UnregisterPubSub_Subscriber(pubsubHandle, sub);

PubSubの切断は、Subscriberの登録時に指定したコールバックUnRegisterCallback内で行っています。 ManagerクラスのDisconnectPubSubメソッドで、サーバーとの接続を切断します。 接続時に指定したクライアントハンドルを引数として渡します。

// Subscriber 登録解除完了時に実行
sub.UnRegisterCallback = (_sub) =>
{
  // PubSub切断
  Manager.Instance.DisconnectPubSub(pubsubHandle);
};

PubSubの切断が終了すると、PubSub切断後のコールバックでルームの一覧に遷移します。

これで、メッセージの購読の解除とルームから退室出来るようになりました。

以上で、Unity 版クライアントの完成です。