実装編 その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);
}
}