tmlib.js で今流行のフラットデザインを使ったタッチゲームを作ろう – Step02 ピースのタッチ処理とゲームクリア処理を実装しよう

phiary に引っ越しました. 毎日プログラミングやWebに関する情報を発信しています! RSS 登録してたまに覗いたり, tweet やハテブして拡散してもらえると幸いです.

step02

Pocket

今回は, ピースをタッチした際にひっくり返るようにして, 更にすべてタッチし終わったらスコアを表示するようにします.

この Step でなんとなくゲームとして遊べるようになります.

Table of contents

チュートリアル目次

前半

後半

up

サンプル

今回作るサンプルです.

タッチするとひっくり返ってグレイになるのがわかると思います. また, 全てタッチし終わると掛かった時間が alert されます.

up

ソースコード

main.js

スクリプト部分のコードです. 追加した箇所のコードはハイライトされています.

Piecedisable というメソッドを追加しています.

更に MainScenePiece を生成した際に, onpointingstart というプロパティに関数を登録しています.

これでタッチ時にこのメソッドが呼ばれるようになります.

詳しい解説は下へ.

/*
 * main.js
 */

/*
 * contant
 */
var SCREEN_WIDTH    = 680;              // スクリーン幅
var SCREEN_HEIGHT   = 960;              // スクリーン高さ
var SCREEN_CENTER_X = SCREEN_WIDTH/2;   // スクリーン幅の半分
var SCREEN_CENTER_Y = SCREEN_HEIGHT/2;  // スクリーン高さの半分

var PIECE_NUM_X     = 5;                // ピースの列数
var PIECE_NUM_Y     = 5;                // ピースの行数
var PIECE_NUM       = PIECE_NUM_X*PIECE_NUM_Y;  // ピース数
var PIECE_OFFSET_X  = 90;               // ピースオフセットX 
var PIECE_OFFSET_Y  = 240;              // ピースオフセットY
var PIECE_WIDTH     = 120;              // ピースの幅
var PIECE_HEIGHT    = 120;              // ピースの高さ

var FONT_FAMILY_FLAT= "'Helvetica-Light' 'Meiryo' sans-serif";  // フラットデザイン用フォント

/*
 * main
 */
tm.main(function() {
    // アプリケーションセットアップ
    var app = tm.app.CanvasApp("#world");       // 生成
    app.resize(SCREEN_WIDTH, SCREEN_HEIGHT);    // サイズ(解像度)設定
    app.fitWindow();                            // 自動フィッティング有効
    app.background = "rgba(250, 250, 250, 1.0)";// 背景色

    app.replaceScene( GameScene() );    // シーン切り替え

    // 実行
    app.run();
});

/*
 * ゲームシーン
 */
tm.define("GameScene", {
    superClass: "tm.app.Scene",

    init: function() {
        this.superInit();

        var self = this;

        // カレント数
        self.currentNumber = 1;

        // ピースグループ
        this.pieceGroup = tm.app.CanvasElement();
        this.addChild(this.pieceGroup);

        // 数字配列
        var nums = [].range(1, PIECE_NUM+1);  // 1~25
        nums.shuffle(); // シャッフル

        // ピースを作成
        for (var i=0; i<PIECE_NUM_Y; ++i) {
            for (var j=0; j<PIECE_NUM_X; ++j) {
                // 数値
                var number = nums[ i*PIECE_NUM_X+j ];
                // ピースを生成してピースグループに追加
                var piece = Piece(number).addChildTo(this.pieceGroup);
                // 座標を設定
                piece.x = j * 125 + PIECE_OFFSET_X;
                piece.y = i * 125 + PIECE_OFFSET_Y;
                // タッチ時のイベントリスナーを登録
                piece.onpointingstart = function() {
                    // 正解かどうかの判定
                    if (this.number === self.currentNumber) {
                        // クリアかどうかの判定
                        if (self.currentNumber === PIECE_NUM) {
                            // 結果表示
                            var time = (self.app.frame/self.app.fps)|0;
                            alert("GameClear: {0}".format(time));
                        }
                        self.currentNumber += 1;// インクリメント
                        this.disable();         // ボタン無効
                    }
                };
            }
        }
    },
});


/*
 * ピースクラス
 */
tm.define("Piece", {
    superClass: "tm.app.Shape",

    init: function(number) {
        this.superInit(PIECE_WIDTH, PIECE_HEIGHT);
        // 数値をセット
        this.number = number;

        this.setInteractive(true);
        this.setBoundingType("rect");

        var angle = tm.util.Random.randint(0, 360);
        this.canvas.clearColor("hsl({0}, 80%, 70%)".format(angle));

        this.label = tm.app.Label(number).addChildTo(this);
        this.label
            .setFontSize(70)
            .setFontFamily(FONT_FAMILY_FLAT)
            .setAlign("center")
            .setBaseline("middle");
    },

    disable: function() {
        this.setInteractive(false);

        var self = this;
        this.tweener
            .clear()
            .to({scaleX:0}, 100)
            .call(function() {
                self.canvas.clearColor("rgb(100, 100, 100)");
            }.bind(this))
            .to({scaleX:1, alpha:0.5}, 100)
    }
});

up

解説

タッチ判定を無効にする

setInteractionve() メソッドに false を渡して実行することで タッチ判定を無効化することができます.

こうすることで pointingstart, pointingmove, pointingend といった イベントが発火されなくなります.

disable: function() {
    this.setInteractive(false);

Tweener を使ってピースをタッチしたときにアニメーションさせよう

tmlib.js には tweener というクラスがあります.

これを使うことで簡単に複雑なアニメーションをさせることができます. ActionScript の Tweener 的なものですね.

SpriteShape, Label といったインスタンスの tweener プロパティとして使うことができます.

今回, tweener を使った処理について詳しく見て行きましょう.

this.tweener
    .clear()
    .to({scaleX:0}, 100)
    .call(function() {
        self.canvas.clearColor("rgb(100, 100, 100)");
    }.bind(this))
    .to({scaleX:1, alpha:0.5}, 100)

繋げて実行しているのでパッと見, 分かりにくいかもしれませんが 単純に tweener のメソッドを連続で実行しているだけです.

tmlib.js のクラスは基本チェーンメソッドが使えるよう実装してあるので, clear().to(...).call(...); といった形で繋げて実行することができます.

次に処理の方ですが, まず clear() メソッドを呼んで登録されているアニメーションタスクをクリアします.

this.tweener
    .clear()

その次に to() メソッドでアニメーションを登録しています. scaleX を 100 ms かけて 0 にします.

.to({scaleX:0}, 100)

その次に call で登録されたメソッドを呼びます. 今回は色を変更しています.

.call(function() {
    self.canvas.clearColor("rgb(100, 100, 100)");
}.bind(this))

最後にもう一度 scaleX を 1 にすることで 大きさを元に戻しています.

.to({scaleX:1, alpha:0.5}, 100)

これで一旦小さくなって色が変わって元に戻るという アニメーションが再生されます.

ひっくり返ってるように見えませんかね?w

Tweener クラスは他にも wait で処理を待ったり, move で座標を動かしたり, fadeOut でフェードアウトさせたりと便利な機能がたくさんあります.

色々いじって遊んでみてください.

ピースをタッチ時のイベントを登録しよう

setInteractive(true) を実行したインスタンスは, タッチ時に pointingstart というイベントが発行されます.

onpointingstart プロパティに代入した関数もしくは, addEventListener(function() {...}) と登録した関数が実行されます.

// タッチ時のイベントリスナーを登録
piece.onpointingstart = function() {
    ....
};

終了判定を実装しよう

最後に終了判定です.

onpointingstart で実行している処理について見て行きましょう.

// 正解かどうかの判定
if (this.number === self.currentNumber) {
    // クリアかどうかの判定
    if (self.currentNumber === PIECE_NUM) {
        // 結果表示
        var time = (self.app.frame/self.app.fps)|0;
        alert("GameClear: {0}".format(time));
    }
    self.currentNumber += 1;// インクリメント
    this.disable();         // ボタン無効
}

まず, 押されたピースの number プロパティが scene の currentNumber プロパティと 一致するかを判定します.

一致していれば currentNumber を次の数字にして, 押されたピースを disable() メソッドで無効化します.

self.currentNumber += 1;// インクリメント
this.disable();         // ボタン無効

更に, currentNumberPIECE_NUM, つまり最後のピースの数と一致すると 全て押し終わったということになるので終了として 掛かった時間を alert します.

// クリアかどうかの判定
if (self.currentNumber === PIECE_NUM) {
    // 結果表示
    var time = (self.app.frame/self.app.fps)|0;
    alert("GameClear: {0}".format(time));
}

経過時間は app.frame で取得できます. それを app.fps で割れば経過秒数を取得することができます.

これを (...)|0 で整数化して time を求めています.

var time = (self.app.frame/self.app.fps)|0;

更に前回の Step で学んだ format を使って alert メッセージを作成しています.

alert("GameClear: {0}".format(time));

up

まとめ

おさえておいて欲しいポイント

  • tmlib.js のクラスは基本チェーンメソッドが使える
  • Tweener というクラスで簡単にアニメーションできる
  • setInteractive() メソッドでタッチ判定の有効/無効を切り替えられる
  • 経過時間は app.frame で取得でるのでそれを app.fps で割ることで経過秒数を取得できる

up

これで前半の Step は終わりとなります. とても簡単ですよね♪

ここまでで詰まったりうまく動かないなどありましたら 気軽に @phi_jpまでご連絡ください.

次回は後半, ゲームに色々な演出を加えてより ゲームっぽくしていきます.

TRACK BACK URL

POST COMMENT

メールアドレスが公開されることはありません。

COMMENT