テトリスを作るの7
最後のテトリス。
今日はコントローラー。 ゲームの開始と終了、キー入力の受付、時間の管理を担当する。 開始ボタンが押されたら、
- フィールドと得点を初期化する。
- キー入力イベントを設定して、キー入力を受け付けるようにする。
- タイマーを開始する。
フィールドから終了を報告されたら、
- キー入力イベントを解除する。
- タイマーを解除する。
時間の管理は、
- フィールドが行を消したことを報告してきたら、タイマーの間隔を少し短くする。
- 規定の値まで短く(つまり速く)なったら、元に戻す。
操作に関係無く経過時間だけで速くするか、落としたら速くするか、行を消したら速くするか。 ちょっと悩んで、行を消したら速くすることにした。 複数行を消した場合は、その行数だけ速くする。
キー入力は、左移動を H ,右移動を K ,回転を J ,落下を space にしてみた。
// コントローラー
function Controller() {
var self = this;
var INTERVAL = {
INIT : 1000,
MIN : 100,
DIFF : 10
};
var score = new Score();
var field = new Field( this );
var timerId = null;
var interval = INTERVAL.INIT;
var started = false;
var movers = {
'H' : field.left,
'K' : field.right,
'J' : field.turn,
' ' : field.drop
};
this.start = function () {
if ( !started ) {
score.init();
field.init();
document.onkeyup = function ( e ) {
try {
movers[ String.fromCharCode( e ? e.keyCode : event.keyCode ).toUpperCase() ]();
} catch ( e ) {};
};
interval = INTERVAL.INIT;
started = true;
_proceed();
}
};
this.stop = function () {
document.onkeyup = null;
document.getElementById( 'start' ).disabled = true;
started = false;
};
this.report = function ( deletedLines ) {
score.countUp( deletedLines );
if ( ( interval -= ( INTERVAL.DIFF * deletedLines ) ) < INTERVAL.MIN ) {
interval = INTERVAL.INIT;
}
};
function _proceed() {
if ( started ) {
field.down();
timerId = setTimeout( _proceed, interval );
} else {
clearTimeout( timerId );
alert( "終了" );
document.getElementById( 'start' ).disabled = false;
}
}
}
キー入力の検査で、無効なキーの判定がまた例外まかせ。
// 例外を使わない判定処理
document.onkeyup = function ( e ) {
var mover = movers[ String.fromCharCode( e ? e.keyCode : event.keyCode ).toUpperCase() ];
if ( mover ) {
mover();
}
};
とした方がいいような気がする。 または素直に switch で。 だから、だったらそうしろよ、と。
ゲーム中フラグや開始ボタンの操作可否の設定は、ゲーム終了時の挙動を怪しくしないため。
タイマーの解除は、どうやら次の呼び出しを行わないようにするだけで、イベントキューにその時点で登録済みのイベント処理までは消さないらしい。 フィールドからの終了報告でタイマー解除して終了とすると、終了したつもりでいるその直後にタイマーの処理がちょいと動いてしまう時があるのだ。 その辺りの怪しい動きを抑えるために、タイマー処理の中でゲーム中フラグで処理を分け、タイマーの解除もここでやることにした。
最後に、画面読み込み完了イベントでの処理。 コントローラーを生成し、開始ボタンのクリックイベントでゲームを開始するように設定する。
// コントローラーの生成
window.onload = function () {
var controller = new Controller();
document.getElementById( 'start' ).onclick = controller.start;
};
これで一応の完成。
ところでコントローラーだが、完成したのは最後でも、仮版は最初にできてたりする。 仮版に実装しているのは、キー入力イベント関係。 ここだけでも先に作っておくと、早い段階で動きの確認ができていいんだよね。 あと、ちょっとした感動も。 ゲームにはならなくても、キーボードをポチッと押して 「おお! 動く!」 とか、 「回る! 回るぞ!」 とか。 こんなちょっとした感動が、やる気を継続するのに効果的だったりするのだ。
テストプレイをしてるうちに思い出したのだが、かつて遊んだX68000版のテトリスには 「ステージ」 が有ったんじゃなかったかな。 ステージクリアすると、少しゴミが残った状態から次のステージが始まってたような気がする。 ステージが進むと、最初のゴミも増えていたような。 テトリミノの落下は連続的にだんだん速くなるんじゃなくて、ステージ毎に離散的に速くなっていったような。
まあ、いいか。 しかし、いい世の中になったよなぁ。 メモリの節約を考えなくても平気だし、少々無駄な処理があってもそれなりの速度で動いてくれるし。 油断すると堕落の一途。 いや、もう堕ちるところまで堕ちているのかも。 「ブラウザ上のちょっとした玩具なんだから、メモリリークとか気にしなくていいじゃん」 なんて思ってるもんな。
半年後にこのコードを見たら、また全部書き直したくなるんだろう。 それはそれで楽しみ。