【phina.js】ゲーム作成チュートリアル(15パズル)(その5)【tweenerで移動処理】

※これまでのTipsはphina.js チュートリアル集にまとめています。

前回

前回の【phina.js】ゲーム作成チュートリアル(15パズル)(その4)【ピースの移動】では、ピースをタッチで移動できるようにしました。

今回の目標

phina.jsの目玉機能の1つであるtweenerを使ってピースの移動を滑らかにしてみます。

コードは以下のとおりです。

phina.globalize();
// 定数
var SCREEN_WIDTH = 640;            // 画面横サイズ
var SCREEN_HEIGHT = 960;           // 画面縦サイズ
var GRID_SIZE = SCREEN_WIDTH / 4;  // グリッドのサイズ
var PIECE_SIZE = GRID_SIZE * 0.95; // ピースの大きさ
var PIECE_NUM_XY = 4;              // 縦横のピース数
var PIECE_OFFSET = GRID_SIZE / 2;  // オフセット値
// メインシーン
phina.define('MainScene', {
  superClass: 'DisplayScene',
  // コンストラクタ
  init: function() {
    // 親クラス初期化
    this.superInit();
    // 背景色
    this.backgroundColor = 'gray';
    // グリッド
    var grid = Grid(SCREEN_WIDTH, PIECE_NUM_XY);
    // ピースグループ
    var pieceGroup = DisplayElement().addChildTo(this);
    var self = this;
    // ピース配置
    PIECE_NUM_XY.times(function(spanX) {
      PIECE_NUM_XY.times(function(spanY) {
        // 番号
        var num = spanY * PIECE_NUM_XY + spanX + 1;
        // ピース作成
        var piece = Piece(num).addChildTo(pieceGroup);
        // Gridを利用して配置
        piece.x = grid.span(spanX) + PIECE_OFFSET;
        piece.y = grid.span(spanY) + PIECE_OFFSET;
        // タッチを有効にする
        piece.setInteractive(true);
        // タッチされた時の処理
        piece.onpointend = function() {
          // ピース移動処理
          self.movePiece(this);
        };
        // 16番のピースは非表示
        if (num === 16) piece.hide();
      });
    });
    // 参照用
    this.pieceGroup = pieceGroup;
  },
  // 16番ピース(空白)を取得
  getBlankPiece: function() {
    var result = null;
    this.pieceGroup.children.some(function(piece) {
      // 16番ピースを結果に格納
      if (piece.num === 16) {
        result = piece;
        return true;
      }
    });
    return result;
  },
  // ピースの移動処理
  movePiece: function(piece) {
    // 空白ピースを得る
    var blank = this.getBlankPiece();
    // x, yの座標差の絶対値
    var dx = Math.abs(piece.x - blank.x);
    var dy = Math.abs(piece.y - blank.y);
    // 隣り合わせの判定
    if ((piece.x === blank.x && dy === GRID_SIZE) || 
        (piece.y === blank.y && dx === GRID_SIZE)) {
      // タッチされたピース位置を記憶
      var touchX = piece.x;
      var touchY = piece.y;
      // tweenerで移動処理
      piece.tweener.clear()
                   .to({x: blank.x, y: blank.y}, 200, "easeOutCubic")
                   .call(function() {
                     // 空白ピースをタッチされたピースの位置へ
                     blank.setPosition(touchX, touchY);
                   });
    }
  },
});
// ピースクラス
phina.define('Piece', {
  // RectangleShapeを継承
  superClass: 'phina.display.RectangleShape',
    // コンストラクタ
    init: function(num) {
      // 親クラス初期化
      this.superInit({
        width: PIECE_SIZE,
        height: PIECE_SIZE,
        cornerRadius: 10,
        fill: 'silver',
        stroke: 'white',
      });
      // 数字
      this.num = num;
      // 数字表示用ラベル
      this.label = Label({
        text: this.num + '',
        fontSize: PIECE_SIZE * 0.8,
        fill: 'white',
      }).addChildTo(this);
    },
});
// メイン
phina.main(function() {
  var app = GameApp({
    startLabel: 'main',
  });
  app.run();
});

[runstantで確認]

前回までに比べて移動が滑らかになったかと思います。
phina.jsでは、このような動きをtweenerという機能で実現できます。

コード説明

movePiece関数を以下のように変更します。

// ピースの移動処理
movePiece: function(piece) {
  // 空白ピースを得る
  var blank = this.getBlankPiece();
  // x, yの座標差の絶対値
  var dx = Math.abs(piece.x - blank.x);
  var dy = Math.abs(piece.y - blank.y);
  // 隣り合わせの判定
  if ((piece.x === blank.x && dy === GRID_SIZE) ||
      (piece.y === blank.y && dx === GRID_SIZE)) {
    // タッチされたピース位置を記憶
    var touchX = piece.x;
    var touchY = piece.y;
    // tweenerで移動処理
    piece.tweener.clear()
                 .to({x: blank.x, y: blank.y}, 200, "easeOutCubic")
                 .call(function() {
                   // 空白ピースをタッチされたピースの位置へ
                   blank.setPosition(touchX, touchY);
                 });
  }
},

tweenerの設定部分を説明します。
最初にclearで設定をリセットします。

    piece.tweener.clear()

見やすいように改行していますが、次にさせたい処理をチェインメソッドで繋げます。

                 .to({x: blank.x, y: blank.y}, 200, "easeOutCubic")

  • toの{}内にパラメータを設定します。今回は指定の位置(空白ピース)へ移動と設定しています。その他、拡大縮小、回転といったパラメータが同時に設定可能です。

  • 次の200という数字は、{}内の処理をどれくらいの時間をかけて行うかの設定です。単位はミリ秒(1000分の1秒)です。

  • 最後のeaseOutCubicは、イージングといって、動きにエフェクトをかけることができます。tweenerの使い方については、simiraaaaさんの記事[phina.js] Tweenerを使いこなそう! [Tweener 基本編]がとても参考になります。

callを使うことで、以下のように通常の処理を繋げて非同期で実行させることができます。

.call(function() {
  // 空白ピースをタッチされたピースの位置へ
  blank.setPosition(touchX, touchY);
});

以上をまとめると、タッチされたピースを空白ピースの場所に移動させた後、空白ピースをタッチされたピースの元位置に移動(入れ替え)という一連の処理になります。書き方に多少慣れが必要ですが、より複雑な動きもtweenerなら手軽に設定することができます。

今回はここまで

ここまでで、tweenerを使ってピースが滑らかに移動できるようになりました。
次回はゲーム的要素の追加で、よりゲームらしくしていきます。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.