ターン制パズルゲームを作ってみるぞ
待たせたな!(大塚さん風に)
いままで資料館を読み続け、うわべだけの基礎知識を習得したチルドレン達なら、もう何も見ずともプログラムを組めるかもしれないが、一応実例を挙げてやってみようじゃないか、というのが今回の趣旨だ。
今回作るゲームのあらまし
手っ取り早くプログラムの話に入りたい人は読まなくてよし。
今回作るゲームだが、かなり前にスレで一度公開した事がある、「ボンバードマン」というゲームを、作り直すことにした。このゲームは、ハド○ンに喧嘩を売る事が目的ではないが、ターン制のボン○゙ーマンである。1ターンに1手ずつ行動し、先に相手を焼き殺すか、窒息死(後述)させたほうの勝ちという他愛の無いゲームである。詳しくは概要も見るべし。
通信の理想
- 1vs1で対戦を行う
- ただし、他のユーザーもゲームを観戦し、チャットする事が可能である
- 観戦者からのチャットは不許可にすることが可能
今回、観戦者(オブザーバー)が発生する。そいつらの扱いとプレイヤーの扱いが異なるので注意しよう。
プレイヤーとオブザーバー
オブザーバーは、ゲームを途中から観戦できなければならない。
既に始まっていて、5ターンぐらいゲームが進んでいたとしても、新たにオブザーバーとして参加できるようにする、という意味である。そのためには、今まで行われていたゲームの再現が必要である。
- 今まで送受信したデータを全て保存しておき、新規オブザーバーへ渡してやる
- フィールドの情報を全てまとめて送信する
といった方法が考えられる。前者は実装が手軽だが、ゲームが長期化すればするほどデータサイズが長くなり、再現に時間がかかってしまうという問題点がある。なので後者の方法を採用する。
将棋がわかる人は、前者は棋譜を渡す、後者は盤面の写真を渡す、と考えればパツイチで理解できるはずだ。
通信頻度
まず、ゲームデータの送信は、各プレイヤーが行動するたびに行う。
すなわちゲームルールより、データの送信を以て自分のターンを終了とし、データの受信を以て相手のターンを終了とする、という構図が出来上がる。
ただそれとは別に、テキストによるチャットが出来なければならない。つまり一つのソケットに
- ゲームデータ
- チャットデータ
この2種類が流れてくる可能性があるわけだ。ただし、ここに新規オブザーバー用のデータ(後述)も加わるということを覚えておこう。
データの中身
ゲームデータ
毎ターン、フィールドの全ての状況、変数を送信する必要はない。なぜなら、フィールドは相手と自分に等しく存在しており、それをわざわざ送信するのは無駄ということになる。
今回のような単純な通信対戦においては
- 計算で求まるデータは送信せず、計算に必要なデータを送信する
ということに注意してゲームの中身を考えていく。
今回のゲームは、全ての操作をマウスクリックにより行う。なので
- マウスがフィールドのどの座標をクリックしたのか
- 右クリックか、左クリックか
というデータを受け渡しし、処理を行う。
- 自分のターン:自分のマウスクリックの結果を処理
- 相手のターン:相手のマウスクリック情報を受信し、同じように処理
こうすることで、受信した後の処理を共通のものとして書けるだろう。
チャットデータ
テキストデータである。内容のみを送信し、受信側で名前を付与して表示を行う。
ホストが各オブザーバーにチャットデータを送る際に扱いやすいように、送信者の名前と本文を送信する。
オブザーバー向けゲームデータ
今回ゲーム中にやり取りしているのは、どこをクリックしたか、という情報だけであるが、これでは途中からゲームを観戦し始めたオブザーバーに対して問題が発生する。クリック情報によってゲームを進行していくのだが、以前のゲーム情報がないので途中から再現できないのだ。
そこで、新規オブザーバーに対してのみ
- マスの情報(プレイヤーの位置、爆弾の位置やカウントなど)
- プレイヤーの火力など
- 現在何ターン目なのか
その他諸々のゲーム内情報をまとめて送信してやることにする。本来、この情報を毎ターンやりとりしても良いのだが、データサイズが大きくなってしまうのでこれは途中から来たオブザーバーに対して、ホストが一度だけ行う。
レスポンス信号
新たに必要になったので一応付け加えておく。データの中身は無い。
フラグ
データの最初の5byteをフラグとする。まず1byteでデータの種類を判別する。最初の1byteが
- 0:それはゲームデータ
- 1:それはチャットのデータ
- 2:オブザーバー向けゲームデータ
- 3:レスポンス信号
であると判断して処理する。残りの4byteには、データのサイズがbyte単位で書かれているものとする。
ターンの同期
ゲームはマウスクリックの後、爆弾が爆発する処理などを行って次のターンへ進む。マウスクリック情報は殆ど瞬時に相手に伝わるが、その後が問題である。
- マウスクリック情報を受け取る
- 描写処理などを行う
- 次のターンへ進む
という流れであるが、マシンスペックによっては描写に時間がかかったりする。*1
つまり、自分がターンを進めても相手がまだ描写処理中である可能性もあるわけだ。描写処理中はテキストの送受信などを行っていないため、チャットが円滑に行われない可能性もある。これをどうにかするため、次のターンへ進む歩調もあわせることにしよう。具体的には
- 各プレイヤーの準備完了フラグを0にする
- マウスクリック情報を受け取る
- 描写処理を行う
- 自分の準備完了フラグを1にする
- 相手にレスポンス信号を送信する
- 相手のレスポンス信号到着を待つ
- 受信したら、相手の準備完了フラグを1にする
- 二人の準備完了フラグが1になったら、次のターンへ進む
このような流れにしてやれば、少なくとも描写処理によって遅れが生じることは無いだろう。本当はオブザーバーに対してもこのような処理をやればいいのかもしれないが、めんどくさいので省く。
オブザーバーのチャット
観戦者はプレイヤー二人とは接続するが、観戦者同士では接続しない。これでは、オブザーバーがチャットテキストを送信した時、プレイヤーにしか送り届ける事ができず不便だ。
今回は以下のようにする。
- オブザーバーは、プレイヤー2人にチャットデータを送る
- 各プレイヤーがチャットデータを受信する
- このときホストが、オブザーバー全員にチャットテキストを送信する
ホストの負荷がちょっぴり高くなってしまうがまあ、こういう事にしておく。
サンプルデータ
←に公開したのでみてくんろ。
まとめ
以上でサンプルの解説を簡単だが終わりにしたいと思う。ダラダラ続けてしまったが実際の作業時間はゲーム部分1日、ネットワークの構想数日、作業数日といったくらいだと思う。
次回は、CGIと連携してマッチングをやろう。HSPでHTTP通信のやり方を簡単に説明していく。HSP付属のDLLを使えば楽に出来るのでそれでやってしまおうとおもう。TCPでもじゃもじゃ通信するのも面倒だしね。
なお次回の講座ではPerlを用いて簡単なマッチングサーバーを作るが、Perlそのものの説明についてはバッサリ省く予定なのでおべんきょうが必要なチルドレン達は、本買うとかどっかのサイトで予習してくると理解するスピードがシャア専用並に速くなることだろう。あんまり複雑なことはやらないし、もともとPerlもうわべを撫でるだけならHSPバリに単純な文法も手伝って理解しやすい*2ので、多分予習してこなくても流れはわかるはず。多分。知らん。