本エントリーでは、Union Platform alpha3を使ってチャットを作ってみます。UnionのバージョンはUnion alpha3を使用します。 主に、以下の事項を確認していきます。
- observeとjoinの違いを確認
- Client Attributeの使い方を確認
- Messageの使い方を確認
- 各種基本的なイベント処理を確認
実際に動作するサンプルはwonderflに投稿してあります。
機能
このswfファイルを閲覧する人をユーザと呼びます。ユーザは以下のようなことが出来ます。
- ユーザは、observeボタンをクリックすることでRoom ID: com.asmple.union.chatへobserveすることが出来ます。
- ユーザは、joinボタンをクリックすることでRoom ID: com.asmple.union.chatへjoinすることが出来ます。
- ユーザは、色コードを入力し、Update Attributeボタンをクリックすることで、Client Attributeを通じてアバター・発言の色を変更することが出来ます。
- ユーザは、ニックネームを入力し、Update Attributeボタンをクリックスすることで、Client Attributeを通じて発言時のニックネームを変更することが出来ます。
- ユーザは、Roomにjoinした状態であれば以下の操作が可能です。
- ユーザは、メッセージ入力欄にメッセージを入力し、エンターキーを押すことで、Messageを通じて発言することが出来ます。
- ユーザは、アバターフィールド内をクリックすることで自身のアバターの位置をClient Attributeを通じて変更することが出来ます。
observeとjoin
observeとjoinの概念的な説明は、UnionPlatform (2) 概念にて解説しております。
observeとjoinの違いを体験
今回は実際にobserveとjoinの違いを体験できます。
以下のswfは上のものとまったく同じものです。先ほどのswfと下記swf両方を再生し、先ほどのswfでjoinボタンをクリックしてから画面内のアバターを移動してみましょう。
このとき、下記のswfでは何も見えないはずです。まだRoomにobserveもjoinもしていないからです。
次に、下記swfでobserveボタンをクリックすると、画面内にアバターが表示されます。(同時に本エントリーを閲覧している人がいたら数人のアバターが表示されるかもしれません。)ただ、画面内には自分のアバターも表示されませんが、発言は出来ることが分かる思います。
Messageの送信について
なお、Union alpha3ではobserve, joinせずにまたは、observe時もMessageを送信出来ます。RoomにjoinしていないClientからのMessage送信を抑止する為には、自身でチェックコードを書く必要があります。
リリース時には権限管理の強化で挙動は変更される可能性があるそうです。(@keno42さんがcolinさんに確認してくれました。この場を借りて感謝致します。)
というわけで、observeとjoinの違いを感じていただけましたでしょうか。observeとjoinの切り替えにはUnion alpha3の時点では不都合と思われる挙動があるため、本エントリーではその点も確認していきます。
Client AttributeとMessage
Client AttributeとMessageは情報の共有化に使います。情報の共有化については、Union Platform(3) 情報の共有化にて解説しております。
Client Attributeは、今回の例ではニックネームや色となります。後から入室したユーザは最新の情報を共有することがわかります。
Messageでの共有は、今回の例では発言メッセージとなります。後から入室したユーザにはそれ以降の情報しか共有されないことが分かります。
Client Attribute
各クライアントが持つ属性を定義を紹介します。
スコープ | 用途 | key | valueの例 |
---|---|---|---|
グローバル | クライアントの表示名 | nickname | naoto5959 |
グローバル | 色 | color | 0x000000 |
グローバル | アバター情報 | avatar | 100,200,0,100 |
アバターの情報は下記の情報を「,(カンマ)」区切りで格納します。先の例ではx座標:100, y座標:200, 向き:up, 距離:100を表します。
- 目標点のX座標
- 目標点のY座標
- アバターが向く方向(0:up, 1:right, 2:down, 3:left)
- アバターと目標座標との距離
今回はすべてのClient Attributeのスコープをグローバルにしましたが、もしRoomを増やすことになればRoomごとにアバターの情報は異なるでしょうから、アバター情報のスコープをRoomごとに持つように変更しましょう。
ユーザーインターフェース
ユーザーインターフェースのUnionChatUIクラス定義は、L:675以降にあります。 UnionChatUIクラスは以下の図内に示した各パーツに対応したpublicプロパティを持っています。
[caption id="attachment_399" align="aligncenter" width="540" caption="チャットサンプルのUI"][/caption]
プロパティ名 | 役割 |
---|---|
loadingCircle | Union Serverとの接続待ちの際に表示します。後述するメソッドで制御するため直接操作する必要はありません。 |
observeButton | Union Serverの特定のRoomへobserveする際に使用します。observe処理開始時の処理をこのボタンのハンドラへ記述します。 |
joinButton | Union Serverの特定のRoomへjoinする際に使用します。join処理開始時の処理をこのボタンのハンドラへ記述します。 |
currentStatusLabel | Union Serverとの接続状態を表示します。RoomEvent.JOINなど各イベントのハンドラ内でこのラベルの値を書き換えます。 |
currentClientsLabel | Union Serverの特定のRoomにjoinしているClient数を表示します。RoomEvent.CLIENT_COUNTイベントのハンドラ内でこのラベルの値を書き換えます。 |
messageField | ユーザ文字を入力し、エンターキーを押した際に、RoomインスタンスのsendMessage()メソッドを通じてMessageを送信する際に使用します。 |
messageList | Reactorの各種ログの表示と、RoomインスタンスのsendMessages()メソッドを通じて送信されたMessagesを表示する際に使用します。 |
colorChooser | 自身のアバター・発言色を表すClient Attributeのcolorを変更する際に使用します。 |
nicknameField | 自身のニックネーム色を表すClient Attributeのnicknameを変更する際に使用します。 |
updateButton | colorChooser, nicknameFieldで設定した各Client Attributeの値を更新する際に使用します。Client Attributeの更新処理をこのボタンのハンドラへ記述します。 |
avatarField | 自身のアバターの位置を表すClient Attributeのavatarを変更する際に使用します。このオブジェクトをクリックした際 |
メソッドは以下のものを用意しました。
メソッド名 | 引数 | 役割 |
---|---|---|
playLoading | なし | ローディングサークルを再生します。 |
stopLoading | なし | ローディングサークルを停止します。 |
appendMessage | 表示するメッセージ, メッセージ色 | messageListにメッセージを追加します。 |
アバター
アバターは、以下のswfを使用しています。
以下のプロパティとメソッドを備えます。
プロパティ名 | 役割 |
---|---|
color | アバターの色 |
メソッド名 | 引数 | 役割 |
---|---|---|
play | なし | アバターのアニメーションを再生する |
stop | なし | アバターのアニメーションを停止する |
front | なし | アバターを正面向きにする |
back | なし | アバターの背面向きにする |
left | なし | アバターの左向きにする |
right | なし | アバターの右向きにする |
サンプルコードの解説
Union Serverへの接続
コンストラクタから呼ぶconnectメソッドをみてみましょう。
/** * UnionServerへ接続します。 */ private function connect():void { _reactor = new Reactor(null, false); _reactor.addEventListener(ReactorEvent.READY, _reactor_readyHandler); _reactor.addEventListener(ReactorEvent.CLOSE, _reactor_closeHandler); _reactor.connect(UNION_SERVER_HOST, UNION_SERVER_PORT); }
Reactorをnewする際の引数は、1つ目が設定ファイルのxmlの場所。2つ目がデバッグ用のログの出力設定フラグとなります。2つ目の引数をtrueとしておくと、Flash Debug Playerで再生した際に、ログを確認することが出来ます。
設定ファイルの例
今回は使用しませんが以下のようなxmlを用意してあげることで自動的にconnectすることが可能です。
tryunion.com 9100 DEBUG
Union Serverへの接続完了
Union Serverへ接続が完了したことを知るには、ReactorEvent.READYイベントの送出を待ちます。先ほど指定したハンドラの中身をみてみましょう。
private function _reactor_readyHandler(event:ReactorEvent):void { _ui.currentStatusLabel.text = "connect"; _ui.appendMessage("ReadyEvent.READY"); // 自身のClient参照を取得 _self = _reactor.getClientManager().self(); _self.setAttribute("nickname", _clientData.nickname); _self.setAttribute("color", _clientData.color); _self.setAttribute("avatar", _clientData.avatar); // 自身のnicknameを設定 _ui.nicknameField.text = _clientData.nickname; // 自身のcolorを設定 _ui.colorChooser.value = _clientData.color; // RoomManager参照を取得 _roomManager = _reactor.getRoomManager(); _roomManager.addEventListener( RoomManagerEvent.CREATE_ROOM_RESULTS, _roomManager_createRoomReslutsHandler); // Roomへの接続が0となっても残す設定 var roomSetting:RoomSettings = new RoomSettings(); roomSetting.dieOnEmpty = false; // 指定したRoom IDを作成する。 _room = _roomManager.createRoom(ROOM_CHAT, roomSetting); addRoomEventListeners(); // logger参照を取得 _logger = _reactor.getLog(); _logger.addEventListener(LogEvent.UPDATE, _logger_updateHandler); }
Client Attributeの初期化
ReactorEvent.READYイベントが送出された後に、L:491にて自身のClientオブジェクトを取得しています。グロバールスコープのClient Attributeはこのタイミングで代入しておくと良いでしょう。
Room作成チェック
L:503にて、RoomManagerインスタンスを取得し、Room作成時のイベントの送出を待ちます。L:514でRoomインスタンスを生成していますが、このRoomインスタンス生成時にUnion Server側でもRoom生成が正常に行われたかをチェックする為に使います。
RoomSettingでRoomの自動削除を防ぐ
デフォルトではRoomに接続しているClientがいなくなった時点でRoomは破棄されてしまいます。これを防ぐために、L:510でRoomSettingインスタンスを生成し、dieOnEmptyプロパティにfalseを代入します。このRoomSettingインスタンスをRoomManagerインスタンスのcreateRoomメソッドの第二引数に指定するこたおでRoom内にClientが居ない状態でもRoomを残しておくことが可能です。
RoomEventのリスナー登録
L:520でaddRoomEventListeners()メソッドを実行することで、RoomEventの各イベントを監視します。このイベント監視によって、ReactorとUnion Serverとの情報共有を行います。詳しくは、後ほどみます。
Room作成完了
Roomの作成完了を知るには、RoomManagerEvent.CREATE_ROOM_RESULTSイベントの送出を待ちます。先ほど指定したハンドラをみていきましょう。
private function _roomManager_createRoomReslutsHandler( event:RoomManagerEvent):void { var status:String = event.getStatus(); _ui.appendMessage("RoomManagerEvent.CREATE_ROOM_RESULTS:" + status); if (event.getStatus() == Status.SUCCESS || event.getStatus() == Status.ROOM_EXISTS) { if (_attempt == "observe") _room.observe(); else if (_attempt == "join") _room.join(); else activate(); } }
作成の結果はevent.getStaus()の値を確認します。下記のいずれかの値をとります。
- Status.SUCCESS
- Status.ROOM_EXIST
- Status.ERROR
作成に成功した場合は、Status.SUCCESSとなります。今回の場合は、Roomの自動削除を無効としているので、Status.ROOM_EXISTとなる状況が多いと思います。いずれもroomへの接続が可能な状態を示します。
初回は、_attemptの値は空文字なので
/** * join or observe */ private var _attempt:String = "";
次に実行されるのは、activate()メソッドです。attemptの値がobseverの場合は、room.observe()メソッドの実行、attemptの値がjoinの場合は、room.join()メソッドの実行となることを心に留めておきましょう。
なぜこのような作りとしているかは後ほど説明します。
activate()メソッドでユーザーインターフェースの活性化
activate()メソッドを実行する段階では、Union Serverとの接続が確立されてユーザのイベントを待つ状態となります。
private function activate():void { _ui.stopLoading(); // observeボタンの動作を登録します。 _ui.observeButton.addEventListener(MouseEvent.CLICK, _ui_observeButton_clickHandler); // joinボタンの動作を登録します。 _ui.joinButton.addEventListener(MouseEvent.CLICK, _ui_joinButton_clickHandler); // updateボタンの動作を登録します。 _ui.updateButton.addEventListener(MouseEvent.CLICK, _ui_updateButton_clickHandler); // メッセージ入力時の動作を登録します。 _ui.messageField.addEventListener(TextEvent.TEXT_INPUT, _ui_messageField_textInputHandler); _ui.messageField.addEventListener(KeyboardEvent.KEY_UP, _ui_messageField_keyUpHandler); // アバターフィールドクリック時の動作を登録します。 _ui.avatarField.addEventListener(MouseEvent.CLICK, _ui_avatarField_clickHandler); }
先ほど確認したユーザーインターフェースの各プロパティにマウスイベントやテキスト入力時のイベントを設定しています。
observeボタンの動作
L:384で、ReadyEvent.READYイベントのハンドラで登録した、RoomEventのリスナーを解除します。L:381で_attemptにobserveを代入してから、connectメソッドを実行します。
private function _ui_observeButton_clickHandler(event:MouseEvent):void { _ui.playLoading(); removeRoomEventListeners(); deactivate(); _attempt = "observe"; _reactor.disconnect(); connect(); }
これは、先ほど見たとおり、roomManager_createRoomReslutsHandler内で、下記のようにroom.observe()を実行することになります。
if (_attempt == "observe") _room.observe(); else if (_attempt == "join") _room.join(); else activate();
joinボタンの動作
observeとの違いは、_attemptにjoinを代入する点です。このような処理は、本来ならば1つの関数にまとめたほうが良いかも知れません。
private function _ui_joinButton_clickHandler(event:MouseEvent):void { _ui.playLoading(); removeRoomEventListeners(); deactivate(); _attempt = "join"; _reactor.disconnect(); connect(); }
何度も見てきたように、roomManager_createRoomReslutsHandler内で、下記のようにroom.join()を実行することになります。
if (_attempt == "observe") _room.observe(); else if (_attempt == "join") _room.join(); else activate();
つまり、observe、joinするたびにReactorとUnion Serverの接続を繋ぎなおしています。
Union Serverの接続を繋ぎなおす理由
Union alpha3では、一度observeしたRoomに対してjoinしたり、いったんstopObservingしてからjoin。もしくはその逆を行うと、RoomEventが正常にdispatchされないなど動作が不安定となるようです。
そのため、observeとjoinの切り替え時にコネクションを張りなおす処理を行っています。
updateボタンの動作
ClientのAttributeを更新する処理です。L:413, L:414で自身のClientインスタンスのsetAttribute()メソッドを呼び出します。
private function _ui_updateButton_clickHandler(event:MouseEvent):void { var nickname:String = _ui.nicknameField.text; var color:uint = _ui.colorChooser.value; if (nickname.length == 0) { _ui.appendMessage("ニックネームは空に出来ません", 0xFF0000); return; } _self.setAttribute("nickname", encodeURI(nickname)); _self.setAttribute("color", String(color)); }
メッセージ入力時の動作
TextFieldで入力した文字をエンターキーで抽出します。KeyboardEvent.KEY_UPのハンドラ内で、Keyboard.ENTER判定をするだけだと、日本語入力の変換時のエンターキー入力を拾ってしまい、入力途中で文章が抽出されてしまいます。 そこで、文字入力中かどうかを判定するためにTextEvent.TEXT_INPUTのハンドラを活用します。
private function _ui_messageField_textInputHandler(event:TextEvent):void { _isInputText = true; }
L:441でRoomインスタンスのsetMessage()メソッドを使ってchatMessageというMessageを送信しています。
private function _ui_messageField_keyUpHandler(event:KeyboardEvent):void { if (_ui.messageField.text.length == 0) return; if (!_isInputText && event.keyCode == Keyboard.ENTER) { var message:String = encodeURI(_ui.messageField.text); _room.sendMessage("chatMessage", true, null, message); _ui.messageField.text = ""; } _isInputText = false; }
アバターフィールドクリック時の動作
クリックされた場所とアバターの位置関係と距離計算して、アバターが向くべき方向を決定します。L:475で自身のClientインスタンスのsetAttribute()メソッドを呼び出します。これらの値は同時にAttributeの更新イベントを通知して欲しいので1つのAttributeにカンマ区切りで代入しています。
private function _ui_avatarField_clickHandler(event:MouseEvent):void { // 入室していなければ何もしない。 if (!_self.isInRoom(ROOM_CHAT)) return; var avatar:* = _avatars[_self.getClientID()]; var direction:uint; var distanceX:Number = event.localX - avatar.x; var distanceY:Number = event.localY - avatar.y; var distance:Number = Math.sqrt( distanceX * distanceX + distanceY * distanceY ); // 小数点を切り捨てる distance = int(distance); // アバターが向く方向を決定 if (Math.abs(distanceX) <= Math.abs(distanceY)) { if (distanceY < 0) direction = DIRECTION_UP; else direction = DIRECTION_DOWN; } else { if (distanceX < 0) direction = DIRECTION_LEFT; else direction = DIRECTION_RIGHT; } // Client Attributeを更新する。 var values:Array = [event.localX-16, event.localY-16, direction, distance]; _self.setAttribute("avatar", values.join(",")); }
addRoomEventListeners
joinボタン、observeボタンをクリックした後、ReactorEvent.READYイベントが送出された段階で呼び出される_reactor_readyHandler内で実行しているメソッドです。
RoomEventの各イベントをリスナー登録します。ReactorとUnion Serverとをつなぐ要とも言える処理です。
private function addRoomEventListeners():void { _room.addEventListener(RoomEvent.OBSERVE, _room_observeHandler); _room.addEventListener(RoomEvent.JOIN, _room_joinHandler); _room.addEventListener(RoomEvent.CLIENT_COUNT , _room_clientCountHandler); _room.addEventListener(RoomEvent.SYNCHRONIZE , _room_synchronizeHandler); _room.addEventListener(RoomEvent.ADD_CLIENT , _room_addClientHandler); _room.addEventListener(RoomEvent.REMOVE_CLIENT , _room_removeClientHandler); _room.addEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE , _room_updateClientAttributeHandler); _room.addMessageListener("chatMessage", chatMessageHandler); }
各イベントと登録されたハンドラ内の処理をみていきます。
RoomEvent.OBSERVEとそのハンドラ
RoomEvent.OBSERVEはRoomインスタンスのobserveが完了すると、Roomインスタンスから送出されるイベントです。
activate()メソッドでユーザーインターフェースの活性化を行います。詳細は前述の通りです。
private function _room_observeHandler(event:RoomEvent):void { _ui.currentStatusLabel.text = "observe"; _ui.appendMessage("RoomEvent.OBSERVER"); _ui.stopLoading(); activate(); }
RoomEvent.JOINとそのハンドラ
RoomEvent.JOINはRoomインスタンスのjoinが完了すると、Roomインスタンスから送出されるイベントです。
activate()メソッドでユーザーインターフェースの活性化を行います。詳細は前述の通りです。
private function _room_joinHandler(event:RoomEvent):void { _ui.currentStatusLabel.text = "join"; _ui.appendMessage("RoomEvent.JOIN"); _ui.stopLoading(); activate(); }
RoomEvent.CLIENT_COUNTとそのハンドラ
RoomEvent.CLIENT_COUNTはRoomにjoinしているClient数が変化すると、Roomインスタンスから送出されるイベントです。
RoomEventインスタンスのgetNumClients()メソッドで人数を取得します。
private function _room_clientCountHandler(event:RoomEvent):void { _ui.appendMessage("RoomEvent.CLIENT_COUNT"); _ui.currentClientsLabel.text = "occupants:" + event.getNumClients() + "人"; }
RoomEvent.SYNCHRONIZEとそのハンドラ
RoomEvent.SYNCHRONIZEはRoomインスタンスがUnion Serverとの同期を完了すると、Roomインスタンスから送出されるイベントです。
新しくjoin, observeしたClientが現状のRoom内のClientの情報などを取得する処理を記述します。今回は、すでにRoomにjoinしているClientのアバターを描画します。
RoomインスタンスのgetClients()メソッドを通して、Room内のClientのオブジェクトを取得する点がポイントとなります。
addAvatar()メソッドは後述します。
private function _room_synchronizeHandler(event:RoomEvent):void { _ui.appendMessage("RoomEvent.SYNCHRONIZE"); for each (var client:IClient in _room.getClients()) { addAvatar(client); } }
RoomEvent.ADD_CLIENTとそのハンドラ
RoomEvent.ADD_CLIENTはRoomインスタンスの新しいClientが追加されると、Roomインスタンスから送出されるイベントです。
現ClientがRoomにjoinまたはobserveした後にRoomにjoinしてきたClientの情報を取得します。
private function _room_addClientHandler(event:RoomEvent):void { _ui.appendMessage("RoomEvent.ADD_CLIENT"); addAvatar(event.getClient()); }
addAvatar()メソッド
Loaderインスタンスを使って、外部のswfをロードします。ロード後の処理も一括で記述したかったので、無名関数をリスナー登録しています。
アバターのcolorメソッドにClient Attributeのcolorを代入したり、Client AttributeのavatarをAvatarに設定しています。updateAvatar()メソッドは後述します。
private function addAvatar(client:IClient):void { var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.INIT, function(event:Event):void { var avatar:* = loader.content; avatar.color = client.getAttribute(null, "color"); var values:Array = client.getAttribute(null, "avatar").split(","); updateAvatar.apply(null, [avatar, false].concat(values)); _avatars[client.getClientID()] = avatar; _ui.avatarField.addChild(avatar); } ); loader.load(new URLRequest(AVATAR_URL)); }
RoomEvent.REMOVE_CLIENTとそのハンドラ
RoomEvent.REMOVE_CLIENTはRoomインスタンスからClientが削除されると、Roomインスタンスから送出されるイベントです。
現ClientがRoomにjoinまたはobserveした後にRoomからleaveしたClientの情報を取得します。
private function _room_removeClientHandler(event:RoomEvent):void { _ui.appendMessage("RoomEvent.REMOVE_CLIENT"); removeAvatar(event.getClient()); }
removeAvatar()メソッド
アバターフィールドから、アバターを削除します。
private function removeAvatar(client:IClient):void { var avatar:* = _avatars[client.getClientID()]; if (!_ui.avatarField.contains(avatar)) return; _ui.avatarField.removeChild(avatar); delete _avatars[client.getClientID()]; }
RoomEvent.UPDATE_CLIENT_ATTRIBUTEとそのハンドラ
RoomEvent.UPDATE_CLIENT_ATTRIBUTEはRoom内のClientが自身のClient Attributeを更新すると、Roomインスタンスから送出されるイベントです。
Client Attributeは内部で設定される属性も存在するため、更新されたClient Attributeが意図したClient Attributeであるかを確認します。今回は、colorとavatarの更新を検知するようにしています。
実際にアプリケーションを実行すると、一定感覚でメッセージリストにRoomEvent.UPDATE_CLIENT_ATTRIBUTEが表示されますが、これは内部で更新されるClient Attributeです。_PINGという名前なので、おそらくClientの疎通確認用のClient Attributeだと思います。
private function _room_updateClientAttributeHandler(event:RoomEvent):void { _ui.appendMessage("RoomEvent.UPDATE_CLIENT_ATTRIBUTE"); var attribute:Attribute = event.getChangedAttr(); _ui.appendMessage(" id:" + event.getClientID() + ", name:" + attribute.name + ", oldValue:" + attribute.oldValue + ", value:" + attribute.value ); var avatar:* = _avatars[event.getClientID()]; if (!avatar) return; switch (attribute.name) { case "color": { avatar.color = uint(attribute.value); break; } case "avatar": { var values:Array = attribute.value.split(","); updateAvatar.apply(null, [avatar, true].concat(values)); break; } } }
updateAvatar()メソッド
Client Attribute avatarの更新を実際のアバターに適用します。アバターの向きを指定して、目的地へアバターを移動します。移動する際のモーションは、BetweenAS3を使っています。BetweenAS3は大変直感的に使える素敵ライブラリです。
private function updateAvatar( avatar:*, isTween:Boolean, positionX:Number, positionY:Number, direction:uint, distance:Number):void { avatar.play(); switch (direction) { case DIRECTION_UP: avatar.back(); break; case DIRECTION_RIGHT: avatar.right(); break; case DIRECTION_DOWN: avatar.front(); break; case DIRECTION_LEFT: avatar.left(); break; } if (isTween) { var tween:IObjectTween = BetweenAS3.tween( avatar, {x: positionX, y: positionY }, {x: avatar.x, y: avatar.y }, distance * 0.005, Sine.easeOut ); tween.play(); } else { avatar.x = positionX; avatar.y = positionY; } }
chatMessageのMessageイベント
chatMessageというIDのMessageを受信した際のハンドラを指定します。受け取ったMessageをメッセージリストに追加しています。
private function chatMessageHandler( fromClient:IClient, message:String):void { var nickname:String = decodeURI( fromClient.getAttribute(null, "nickname") ); var color:uint = uint(fromClient.getAttribute(null, "color")); message = decodeURI(message); _ui.appendMessage( "[chatMessage]" + nickname + "(" + fromClient.getClientID() + ")" + "さん「" + message + "」" + fromClient, color ); }
まとめ
さて、大変長々と説明してまいりました。お付き合い頂きありがとうございます。今回は具体的なサンプルコードでUnionを使ったアプリケーションの構築方法を紹介してきました。いかがでしたでしょうか。
Unionを使ったアプリケーションの作り方は、もちろん他にもあると思います。1つの例として参考にしていただければと思います。ご意見やご感想は、お気軽にコメントやtwitterでのつぶやきを頂ければと思います。
次回は、Union Serverをローカルにインストールしてみようと思います。