home > 過去ログ(2004年以前) > poppinSoccer ver 1.01解説
2002/03/08

poppinSoccer ver 1.01解説


目次


  • はじめに
    単純なサッカーゲームpoppinSoccerの説明をします。このゲームの場合、通常のマルチユーザーコンテンツの処理に加えて、自分はどっちのチームに入ったらいいのか、自分にとって他のクライアントが敵なのか、味方なのか、等の問題をうまく処理しないといけません。このチュートリアルでは、主にクライアント・サーバー間のやり取りに焦点を置いて、上記の問題をどうやってクリアしているのかを解説しています。具体的なスクリプトの解説はサンプルファイルの方に詳述していますので、そちらを参照してください。

    サンプルコンテンツ
    サンプルファイル

  • ムービークリップオブジェクトの構造
    基本的に、それぞれのオブジェクト自身が自分の動作に関係のあるファンクションや変数を保持しています。XMLsocketに関する部分は_rootに書かれています。(インデントは階層の深さを表しています。)

    _root:
    XMLsocketの通信に関わるスクリプト、チーム分けスクリプト
    enterButton:
    クリックして接続、ゲームを開始
    fieldmonitor:
    プレイヤーの位置を縮小して表示
    player:
    自分。ジャンプのアニメーション、キック判定のタイミングを出す。
    field:
    プレイヤーの位置にあわせてグリッドを移動、他プレイヤーとボールの表示
    company,enemy:
    他プレイヤー、キック時にアニメーション
    ball:
    ボールの移動、ジャンプ時のキック判定
    timer:
    1秒ごとに自分の位置をサーバに送信するためのタイマー
    arrow,jumpButton:
    プレイヤーの位置を変更、ジャンプ
    keyControl:
    キーボードからプレイヤーの位置を変更、ジャンプ
    youLose,youWin:
    ボールがゴールに当たったときに流れる文字


  • FACEsサーバとのやり取りの流れ
    サーバとクライアント間のどういったやり取りが、ゲームとしての様々な機能を実現しているかを説明します。具体的なスクリプトの解説はFlaファイルの方に詳述しましたので、そちらをご覧ください。

  1. FACEsサーバの「データを共有する部屋」に接続(参照:図1
    FlashからFACEsサーバに接続して、「一定数のクライアント同士でデータを共有する部屋」に移動するまでの流れを説明します。この手順は、定員数の決まっているコンテンツを作成する場合は毎回踏まないといけないものなので、ほかのコンテンツ作成時にもそのまま参考になるかと思います。
    1. 接続
      XMLSocketオブジェクトの関数connect()を実行すると、サーバにconnectできた場合、onConnectで指定したコールバック関数(この場合checkConnect())にtrue値が返されます。
    2. データを共有する部屋のロビーの指定
      FACEsプロトコルの<QN app="poppinSoccer">を送信すると、"poppinSoccer"というデータを共有する部屋のロビーに入ることができます。ロビーに入ると、<N n="番号">という形でサーバからクライアント固有の番号(以下IDと呼びます)を返されます。この値は各クライアントを識別するIDとなります。
    3. 部屋番号取得
      poppinSoccerは最大6人でプレイするゲームなので、ロビーから、さらに6人ごとに区切られたデータを共有する部屋に移動しなければなりません。FACEsプロトコルの<QER max="6">を送信すると、<ER r="番号">という形で、まだ定員に達していない部屋のうち、最小の部屋番号が返されます。
    4. ロビーにユーザー情報を残しておく
      これから自分がどの部屋に向かうか、というデータをサーバに残しておきます。<LOG n="ID" r="部屋番号" svae="適当なハッシュ名(今回は"assign"),key="ID">という形で送信します。LOGを残さないと、後から入ってきたクライアントが適切な部屋番号を取得することができません。keyにIDを用いるのは、他のクライアントが同じkeyを使って現在のLOGデータを上書きしないためのようです。
    5. 「データを共有する部屋」に接続
      FACEsプロトコルの<QN app="poppinSoccer" r="部屋番号">で、ロビーからデータを共有する部屋に移動します。サーバからは<N n="ID">が返されます。ここで返されるIDは、2で返されるIDと同じです。

  2. ゲームの初期設定(他クライアントの存在の有無、ボールの位置情報の取得、クライアント情報の登録)(参照:図2
    ここからは、このゲーム独自の仕様になります。 poppinSoccerのルールとして、クライアントはサーバメモリ上に自分の情報を登録することになっています。新規に部屋に入ったクライアントは、まずその部屋に他のクライアントがいるかどうか(登録情報があるかどうか)サーバに問い合わせます。
    他のクライアントがいれば(登録情報があれば)、それらの情報を元に、
    • 自分をどちらかのチームにふりわけます(チームは"1"と"0"に分かれます)
    • それぞれのクライアントと、Flash上の敵味方のキャラクター(以下プレイヤーと呼びます)を対応づけ、fieldムービークリップ上(以下fieldと呼びます)の指定された位置に移動させます
    • 最新のボールの位置情報を、他クライアントに問い合わせ、Flashに反映させます。
    他のクライアントがいなければ(登録情報がなければ)、
    • 常にチーム"1"に参加
    • 自分自身でボールを投げ入れます。
    その後、自分の情報をサーバメモリ上に登録します。 以下、具体的に説明します。

    1. 他クライアント存在の有無の問い合わせ
      poppinSoccerのルールとして、クライアントの登録情報は<PLR>ノード下に置くことになっています。なので、他クライアント存在の有無を問い合わせるために、FACEsプロトコルの<QR n="PLR">を送信して<PLR>ノードをリクエストします。帰ってきた<PLR>ノードに子ノードがない場合(<PLR />だった場合)、その部屋には誰もいない、ということになります。<PLR>ノード下に子ノードがあった場合は、それはクライアントデータなので、分析してFlashに反映させます。

      A. 他クライアントがいる場合(<PLR>ノードに子ノードがある場合)
      1. チーム分け
        サーバから返されるデータは以下のような形です。(実際には改行無しの1行のデータとして送られてきます)
        <PLR>
            <LCT n="ID" team="1または0" x="座標" y="座標">
        
            <LCT n="ID" team="1または0" x="座標" y="座標">
            :
            :
        </PLR>
        このデータを分析していきます。
        まず、自分がどっちのチームに参加するか決めます。今どっちのチームの人数が少ないか判断して、少ないほうのチームに参加します。同数の場合はチーム"1"に参加します。
        次に他クライアントを、Flash上の敵味方のプレイヤーと対応させ、位置情報に基づいてfield上を移動させます。
      2. ボール情報の取得
        ボールの位置情報を他クライアントに問い合わせます。問い合わせる相手は、<PLR>下に最後に登録したクライアントです。登録の順番は、LCTノードの並び順から推測できます。<IBL n="ID" to="最初の子ノードの属性n">と送信すると、<BLL x="座標".....>という形で、最後に登録したクライアントが「そのクライアントのFlash上の現在のボール位置」を返してきます。(<IBL><BLL>ノードはpoppinSoccer独自のものです。<BLL>ノードは通常のボールキック時にも使います)そのデータをField上のボールに反映させます。

      B. 他クライアントがいない場合(<PLR>ノードに子ノードがない場合)
      1. チームわけ
        最初のクライアントは常に"1"チームに参加します。
      2. ボール投入
        誰もいないということはボールがまだ投げ込まれていないということなので、自分で投げ入れます。<BLL k="ID" x="座標".....>を送信します。サーバは<BLL k="ID" x="座標".....>データをそのまま返してくるので、そのデータをField上のボールに反映させます。

    2. サーバに自分のデータを登録
      <LCT n="ID" team="チーム" x="座標" z="座標" save="PLR" key="ID" self="ID">という形で他クライアントに自分のデータを送信します。(<LCT>ノードは通常の位置情報のやり取りでも使います。)他クライアントはこのデータを受けて、クライアントの新規参加を知り、それぞれのFlash上に反映させます。また、このデータはサーバメモリ上にも記録されます。自分以降に部屋に入ってきたクライアントは、(2A)のようにサーバメモリ上のデータを参照して、その存在を知ることができます。
      このノードは自分がdisconnectした時に自動的に消去されます。

  3. 通常時のデータやり取り
    各クライアントは1秒ごとに自分の位置情報を<LCT n="ID" team="チーム" x="座標" z="座標" y="座標" save="PLR" key="ID" self="ID">というかたちでサーバに送信します。このデータはサーバメモリ上に保存されると同時に<LCT n="ID" team="チーム" x="座標" z="座標" y="座標">という形で各クライアントに送信されます。各クライアントはこのデータを元に対応するプレイヤーを移動させます。

  4. ボールキック時
    ボールをキックしたクライアントは、<BLL k="ID" x="座標"...>という形でサーバにデータを送信します。サーバは各クライアントに<BLL k="ID" x="座標"...>というデータを送信します。各クライアントはそのデータを元にFlash上のボールの位置を変更します。(この時、通信速度の関係からボールの位置が現在の位置から多少ワープしてしまいます。この辺の同期の取り方、あるいはごまかし方は今後の課題でもあります。)

  5. 他クライアント接続時
    送信されてくる<LCT>ノードは常にそれがどのクライアントから送られてきたものかチェックしています。もし、送られてきた<LCT>ノードのクライアントが今まで知らなかったものなら、それを新規のクライアントだと判断し、Field上のプレイヤーを対応させます。

  6. 他クライアント切断時
    他のクライアントがdisconnectすると、<D n="ID">というデータがサーバから送られてくるので、IDに対応するプレイヤーを消す処理をします。


    チュートリアル目次に戻る