落ちゲーTipsの巻


 今回は「テトリス」や「ぷよぷよ」に代表される落ちゲーを作るためのノウハウを惜しげもなく大公開しようと言う企画です。
 あらかじめ断っておきますが、ここでの説明はあくまで「一例」です。必ずしもこの通りに行わなければならないと言うわけではありませんし、ここにかかれている方法よりも効率のよいやり方があるという可能性は十二分にあります。

●準備
 落ちゲーに限らずゲームを作る際にはあらゆる情報をメモリに記憶させておかないと後々苦労します。そんなわけでフィールドと同じ以上のサイズを確保した2次元配列(以下、仮想フィールド)を必ず用意してください。これを忘れると次のステップでいきなり無駄な試行錯誤を始めてしまって疲れます。
 それと落下しているブロックの形状を把握するためにブロック一個一個の座標を格納するための配列も用意した方がいいでしょう。
 本当はこれ以外にもたくさん「暗黙の了解」とか「前提」とかがあるんだけどきりがないので必要に応じて説明することにします。

●操作
 ではまず「キー操作によりブロックを動かす」所をやってみましょう。キー操作部分は説明するまでもないので省きますが、ここでの壁はやはり「他のブロックにぶつからないか」でしょう。
 これは「移動先の仮想フィールドを調べて何もなければ移動可能」という処理を組むことで簡単にできます(←あ、それが簡単にできれば苦労しないか(^^;)。また、このとき仮想フィールドを一回り大きく取っておくことで画面端の判定も一緒に行うことができます。ただし落ちゲーのブロックというのは複数で1セットになっており、かつ複雑な形をしているのが普通なので無精せずに全てのブロックについて判定をしなければなりません。そうしないと特定の条件においてブロックをすり抜けたりします(写真1、2)。そのために落下しているブロックがどんな形状をしているのかが把握できないと非常にツライです。
 ちなみにこれを横のかわりに下方向について調べることで落下中のブロックが地面やすでに置かれているブロックについたかどうかを判定できるようになります(写真3)

写真1 一つしかブロックを検索しないとこのような場合に貫通してしまう 写真2 このように全てのブロックについて検索すれば貫通せずに移動できる 写真3 下方向へ検索すれば着地判定になる

 キー操作の類なので一緒に落下についても説明しておきます。「時間がたつと勝手に落下する」処理は、

  1. まずカウント用に変数を用意しておく。
  2. ブロックが出たときにカウントを一定に値にセットしておく。
  3. 1フレーム(1回の処理)ごとにカウントを1ずつ減らしていく。
  4. 落下キーが押されたらカウントを0にする。
  5. カウントを調べ、0もしくはそれ以下の時にY座標を下に移動させ、カウントを再びセットする。

 以上でできます。あとはレベルによってセットする値を増減させればよいです。

●ブロックを把握するとは?
 さっきから「落下しているブロックの形状を把握する」なんて言ってるけど、じゃあどうするのかということになりますが、簡単に言うと「それぞれのブロックの座標がわかれば」よいわけです。
 たとえばテトリスのブロックなんてのは4個一組で構成されています。それらのブロックのうちどれか一つを原点とし、残り3個のブロックの座標を原点からの相対的な座標として配列変数に(4つとも)代入させます(写真4)。これでプログラム的にブロックの形状が把握できることになります。このとき各座標は絶対座標(写真5)で記憶させると移動のたびにいちいち計算し直さなければならなくなるので相対座標(写真6)で記憶させた方がいいでしょう。

写真4 座標がわかれば形状が把握できる 写真5 これが絶対座標。画面左上からの座標のこと 写真6 こっちは相対座標。ある点を原点にした座標ね

●回転
 ブロックを回転させる方法として「あらかじめ回転パターンを配列に記憶させる」方法と「sin,cosを使う」方法がありますが、誰がなんと言おうとパターンを記憶させて置いた方がよいです。とりあえず準備からゲーム中の処理までの組み方の一例(テトリスタイプのゲームを作ることを想定した組み方となっています)を端折って説明します。

  1. 「dim bx,4,4,8:dim by,4,4,8」とかやって回転パターン(兼ブロックパターン)用の配列を用意する。ここでは二つ目の添字(パラメータ)がブロックの「向き」を表します。
  2. 配列にパターンをひたすら放り込む(笑)。
  3. キー入力を読んで右回りなら0→1→2→3→0→・・・、左回りなら0→3→2→1→0→・・・と数値を回す(放り込んだときに逆にしたら当然回し方も逆にすること)。
  4. この時点で一度ブロックと重なっていないか調べ、重なっていたら数値を元に戻す。

 こんな感じでOK(端折りすぎだって(^^;)。ところで本家のテトリスってこんなことできたっけ(写真7)?

写真7 この状態で回転させて横向きにして、そのまま右移動で隙間に収納。イエイ

 sin,cosを使って回転するときは中間パターンが作れるので滑らかに回転できますが、正直メリットはそれだけです(かなり独自路線な落ちゲーを作るのならアリだと思いますけどね)。それでも三角関数を使いたいという人は以下の公式を参考にいろいろやってみてください。ちなみに筆者は「ラジコン操作でゴーゴー」の回で回転処理を作りましたが、小数点の管理とかが面倒なのでヤル気が起きません。

角度をr、回転前の座標をx,y、回転後の座標をx',y'とします。

x'=x*cos r-y*sin r
y'=y*sin r+x*cos r

●消す
 落ちゲーの大事な部分その1、「消す」。これがキチンとできなきゃゲームになりません。では早速ゲームの種類別に説明しましょう。

●隙間を埋める
 ブロックが消えたら今度は空いた透き間を埋めてやらねばなりません。その方法として、「仮想フィールドを1マスずつ調べ、空白が見つかったらそこから上の仮想フィールドの内容を下へずらしてやり、その結果を画面に反映させる」というものがあります。これについては理論で説明するよりも実際に見てもらった方がわかりやすいのでこのスクリプトを実行してみてください。
 あまり演出に凝ろうとせず、この程度にしておくのが無難でしょう(ゲームに使うにはawaitの値を減らして、もっと速く落ちるようにしたほうがいいかも)。

●連鎖する
 テトリス以外の落ちゲーのほとんどが「連鎖」という要素を持っているわけで、これをつけずに落ちゲーは成り立たないと言っても過言ではないわけでして、連鎖処理の組み方です。
 といっても先ほどが「落としたブロックを中心に判定を行っていた」のに対して今度は「フィールド上の全てブロックに対して判定を行う」だけです。そこで消去判定処理をサブルーチンにしてしまい、repeatを使って左上から(じゃなくていいけど)順に検索していけばよいのです。かなりネストするので場合によってはエラーになるかも・・・。
 「ぷよぷよ」ルールだと一度調査が入った(つまりマークした)ブロックは次の連鎖までは2度と検索しなくてよいのですが、前述の通り「コラムス」ルールではそうはいかないため、かなり判定に時間がかかってしまいます。まあこれは仕方がありません。
 もちろん連鎖判定を開始する前にマークに使った変数などは全てクリアするのを忘れると意味ないですので注意。


 そんなわけでまだゲームオーバー判定や対戦など、触れていないことがいろいろありますが、これ以上書くといつ終わるかわからないのでひとまずこれくらいにしておこうと思います。また、今回スクリプトを全くと言っていいほど掲載しなかったので「だからどう組むのよ」とか言われそうですが、余裕のあるときにサンプルゲームという形で公開できればと思っています。


戻る