ネットワーク対戦テトリス(2001.03.29から)

スピーディで燃える対戦テトリスを作ります。目標はGB版テトリス(?)です。


途中成果物(ゲーム本体)のダウンロードはこちら

#地味なスクリーンショット…


2001.04.18

気づけば半月も過ぎていますね。ふぅ。

テトリスなどの落ちゲーよく見かけます。いろんな環境で皆さん作られています。でも共通の不満があります。その不満とは、

  1. ブロックを途中で止められない。
  2. ブロックの引っ付きタイムがない。
  3. ブロックの回転のしかたがおかしい。
  4. 左右移動のキーリピートがなっとらん。

※ブロック以外のものが落ちてくるゲームではブロックを他のものに読み替えてください。

1.はオリジナルのロシア人がつくったものもそういう仕様かもしれませんが、燃えるゲームとしてはいけてないと思います。「ズラシ入れ」や「回転入れ」などのテクニックを好むプレイヤーには見向きもされないでしょう。

2.ブロックは上から下に落ちてくるのですが、下に積まれたブロックがあるとそれ以上落ちることができず固定されます。これを「固まる」または「引っ付く」と呼びます。さて、ブロックはゲームの進行にしたがって落ちる速度が速くなります。実際には1ブロック、1マス分落ちるのにかかる時間(1マス落ちタイム)が短くなります。しかしながら、ブロックがそれ以上落ちることが出来ずに「さぁ引っ付くぞ」という時には、この「1マス落ち時間」を使ってはいけないのです。それでは燃えるゲームにはなりません。GB版テトリスや、セガのテトリスのようにちゃんと別のタイムを使わなければなりません。別々に管理しなければならないのです。さらに、「今引っ付きタイムがはじまったぞ」ということをプレイヤーに知らせるのは非常に効果的です。セガの「ドォン」という低い音は心臓に悪いながらも燃え加減を増してくれます。

3.回転の中心には最新の注意が必要です。燃える回転にせねばなりません。適当に決められた回転のしかたでは興ざめです。やりこんでいるプレイヤーのプレイをよく見なければなりません。

4.ゲームがかなり進むと、目にもとまらぬ速さでブロックが落ちてきます。引っ付きタイムがあるおかげで、高いところにあるブロックに落としつつ、狙った列へブロックを移動できます。しかしながら、左右移動のキーリピートが横着に処理されていると、ブロックは真中の3列くらいにしか移動できず、興ざめです。大抵そこまでゲームを進めるプレイヤーは次のブロックを見ていますから、必要であれば「ブロックが落ち始める前に」左右のキーを押し始めています。したがって、ブロックが引っ付いてから、次のブロックが落ち始めるまでに「キーは読むがブロックは表示されない・動きもしない」時間を設け、落ち始めると同時にリピートが効くようにせねばなりません。このとき、キーリピート有効時の1マス落ちタイム間の最大移動マスを調整できるようにしておくことが重要です。あるいは、「ゲームが進んだらキーリピート開始タイムを短くする」作戦もあると思います。いづれにしても左右のはしにブロックを到達させるのに2〜3マス落ちてしまわないと燃えることは出来ないでしょう。キーリピートの重要性をわかっていないと、コンピュータ(COM)対戦でCOMのブロックの左右の移動がインチキになってしまったりします。

テトリスのようなゲームを作ることは簡単に見えますが、燃えるように作るのは結構大変です。セガのアーケード版テトリス(猿が踊るやつ)なんかは本当に燃えるゲームになっていると思います。

ゲームプログラマでもない人間がこういうことを書くのはどうかとも思いますが…

実時間で進行するゲームでは操作感がとても重要です。操作感がどのように決まるのかというと、アクションゲームなら、

が最重要だと思います。これに見かけの慣性などを追加して表現したりするのでしょう。


2001.04.19

ネットワーク対戦のためにTCP/IPを通信手段として利用します。もしかするとUDPなのですが、ソケットを使うならどっちゃでもいっしょですわな。対戦テトリスくらいなら、わざわざDirectXを使うのもナニですし。

ちょっとした問題は「相手のIPアドレスをどうやって知るか?」です。ネットワークゲームではいつもここら辺がうっとおしいのです。IPアドレスはダイアルアップ環境などでは接続のたびに変わります。そういうことはゲームサーバが管理するのが筋ですが、そんなたいそうなゲームでもありませんし。ICQユーザでもいまどきは、IPアドレスを隠している時代なので、どうしたものでしょうか?メールでIPアドレスを教える?う〜ん、うっとおしいですなぁ。それと、ファイアウォールの中にいるマシンだと状況はさらにうっとおしいです。TCPでポートを横着して選ぶと大抵のファイアウオールで閉じているのでまずいです。必ず空いているポートは80ですのでHTTPの振りをして通信すればいいのですが、これでもどちらか一方はグローバルIPアドレスを持っていなければなりません。ナップスターとかグヌーテラとかICQなんかのIMではそこらへんをうまいことごまかすところが味噌らしいですが、そんなことがやりたいことではないので、

ということでお願いします。

実はこの工房で予定されている「超簡易HTTPサーバ」というのは、そういう厄介ごとを解決するためにお互いがWebサーバとなってHTTPで自在に通信するための基盤として開発を決意したのでした。

ゲームの中で対戦相手に伝えなければならない情報は

また、対戦相手の様子がこちらでも見えるようにするには

も伝えなければなりません。


2001.04.25

実装方法について検討します。

ゲームの本体はやねうらおさんのYGS2000を使用します。このスクリプトは強力で、軽く、かつ拡張性に富んでいます。ちょっとしたゲームに用いるのは非常にもったいないですが、素早くテトリスを完成させるにはもってこいです。

YGS2000についての詳細はやねうらおさんのページで見ていただくとして、とにかくテトリスのスクリプトを書いてみました。(スクリンショット)元にしたのは、同じくやねうらおさんのYGS2000入門記事です。

ネットワーク対戦のための機能がありませんので、そのためのインポートライブラリを実装せねばなりません。親切なやねうらおさんはインポートライブラリのソースも公開されているのですんなり実装できるはずです。おそらく必要な関数は

それぞれをこなすものでしょう。文字が送受信できれば他はあとでなんとかなるでしょう。

あとは、お互いのIPアドレスをどうやって伝えるかです。今回は片方がサーバ、相手がクライアントとして動作するようにします。これで片方だけIPアドレスが分かれば良いことになります。ついでにサーバ側がどこかのグローバルIPアドレスを持ったWebサーバに自分のIPアドレスを送信しておけば、クライアントはいったんそこを覗いてサーバのIPアドレスを知ることができますね。


2001.04.27

とりあえずゲーム部分だけ実装しました。ダウンロードはこちら

ネットワーク部分はWinsockでブロッキングしない受信サンプルを作成中です。単純につくってしまうと、受信側がrecv()でブロッキングしてしまい、データがこないとゲームが止まってしまいます。それでは困ります。

ゲーム部分の完成度は30%といったところでしょうか。でも操作感については若干調整したのでそこそこ満足しています。(CPUパワーが必要ですが…)

できれば感想などいただければうれしいですが、このページってだれかに読んでいただいているのでしょうか…?


2001.04.28

うぅ、問題が多いですなぁ>Winsock。ブロッキングしないようにするには

のいづれかの手法があるようです。YGSのインポートライブラリからスレッドを起こすか、ウィンドウを起こすか、どちらも私の力量ではすんなり行きそうにないので、

こととしました。これなら通信プロセスは使いまわせそうです。


2001.05.03

さっそく方針転換です。

なにげにサンプルのインポートライブラリをいじっていて、外部に通信プロセスを置くのがものすごく間抜けに思えてきました。

まずは調査結果です。YGS2000向けの通信用インポートライブラリをふたつみつけました。いづれもHTTPのみを前提としています。確かにCGIサーバを想定すればHTTPだけで受信だけでなく送信も可能です。しかしながら、専用自前サーバでないかぎり頻繁にアクセスが発生すればすぐにプロバイダにおこられそうです。管理者にしてみればチャットと同じですからなぁ。

CGIサーバは対戦相手を見つける&ランキングなどの場所として、対戦中は直接通信した方が良いと考えています。問題はプロキシを経由したりするとだめだったりすることでしょう。でも4.19に書いたように

なので、問題ではありません、ということにします。

ここまで来たら、できたようなもんです。連休中に対戦できるようにしたいものですな。


2001.05.04

仮のdllでのテスト結果をまとめます。

LAN上の2台のPC間でのテトリス対戦に成功しました。ただし、接続相手は事前にホスト名がわかっており、どちらから接続を仕掛けるかは手動で操作しました。通信内容は攻撃をしかけるライン数を相手に送るだけです。2ライン消して、相手のPCでブロックがせりあがった時はなかなか感慨深かったです。やればできるもんですなぁ。

さてテスト中に気づいた問題点は…

  1. ホスト名の設定方法
  2. 接続待ちか仕掛けるかの設定方法
  3. ゲーム終了時の処理
  4. 通信内容

ホスト名の設定方法

対戦相手のホスト名をどうやって設定すればいいのでしょうか。

まさか「設定ファイルをメモ帳で編集してください。」とは言えないので、ゲーム画面上で設定できるようにしなくてはなりません。ホスト名はIPアドレスでもいいので、それくらいならゲーム画面で入力するようにしておこう。

民生ゲーム機のように矢印キーと決定キーだけで文字を入力する仕組みはそのうち作ることにします。

接続待ちか仕掛けるかの設定方法

ソケット通信では接続待ちをするサーバ側と仕掛ける側のクライアントで動作が違います。よって対戦する場合、どちらかが接続を待ち、相手はそこへ接続しなければなりません。

これは、ゲームサーバがやってくれるのが理想的だと考えています。まずゲームが対戦サーバに対戦要求を伝えます。サーバはこれを「対戦相手待ち」として受け付け、ホスト名を公開します。ゲームは接続待ち状態で待機します。対戦サーバが次の対戦要求を受け付けると先ほどの「対戦相手待ち」のホスト名をゲームに伝え、ゲームは接続待ち状態となっているゲームに対して直接接続します…

ホスト名の設定までサーバが面倒を見てくれるのでゲーム自体は楽チンになると思います。

ただこれだと、LAN環境で遊ぶときにわざわざサーバ、それもWEBサーバでCGIも必要、が必要となってしまいます。よってサーバがなくてもピアツーピアでも動作できるような仕組みが必要です。

結論としては、「かなり手がかかりそう」です。

ゲーム終了時の処理

「ESC」キーを押した場合は接続断をしてから終了できます。YGS2000のバツ印を押した場合などはソケットの残骸などがどうなるか不安です。不安ですが、プログラム、ピヨピヨのわたしにはどうしようもないので、放っておくしかありません。ちなみにテストではワトソン博士出まくりでした。(汗)

通信内容

たぶん、文字列しか送受信できないので、数値は文字列に変えて送る必要があります。10進数よりは16進数の方が文字数が少なくてすみそうですね。それなら、64進数で送ったれ…と考えてたら、「それをBASE64エンコードっていうんじゃよ」と仙人の声が聞こえてきたので、16進数に落ち着きました。

あと、数字だけ送っても意味がわからんので、なにか文字を付け加えなければならないでしょう。どっちゃにしてもややこしい文字列操作が増えそうですが、「柔軟性を残す」というタイギメイブンのもとに、スクリプト側にまかせることとします。


2001.05.05

ふぅ。わらわらと残作業がつみあがってきました。

  1. 複数ソケット対応
  2. 不意の接続断への対応
  3. 乱数の同期
  4. ゲーム接続〜開始〜終了〜切断の仕組み
  5. ゲームサーバ仕様決定
  6. 相手のブロックは見えるのか?

複数ソケット対応

よくよく考えたら(そんなに考えなくてもわかりますが)、4人で対戦するなら、サーバがない限り、最大3つのソケットが必要です。わぉ。そんなにいっぱい接続しても大丈夫かな?

不意の接続断への対応

対戦中などに接続が切れたりしたときにゲームを終了するなどの処理が必要です。WINDOWS98をお使いであれば、ネット接続中にOS自体が落ちることも多々あるようですし…

でも、複数ソケットがそれぞれ勝手に切断されたら、どないしたらええんでしょうか?例えばホストA〜Cの3台で対戦中を考えます。AとBとの間の接続が切れてしまったら、何も知らないCにその旨伝えなければなりませんね。で、伝えようとしている時にCとの接続が切れたりして(汗)。これは(汗)では済みまへんなぁ。やっぱりゲームサーバは必須なのでしょうか?

乱数の同期

遠くにあるPC同士で対戦するからといってでてくるブロックが違ったら、「い〜み、なぁぃじゃぁ〜ん」です。よって擬似乱数の種を教えあいして同じ乱数を使わねばなりません。これはテストで成功しています。

ゲーム接続〜開始〜終了〜切断の仕組み

今は、4人分の画面が表示され、それぞれ勝手にスタートする仕組みになっています。これはこれで常時乱入可能なバトルロワイヤル風でいいのですが、乱数の種の管理がうっとおしいですな。やっぱりまじめに管理せねばならんでしょう。

つまり、「参加者受付中」を一定時間表明して、時間内に参加者を募り、時間経過後に同時にゲームが開始する。ゲーム終了後は勝敗を表示して切断。あるいは、終了時には同じメンバーですぐに再度対戦できるのもいいかもしれません。

ゲームサーバ仕様決定

プレイヤーのロビーとしての機能、ホスト名設定機能および、サーバ役決定機能を持たせたいところです。これらはゲームに依存しないのである程度汎用的に使えると、いいな。

相手のブロックは見えるのか?

目標としているGB版では相手の画面は見えないのですが、セガテトリスとか見てると見えるのも悪くないかも。でもなぁ、200個のブロックの様子を正確に送信すると、何バイトだ?詰めても100バイトくらいにはなってまうねぇ。うちはなんと言っても28.8kだからなぁ。いややなぁ。

ブロックの回転がおかしい(自分でもわかってました)とか、レベル5が5歳児にはむつかしすぎる(クリアできるレベルで年齢がわかるかもしれませんな)とか、貴重なご意見をいただいていますが…なにしろ手元にGB版やセガ版など参考にするものがありません。

どこかに良いゲーセンはないのかなぁ。


2001.05.16

途中経過です。

  1. 複数ソケット対応 実装済み・テスト中
  2. 不意の接続断への対応
  3. 乱数の同期 
  4. ゲーム接続〜開始〜終了〜切断の仕組み
  5. ゲームサーバ仕様決定
  6. 相手のブロックは見えるのか?

不意の接続断への対応

いまのところ、ネット対戦中に接続断が起こったら、ノーゲームとします。また、3台以上で対戦する場合の通信はトークンを持ちまわる方式にして、参加PC全てがノーゲームと認識するように工夫することにします。

乱数の同期

掲示板でnozさんからこんなご意見をいただきました。ありがとうございます。このごみための唯一の閲覧者かもしれません。

あとは、出てくるブロックの同期を取るのは、ランダムシードの同期を取らなくても、
最初に200ブロック分くらいのブロックの順番のリストを作って全員に送っておいて、
それを順番に出していくというのもいいのではないかと思いますが…

200ブロック過ぎると最初に戻っても、現実的にはあまり気にしなくてもいいかも。

人間と言うのはぼぉっとしているようでも、妙なことに敏感です。経験上繰り返しはまずいと思いまして、乱数の種を教え合いっこする方針としました。

ゲーム接続〜開始〜終了〜切断の仕組み
ゲームサーバ仕様決定

フリーのWebサーバに乗るようなCGIでゲームサーバを構成する方針となりました。今perlのマニュアル読んでます。以前にちょっとしたものなら書いたことあるので、「思い出すコマンド」実行中です。

そのうちムニャムニャしてるうちに頭の上に煙が出てきたら、じきにできるでしょう。

相手のブロックは見えるのか?

これについてもnozさんから貴重なご意見をいただきました。ほんまにおおきにです。

相手の状態を見られるようにするのであれば、
「どのブロックを」「どこに」置いたという情報だけ通信してはどうでしょう?
全員分の判定が面倒ならば、どの列が消えたという情報まで送ってもいいと思います。
それだったら、ブロックを置くたびに通信しなきゃいけなくなりますけど、一回一回の通信量はそんなに多くならないのでは?

(赤字はごみためまんがつけました)

わたしも全ブロックの情報を送るのはやばいと思っていました。ただ、少ない情報を蓄積する方式だと文字化けとか起こった時には、困るので、困るのです。文字化けってそんなに起こらないのでしょうか。いらぬ心配かも。そう、いらぬ心配なので、「改良型nozさん対戦通信方式」として、トークンにそういう情報を載せて送る方針とします。

実装上の問題点は以下のとおり

通信を非同期におこなうために、スクリプトが読みに行かない限り受信バッファに送られてきたデータがたまってしまいます。YGSは別ウィンドウにフォーカスが移ると、スクリプト自体も停止するので、取り出されないバッファはどんどんたまります。それでは困るのですが、トークンを持ちまわる方式なら、「あ、あいつ、ゲーム止めてる。インチキや。」と気づくことができるっぽいので、OKと考えています。

送信バッファは送信完了してないのに次のデータを送りたくなったらどうなるかです。現状送信は非同期でないのですが、winsockのそのあたりの細かい動きがわかりません。まぁ、トークン方式だから持ち回り周期をスクリプトで決めちゃえばいいんでしょうな。

やっぱり4人対戦にこだわるがゆえに仕様がおそろしくふくらんでしまっています。これをスクリプトで書くのは大仕事ですな。

近所の良いゲーセン

大宮バイパス沿いの小さな店を見つけました。ロジャースも近くにあって、いい感じです。セガのテトリスもあるのでまったくもっていい感じです。ゲーセンなんてひさしぶりに行きました。


2001.05.26    ライブラリα1.0公開(動作無保証版)

ゲームシステム自体が大きくふくらんでしまったので、

とりあえずインポートライブラリのα版だけ公開します。動作検証用のものですから、エラー処理などが一切ありません。もしそれでも使ってみようという方は以下の点にご注意ください。

ダウンロードはこちら、から。