home > 2002年03月 アーカイブ

記事全ての最大/最小化切り替え

2002/03/18

* サンプル1(簡単な作例)

Flashによるマルチユーザーコンテンツ このチュートリアルでは、実際にActionScriptを使った作例を通してマルチユーザコンテンツを説明します。マルチユーザといっても難しく考える必要はありません。ユーザ1人1人が皆に、自分のことを教えてあげればよいのです。重要なことは、「皆に教えること」「皆から教えてもらうこと」なのです。それだけできれば、マルチユーザコンテンツができあがるのです。それを潤滑に行うためにplashサーバープログラムがあります。では、実際にfacesサーバープログラムを使用したマルチユーザコンテンツの作例を見てみましょう。 説明の中で、flash特有のもの、faces特有のもの、それぞれのcontents特有のものがあります。文中では、(fla)、(fcs)、(con)と表記します。 (また、ActionScriptで定義されているものをメソッド、プロパティと呼び、今回functionを使用しこちらで作成しているものを関数、変数と呼びます。) サンプルコンテンツ サンプルファイル 「みんなで協力して隠れた画像を見よう」(peepTheDeep) flaファイル内で機能ごとに4シーン作成しています。 バージョンチェック ローディング ロビー メインコンテンツ バージョンチェック 最初のシーンは、バージョンチェックです。 flashでマルチユーザコンテンツを作成する場合、XML言語を扱います。XML未対応のバーション4以下のflashプレイヤーでは、マルチユーザコンテンツは見れないわけです。そこで、一番最初に_currentframeプロパティ(fla)(flash4対応)や、getVersion()メソッド(fla)(flash5対応)などを使ってバージョンチェックを行います。 クライアント側がflash5プレイヤーであれば、無事ローディングへと進みます。 1: version = getVersion(); 2: version = version.split(" "); 3: userAgent = version[0]; 4: version = version[1].split(); 5: majorVer = version[0]; 6: minorVer = version[2]; 7: if (majorVer>=5) { 8: this.gotoAndPlay(_currentframe+2); 9: } ローディング 次に、ローディングです。ネットワークを通して他ユーザとやり取りするXMLsocketオブジェクトを認識させるための準備をします。 新しくXMLsocket(fla)を生成し、サーバーに接続します。 Connect()メソッド(fla)により、指定したサーバーへ指定したポート番号を使って接続します。 1: function loadEnd () { 2: mySocket = new XMLSocket(); 3: mySocket.onConnect = checkConnect; 4: mySocket.connect(serverAddr, serverPort); 5: mySocket.onClose = checkClose; 6: } onXML()メソッド(fla)により、XMLsocketオブジェクトからデータを受信した時に呼び出されるコールバックです。ここでは、次のロビーで定義するgetData関数(fcs)を指定しておきます。 1: function checkConnect (check) { 2: if (check) { 3: trace ("trueconnection"); 4: mySocket.onXML = getData; 5: queryNumber(appNam,roomNum); 6: } else { 7: trace ("falseconnection"); 8: } 9: } やり取りするXMLの定義を行います。 postXML()関数(fcs)は、文字列を受け取り、XMLとして読み込み、mySocket(fcs)へ送信します。各sendData用関数(fcs)内で使用します。 1: function sendStr () { 2: theXML = new XML(); 3: theXML.parseXML(str); 4: mySocket.send(theXML); 5: } queryNumber()関数(fcs)は、アプリケーション名と、ルームナンバーを送信します。 1: function queryNumber (app,room) { 2: trace ("connect : "+app+" room : "+room); 3: str = "<QN app=\""+app+"\" r=\""+room+"\" />\n"; 4: sendStr(str); 5: } queryServer()関数(fcs)は、ノードを送信します。サーバーが保持するデータの内、指定したノードのデータだけリクエストします。 1: function queryServer (node) { 2: str = "<"+node+"/>\n"; 3: sendStr(str); 4: } ロビー 簡単なロビーを設けています。今回は、画面をクリックすることで不特定多数のユーザーがコンテンツに参加できるようにしています。ユーザに参加意思を聞き、参加であれば、アプリケーション名、ルーム番号と、ユーザ番号を発行します。 接続するサーバーアドレス、ポート暗号を指定します。アプリケーション名、ルーム番号を指定します。ここでは、アプリケーション名をtutorial、ルーム番号を0とし、ルーム番号0をロビーとしています。コンテンツによっては、ここで部屋毎にユーザーを分類するような機能を設けます。 1: serverAddr = "localhost"; 2: serverPort = "8080"; 3: appNam = "peepTheDeep"; 4: roomNum = "0"; (2)で実装したloadEnd()関数(fcs)を実行し、サーバー接続します。 1: loadEnd(); 2: stop (); Buttonを押すと、このコンテンツへ参加したことになります。その際に、自分の番号とルーム番号を送信します。ここでは、ユーザに関係なくルーム番号1に入室することにします。 1: on (release) { 2: roomNum = "1"; 3: gotoAndPlay ("play"); 4: } メイン メインコンテンツです。ここで、コンテンツ独自の仕様を作成します。クリエイターの腕の見せ所だと思います。 このチュートリアルでは、画面上で画像を隠すマスク部分の整備をします。ユーザは入室した時、画面をランダムに配色(ユーザ毎に変わります。なぜなら配色のデータはXMLでやり取りしていないからです。)し、サイズ60の円を配列します。 1: cC= new Color(_root.m.c); 2: cR = Math.random ()*100; 3: cG = Math.random ()*100; 4: cB = Math.random ()*100; 5: cA = 100; 6: cCT = {ra:cR, rb:'0', ga:cG, gb:'0', ba:cB, bb:'0', aa:cA, ab:'0'}; 7: cC.setTransform( cCT ); 8: cC= new Color(_root.mbg); 9: cCT = {ra:cR, rb:'0', ga:cG, gb:'0', ba:cB, bb:'0', aa:cA, ab:'0'}; 10: cC.setTransform( cCT ); 11: cSize = 60; 12: setProperty (_root.m.c, _width, cSize*1.02); 13: setProperty (_root.m.c, _height, cSize*1.02); 14: for (x=1; x<= 8; x++) { 15: for (y=1; y<=6; y++) { 16: dupName = "cX" + x + "Y" +y; 17: m.c.duplicateMovieClip(dupName, x*10+y); 18: setProperty ("_root.m.cX" + x + "Y" + y, _x, cSize/2 + (x - 1)*cSize); 19: setProperty ("_root.m.cX" + x + "Y" + y, _y, cSize/2 + (y - 1)*cSize); 20: } 21: } getMainData()関数(fcs)は、メインコンテンツ上で、他ユーザからのコールバックを firstChildプロパティ(fla)で識別し、それぞれの処理を行います。 firstChildプロパティ(fla)が"Hd"(con)の場合、指定の円を非表示するメソッドを実行します。 firstChildプロパティ(fla)が"Shw"(con)の場合、指定の円を表示するメソッドを実行します。 firstChildプロパティ(fla)が"N"(fcs)の場合、サーバーより割り当てられたユーザ番号を受け取り、selfname変数(fcs)に代入します。また、現在のサーバー上に保持されている情報をリクエストします。 1: function getMainData (receiveXML) { 2: var e = receiveXML.firstChild; 3: if (e != null) { 4: trace (e); 5: if (e.nodeName == "H") { 6: // circleを非表示にする 7: t = e.attributes.n; 8: eval("m." + t).hideCircle(); 9: } else if (e.nodeName == "S") { 10: // circleを表示する。 11: t = e.attributes.n; 12: eval("m." + t).showCircle(); 13: } else if (e.nodeName == "N") { 14: // connect 15: selfname = e.attributes.n; 16: trace ("selfname : "+selfname); 17: queryServer("QR"); 18: } 19: } 20: } setXML()関数(fcs)は、指定したノードとユーザ番号を送信します。 1: function setXML (node) { 2: str = "<"+node+" n=\""+selfname+"\" />\n"; 3: sendStr(str); 4: } setXMLc()関数(fcs)は、指定したノードと1つの指定した情報を送信します。 1: function setXMLc (node, n) { 2: str = "<"+node+" n=\""+n+"\" />\n"; 3: sendStr(str); 4: } onXML()メソッド(fla)により、XMLSocketによりデータを受信した時に呼び出されるコールバックです。ここでは、2.で定義するgetMainData関数(fcs)を指定しなおします。再度アプリケーション名、ルーム番号を送信します。 1: mySocket.onXML = getMainData; 2: queryNumber(appNam, roomNum); 各円内に自身の表示非表示を実行するためのメソッドです。 ここではalphaチャンネルを使ってやっていますが、単純にthis._visibleをtrue,false切り替えだけでも機能します。 1: onClipEvent (load) { 2: function hideCircle () { 3: cC= new Color(this); 4: mC = cC.getTransform(); 5: cR = mC.ra; 6: cG = mC.ga; 7: cB = mC.ba; 8: cA = 0; 9: cCT = {ra:cR, rb:'0', ga:cG, gb:'0', ba:cB, bb:'0', aa:cA, ab:'0'}; 10: cC.setTransform( cCT ); 11: } 12: function showCircle () { 13: cC= new Color(this); 14: mC = cC.getTransform(); 15: cR = mC.ra; 16: cG = mC.ga; 17: cB = mC.ba; 18: cA = 100; 19: cCT = {ra:cR, rb:'0', ga:cG, gb:'0', ba:cB, bb:'0', aa:cA, ab:'0'}; 20: cC.setTransform( cCT ); 21: } 22: } 各円内にあるボタンアクションです。ロールオーバーで、自身を非表示させるためのXMLを送信しています。ロールアウト、ドラッグアウトで表示させるためのXMLを送信しています。 1: on (rollOver) { 2: _root.setXMLc("H", this._name); 3: 4: }on (rollOut, dragOut) { 5: _root.setXMLc("S", this._name); 6: } まとめ 簡単な説明ではありましたが、マルチユーザーコンテンツの仕組みがわかっていただけたかと思います。とはいっても、いろいろ面倒なことがあるのも事実です。ですが、一度流れを理解すれば、そんなに難しくないはずです。是非皆さん一度チャレンジしてみてください。おもしろいですから。 今回の作例は、シンプルなものにしてあるので、メイン部分で、個別認証の部分を省いています。もっとマルチユーザーコンテンツをおもしろくするには、ユーザー一人一人を認識し個別化することで更に違ったコミュニケーションが可能になります。 チュートリアル目次に戻る

2002/03/11

* FACEsメールマガジンVol.2

■FACEsメールマガジンVol.2 (2002/03/11) (リンク・情報は発行当時のものです)
続きを読む »

2002/03/08

* poppinSoccer ver 1.01解説

目次 はじめに ムービークリップオブジェクトの構造 FACEsサーバとのやり取りの流れ FACEsサーバの「データを共有する部屋」に接続 ゲームの初期設定 通常時のデータやり取り ボールキック時 他クライアント接続時 他クライアント切断時 詳細図 FACEsサーバの「データを共有する部屋」に接続 ゲームの初期設定 はじめに単純なサッカーゲームpoppinSoccerの説明をします。このゲームの場合、通常のマルチユーザーコンテンツの処理に加えて、自分はどっちのチームに入ったらいいのか、自分にとって他のクライアントが敵なのか、味方なのか、等の問題をうまく処理しないといけません。このチュートリアルでは、主にクライアント・サーバー間のやり取りに焦点を置いて、上記の問題をどうやってクリアしているのかを解説しています。具体的なスクリプトの解説はサンプルファイルの方に詳述していますので、そちらを参照してください。 サンプルコンテンツ サンプルファイル ムービークリップオブジェクトの構造基本的に、それぞれのオブジェクト自身が自分の動作に関係のあるファンクションや変数を保持しています。XMLsocketに関する部分は_rootに書かれています。(インデントは階層の深さを表しています。) _root:XMLsocketの通信に関わるスクリプト、チーム分けスクリプト enterButton:クリックして接続、ゲームを開始 fieldmonitor:プレイヤーの位置を縮小して表示 player:自分。ジャンプのアニメーション、キック判定のタイミングを出す。 field:プレイヤーの位置にあわせてグリッドを移動、他プレイヤーとボールの表示 company,enemy:他プレイヤー、キック時にアニメーション ball:ボールの移動、ジャンプ時のキック判定 timer:1秒ごとに自分の位置をサーバに送信するためのタイマー arrow,jumpButton:プレイヤーの位置を変更、ジャンプ keyControl:キーボードからプレイヤーの位置を変更、ジャンプ youLose,youWin:ボールがゴールに当たったときに流れる文字 FACEsサーバとのやり取りの流れサーバとクライアント間のどういったやり取りが、ゲームとしての様々な機能を実現しているかを説明します。具体的なスクリプトの解説はFlaファイルの方に詳述しましたので、そちらをご覧ください。 FACEsサーバの「データを共有する部屋」に接続(参照:図1) FlashからFACEsサーバに接続して、「一定数のクライアント同士でデータを共有する部屋」に移動するまでの流れを説明します。この手順は、定員数の決まっているコンテンツを作成する場合は毎回踏まないといけないものなので、ほかのコンテンツ作成時にもそのまま参考になるかと思います。 接続XMLSocketオブジェクトの関数connect()を実行すると、サーバにconnectできた場合、onConnectで指定したコールバック関数(この場合checkConnect())にtrue値が返されます。 データを共有する部屋のロビーの指定FACEsプロトコルの<QN app="poppinSoccer">を送信すると、"poppinSoccer"というデータを共有する部屋のロビーに入ることができます。ロビーに入ると、<N n="番号">という形でサーバからクライアント固有の番号(以下IDと呼びます)を返されます。この値は各クライアントを識別するIDとなります。 部屋番号取得poppinSoccerは最大6人でプレイするゲームなので、ロビーから、さらに6人ごとに区切られたデータを共有する部屋に移動しなければなりません。FACEsプロトコルの<QER max="6">を送信すると、<ER r="番号">という形で、まだ定員に達していない部屋のうち、最小の部屋番号が返されます。 ロビーにユーザー情報を残しておくこれから自分がどの部屋に向かうか、というデータをサーバに残しておきます。<LOG n="ID" r="部屋番号" svae="適当なハッシュ名(今回は"assign"),key="ID">という形で送信します。LOGを残さないと、後から入ってきたクライアントが適切な部屋番号を取得することができません。keyにIDを用いるのは、他のクライアントが同じkeyを使って現在のLOGデータを上書きしないためのようです。 「データを共有する部屋」に接続FACEsプロトコルの<QN app="poppinSoccer" r="部屋番号">で、ロビーからデータを共有する部屋に移動します。サーバからは<N n="ID">が返されます。ここで返されるIDは、2で返されるIDと同じです。 ゲームの初期設定(他クライアントの存在の有無、ボールの位置情報の取得、クライアント情報の登録)(参照:図2) ここからは、このゲーム独自の仕様になります。 poppinSoccerのルールとして、クライアントはサーバメモリ上に自分の情報を登録することになっています。新規に部屋に入ったクライアントは、まずその部屋に他のクライアントがいるかどうか(登録情報があるかどうか)サーバに問い合わせます。他のクライアントがいれば(登録情報があれば)、それらの情報を元に、 自分をどちらかのチームにふりわけます(チームは"1"と"0"に分かれます) それぞれのクライアントと、Flash上の敵味方のキャラクター(以下プレイヤーと呼びます)を対応づけ、fieldムービークリップ上(以下fieldと呼びます)の指定された位置に移動させます 最新のボールの位置情報を、他クライアントに問い合わせ、Flashに反映させます。 他のクライアントがいなければ(登録情報がなければ)、 常にチーム"1"に参加 自分自身でボールを投げ入れます。 その後、自分の情報をサーバメモリ上に登録します。 以下、具体的に説明します。 他クライアント存在の有無の問い合わせpoppinSoccerのルールとして、クライアントの登録情報は<PLR>ノード下に置くことになっています。なので、他クライアント存在の有無を問い合わせるために、FACEsプロトコルの<QR n="PLR">を送信して<PLR>ノードをリクエストします。帰ってきた<PLR>ノードに子ノードがない場合(<PLR />だった場合)、その部屋には誰もいない、ということになります。<PLR>ノード下に子ノードがあった場合は、それはクライアントデータなので、分析してFlashに反映させます。 A. 他クライアントがいる場合(<PLR>ノードに子ノードがある場合) チーム分けサーバから返されるデータは以下のような形です。(実際には改行無しの1行のデータとして送られてきます) <PLR> <LCT n="ID" team="1または0" x="座標" y="座標"> <LCT n="ID" team="1または0" x="座標" y="座標"> : : </PLR> このデータを分析していきます。まず、自分がどっちのチームに参加するか決めます。今どっちのチームの人数が少ないか判断して、少ないほうのチームに参加します。同数の場合はチーム"1"に参加します。次に他クライアントを、Flash上の敵味方のプレイヤーと対応させ、位置情報に基づいてfield上を移動させます。 ボール情報の取得ボールの位置情報を他クライアントに問い合わせます。問い合わせる相手は、<PLR>下に最後に登録したクライアントです。登録の順番は、LCTノードの並び順から推測できます。<IBL n="ID" to="最初の子ノードの属性n">と送信すると、<BLL x="座標".....>という形で、最後に登録したクライアントが「そのクライアントのFlash上の現在のボール位置」を返してきます。(<IBL>、<BLL>ノードはpoppinSoccer独自のものです。<BLL>ノードは通常のボールキック時にも使います)そのデータをField上のボールに反映させます。 B. 他クライアントがいない場合(<PLR>ノードに子ノードがない場合) チームわけ最初のクライアントは常に"1"チームに参加します。 ボール投入誰もいないということはボールがまだ投げ込まれていないということなので、自分で投げ入れます。<BLL k="ID" x="座標".....>を送信します。サーバは<BLL k="ID" x="座標".....>データをそのまま返してくるので、そのデータをField上のボールに反映させます。 サーバに自分のデータを登録<LCT n="ID" team="チーム" x="座標" z="座標" save="PLR" key="ID" self="ID">という形で他クライアントに自分のデータを送信します。(<LCT>ノードは通常の位置情報のやり取りでも使います。)他クライアントはこのデータを受けて、クライアントの新規参加を知り、それぞれのFlash上に反映させます。また、このデータはサーバメモリ上にも記録されます。自分以降に部屋に入ってきたクライアントは、(2A)のようにサーバメモリ上のデータを参照して、その存在を知ることができます。このノードは自分がdisconnectした時に自動的に消去されます。 通常時のデータやり取り各クライアントは1秒ごとに自分の位置情報を<LCT n="ID" team="チーム" x="座標" z="座標" y="座標" save="PLR" key="ID" self="ID">というかたちでサーバに送信します。このデータはサーバメモリ上に保存されると同時に<LCT n="ID" team="チーム" x="座標" z="座標" y="座標">という形で各クライアントに送信されます。各クライアントはこのデータを元に対応するプレイヤーを移動させます。 ボールキック時ボールをキックしたクライアントは、<BLL k="ID" x="座標"...>という形でサーバにデータを送信します。サーバは各クライアントに<BLL k="ID" x="座標"...>というデータを送信します。各クライアントはそのデータを元にFlash上のボールの位置を変更します。(この時、通信速度の関係からボールの位置が現在の位置から多少ワープしてしまいます。この辺の同期の取り方、あるいはごまかし方は今後の課題でもあります。) 他クライアント接続時送信されてくる<LCT>ノードは常にそれがどのクライアントから送られてきたものかチェックしています。もし、送られてきた<LCT>ノードのクライアントが今まで知らなかったものなら、それを新規のクライアントだと判断し、Field上のプレイヤーを対応させます。 他クライアント切断時他のクライアントがdisconnectすると、<D n="ID">というデータがサーバから送られてくるので、IDに対応するプレイヤーを消す処理をします。 チュートリアル目次に戻る

2002/03/04

* Flash MX announced!

以下はFlashagazine.comの記事: Flash MX(6) announcedの日本語訳です。
続きを読む »