テトリスをJavaScriptで作る 実装編(2)

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

次はStageクラス他

M. K. の紹介

IT屋さんです。プログラミングが大好きで今はJavascriptがお気に入りです。
カテゴリー: JavaScript, ゲーム作成, プログラミング   タグ: ,   この投稿のパーマリンク