HOME > ActionScript > テトリスアルゴリズム > 次のテトリミノ

次のテトリミノ

攻略性

これから落ちてくる予定のテトリミノをフィールドの横に表示する機能を追加します。ここからこの機能のことを「次のテトリミノ」と呼びます。
当たり前すぎて地味な機能ですが、これがある事でゲームに多少の攻略性が加わります。出現予定テトリミノの表示数は3個にします。

ソースファイル pt10.zip

スコアやレベルなどを組み込まないとゲームとしては少し物足りないですが、今回でテトリスに必要な基本的な機能は出揃う感じです。

プログラムの変更点

構造体

今回は「次のテトリミノ」の機能の追加に加えて、前回までのプログラム部分も含め、データの扱い方を大幅に変更しました。

この「テトリスアルゴリズム」カテゴリのソース内容は、プログラムの全体的な連動性をわかりやすくするために関数などの処理はできる限り一つのクラスに記述する方針で作成しています。今回変更を加えたのはプロパティなどです。関数で受け渡しを行う主要なデータ部分を別クラスにし、C言語で言うところの構造体の様にして扱う形に変更しました。

新しいクラスはDataStructureクラス(DataStructure.asファイル)で、基本的にはクラスの中身はデータのプロパティしかありませんが、データをクリアする為のpublicメソッドを一つだけ実装しているほかは全てgetterとsetterです。
もっと手軽にObject型で定義しても良いのですが、個人的にObject型それ自体で何かすることはあまり考えません。

セッション

別クラスのデータ構造体にしたプロパティ群についてですが、ここで作成しているテトリスのソースは、「二人以上でプレイできるように拡張も可能にしたい」と言う事を頭において作成しています。データ構造体として別クラスに切り離したプロパティは、プレイヤー一人分のデータを1セッションと考えた時に、プレイに必要となるデータ群を一つの単位として切り離しました。

リスナー関数にイベントオブジェクト以外の引数を保持させる

データの扱い方が変わっただけでアルゴリズムは変わっていませんが、イベントリスナーに登録するリスナー関数の設定方法に関してはちょっと特殊(でもないですが)な形に変更しています。

通常リスナー関数にはイベントオブジェクト以外の引数を渡せませんが、リスナー関数をクロージャで設定、登録をする事で、クロージャを記述している親の関数で宣言された変数や引数をリスナー関数でも参照できるようになります。(リスナー関数に無名関数を使用する場合、リスナー関数の登録削除を行うと言う事に関しては注意が必要です)

これを利用して、リスナー関数には自分が登録されているイベントターゲットを持つDataStructureインスタンスへの参照を保持させています。それと同時に、そのDataStructureインスタンスにはリスナー関数自身もFunction型のプロパティとして保持されているので、自分の処理の中で自分自身の登録削除を行えるようにしています(main.asファイル 404行目 dropTimerSet()、626行目 keyHandlerSet())。

セッションのデータ群をグローバル変数で扱えば、こんな回りくどい事は必要無いのですが、それだとセッションの数分だけグローバル変数を用意する必要が出てきてしまいます。
また、主要データがグローバル変数で保持されたり、やたらとグローバル変数が宣言されているのはあまり好ましくなかったりします。

次のテトリミノの仕様

考え方

「次のテトリミノ」を追加するにあたって考えておく必要がある事は、「次のテトリミノ」を表示するオブジェクトや機能はゲーム中に一つだと言う事です。先に「複数プレイに拡張も出来る」と言いましたが、この「次のテトリミノ」の機能は複数プレイになったとしても人数分はないという意味です。

プレイヤー分「次のテトリミノ」があっても良いですが、対戦者同士で「次のテトリミノ」を取りあうような形の方が面白いかなと思うからです。

スプール

ただ、この仕様の場合は注意が必要で、複数プレイで「次のテトリミノ」を取りあうと言う事は「次のテトリミノ」の要求が複数のセッションから同時に実行される可能性があると言う事です。また、「次のテトリミノ」をフィールドに送り出す際には送り出しモーションが完了してからフィールドに表示させる為、若干のタイムラグを持たせていますので、このモーション中に別のセッションから要求があった場合の対処も用意しておく必要があります。

今回は印刷時のスプールの様な感じで処理を用意する事で対応しています。
ですが、一人でプレイする形にプログラミングしている為、ここでこの部分の処理が日の目を見る事はありません(涙)。なので説明も省略です。

出現予定データ

「次のテトリミノ」を表示するには出現予定データが必要になります。この出現予定データはVector配列を使用してこれから出現するテトリミノのコードを5個分用意しておき、この順番に従って落ちてくるようにします。一つ落ちる毎に新しい予定を入れていきます。この出現予定コードはVector配列のnextCodesに保存しています。

private var nextCodes:Vector.<uint>;

一番最初の出現データを用意するのはnextCodesInit()です(main.asファイル 307行目)。プレイ中に新しいテトリミノを送り出すたびに、新しい予定データを補充するのはnextBoardOperate()(main.asファイル 522行目)の中で行います。
このnextBoardOperate()は「次のテトリミノ」を送り出すモーションを描画する為の描画メソッドの実行と、モーション完了時にセッションの次の処理への移行(もしくは次のセッションへの送り出し)も行います。

また、冒頭で次のテトリミノとして表示されるのは3個と言いましたが、出現予定データは5個です。両者の数が食い違っていますが「表示させること」と「データを用意する事」は全く別の事と考えればつじつまが合います。
極端に言いますと、出現予定データは10個でも100個でもOKです。要はその出現予定のうちの幾つをプレイヤーに見せるかと言う事が「次のテトリミノ」の表示部分です。

次のテトリミノの描画

描画の方法はフィールドの描画方法と殆ど同じです(main.asファイル 576行目 drawNextBoard())。描画対象は「次のテトリミノ」の表示コンテナに内包するShapeオブジェクトになります。

private var nextBoard:Sprite; // 「次のテトリミノ」の表示コンテナ
private var drawBoard:Shape; // 「次のテトリミノ」を描画する描画オブジェクト

新しいテトリミノをフィールドに送り出す時に、ループ処理で「次のテトリミノ」の表示を動かしていますが、これもフィールドの描画と同じ理屈です。
フィールドではキーイベントやタイマーオブジェクトの実行で特定の方向へセルのサイズ分一つずつ動きますが、「次のテトリミノ」ではこれをENTER_FRAMEで連続しているだけなのです。

なんかdrawNextBoard()の中で細々と座標を計算しているように見えますが、理屈がばれると実はとても大雑把で、要はdrawField()の処理を「次のテトリミノ」の描画に特化させたのがdrawNextBoard()で、それをnextBoardOperate()メソッドから連続で実行しているというプロセスです。

動作確認

動かない時はFlash画面上を一度クリックしてみてください。
今回からステージの幅を少し小さくしました。幅420 x 高さ500の縦長です。
下記の定数値を変えると「次のテトリミノ」の表示が変わるので試してみてください。
幅を狭くすると、細窓から少しだけ見える感じになるのが悩ましげです。

private const NUM_NEXT:uint;     // 「次のテトリミノ」の出現予定保存数
private const NEXT_W:uint;       // 「次のテトリミノ」表示コンテナの幅
private const NEXT_H:uint;       // 「次のテトリミノ」表示コンテナの高さ
private const LOC_INTERVAL:uint; // 「次のテトリミノ」の表示間隔

Flashファイルをご覧いただくためには、
アドビシステム株式会社のフラッシュプレーヤー(Flash Player)が必要です。
インストールされていない方は下のボタンから、最新版が無償で入手できます。

Adobe Flash Playerのダウンロード

ソースファイルについて

ソースファイルはasファイルのみです。

使用する環境としてはFlashIDE(動作確認はSC4のみ)を想定していますので。適当に用意したflaファイルに、ダウンロードしたソースファイルをドキュメントクラスとして適切に設定すれば問題無く動作します。
また、コーディングはFlashDevelopを使用して行っておりますので、プロジェクト内にファイルを適切に配置、もしくはクラスパスを設定するなどすればFlashDevelopだけでも問題なく動作します。Stageサイズは幅、高さともに500pxです。
申し訳ありませんが、ご質問等にはお答えできませんので、ご使用は環境に合わせて各自行ってください。

TOP