実装編 その2です。
描画周りやTETRISクラスの説明です。
その1
ソース:tetris.js
ライブラリー:dq-rtg.js,dq-cancas.js(実際に動かすにはライブラリのコアコンポーネントとjQueryも必要です)。
できあがり
描画
イメージの読み込み
canvasのdrawImageに引き渡すimgオブジェクトですが、描画する前に読み込みが完了している必要があります。
そのため、ゲームエンジンを起動する前に全ての画像の読み込みの完了(loadイベントを使用)をチェックします。
var count = 0; function image_loaded(sender) { count++; if(count == 4) { TETRIS = new DQ.TETRIS(engine); TETRIS.initialize(); //engineはタイマーやキーイベントを管理 engine.start(); } } stageImg = new DQ.Image(DQ.page(), { width: 320, height: 320, src: "default/images/stage.png", onLoaded: image_loaded }); ...
canvasへの描画
前回も少し触れていますがcanvasへの描画はキャンバスクラスが担当していますので、その仕組みを説明しておきます。
キャンバス内では、指定されたFPSになるようにインターバルタイマーが発生してupdate
メソッドを呼び出されます。
update
メソッド内では、背景から順番にイメージを都度描画しています。
update: function () { if (this.drawing) { return; } this.drawing = true; var ctx = this.obj0.getContext("2d"), sort_compare = function (o1, o2) { return o1.zIndex - o2.zInde; } //準備 this._sprites.sort(sort_compare); //描画 !this._bg && ctx.clearRect(0, 0, this._size.width, this._size.height); //背景描画 this._bg && this._bg.draw(ctx, this._size); //スプライト描画 for (var i = 0; i < this._sprites.length; i++) { this._sprites[i].draw(ctx, this._size); } //前景描画 this._fg && this._fg.draw(ctx, this._size); //情報レイヤー描画 this._ig && this._ig.draw(ctx, this._size); this.drawing = false; },
TETRISクラス
TETRISクラスは、分析・設計では[<<場>>ゲーム]として抽出されたクラスです。
ゲームの進行を管理して、ステージやテトリミノなどのインスタンスを保持します。
初期化
インスタンスの作成時に背景やステージなどの設定を行います。
DQ.TETRIS = function (engine) { this._engine = engine; this.next = []; this._next7 = []; //ステージを作成 this.stage = new DQ.TETRIS.Stage(engine); } DQ.TETRIS.prototype = { initialize: function () { //ステージをスプライトとして追加 var stage = new DQ.Screen.Canvas.Sprite({ y: 0, x: 0, width: 320, height: 320, animation: false, numberOfPause: 0, image: stageImg.client[0] }); //背景を設定 var bg = new DQ.Screen.Canvas.BG({ width: bgImg.width(), height: bgImg._height, image: bgImg.client[0] }); this._engine._canvas.push(stage); this._engine._canvas.bg(bg); ... } }
テトリミノの作成
次のテトリミノの作成はwikiによれば、おおよそ以下の通りです。
あらかじめ重複しないように7つのテトリミノを決定します。そして、その7つのテトリミノを使い切ると次の7つのテトリミノを決定します。
このルールを元に次のテトリミノ決定ルーチンを作成します。
画面に表示する3つのテトリミノを取得するようになっています。
{ _next7: [], _genNextTetrimino7: function () { var used = [false, false, false, false, false, false, false]; this._next7 = []; for (var i = 0; i < 7; i++) { while (true) { var candidate = Math.floor(Math.random() * 7); if (i < 2 && (candidate == DQ.TETRIS.TetriminoFactory.DIR.S || candidate == DQ.TETRIS.TetriminoFactory.DIR.Z)) { continue; } if (used[candidate]) { continue; } used[candidate] = true; this._next7.push(candidate); break; } } }, genNextTetrimino: function () { //上限 MAX_NEXT分(3つ)だけ、次のテトリミノを作成する for (var i = this.next.length; i < DQ.TETRIS.MAX_NEXT; i++) { //7つのテトリミノがなければ作成 this._next7.length == 0 && this._genNextTetrimino7(); //テトリミノのインスタンスを作成 var mino = DQ.TETRIS.TetriminoFactory.create(this._next7.shift()); mino.setCanvas(this._engine._canvas); this.next.push(mino); } //待機位置へ移動 for (var i = 0; i < DQ.TETRIS.MAX_NEXT; i++) { this.next[i].to(DQ.TETRIS.NextPos[i].x, DQ.TETRIS.NextPos[i].y); } } }
落下テトリミノの作成
ゲームを開始すると、待機しているテトリミノが落下するテトリミノへと移行します。
{ shiftCurrent: function () { //待機中のテトリミノの先頭を落下中のテトリミノに移行 this.current = this.next.shift(); //ステージの最上部へ移動 this.current.to(DQ.TETRIS.STAGE_LEFT + DQ.TETRIS.CHIP_SIZE * 3, this.stage.top); if (this.hitTest(0, 0)) { //開始位置で衝突するなら終了 this.gameOver(); return; } //減った分だけ次のテトリミノを作成します。 this.genNextTetrimino(); //ゴーストを作成 this.ghost && this.ghost.remove(); this.ghost = DQ.TETRIS.TetriminoFactory.createGhost(this.current); this.ghost.setCanvas(this._engine._canvas); //落下中のテトリミノと位置合わせ this.ghost.to(DQ.TETRIS.STAGE_LEFT + DQ.TETRIS.CHIP_SIZE * 3, this.stage.top); this.moveGhost(); } }
落下処理
無操作の状態で、テトリミノはレベルに応じて落下していきます。
落下する量の計算は次の様になります。
{ calcGDY: function () { this.gdy = this.g * DQ.TETRIS.CHIP_SIZE / this.fps; //1chip分/framを最大値とする(hitTest()が対応していないので) this.gdy = this.gdy > DQ.TETRIS.CHIP_SIZE ? DQ.TETRIS.CHIP_SIZE - .1 : this.gdy; } }
TETRISクラスのupdate()
もFPSに従って定期的に呼び出されます。
{ update: function() { var dy = this.gdy; //当たり判定 if (this.hitTest(0, dy + DQ.TETRIS.CHIP_SIZE)) { //「遊び」に移行 this.game = DQ.TETRIS.Mode.Delay; this._elapse = 0; this.stopAccelerate(); //下に移動 this.current.move(0, dy); //描画上のずれを補正 this.current.fit(); } else { this.current.move(0, dy); } }