このチャプターの目次
今回の目標
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.to({x: blank.x, y: blank.y}, 200, "easeOutCubic")
.call(function() {
// 空白ピースをタッチされたピースの位置へ
blank.setPosition(touchX, touchY);
}).play();
}
},
});
// ピースクラス
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.to({x: blank.x, y: blank.y}, 200, "easeOutCubic")
.call(function() {
// 空白ピースをタッチされたピースの位置へ
blank.setPosition(touchX, touchY);
}).play();
}
},
tweenerの設定部分を説明します。見やすいように改行していますが、次にさせたい処理をチェインメソッドで繋げます。
tweener.to({x: blank.x, y: blank.y}, 200, "easeOutCubic")
- toの{}内にパラメータを設定します。今回は指定の位置(空白ピース)へ移動と設定しています。その他、拡大縮小、回転といったパラメータが同時に設定可能です。
- 次の200という数字は、{}内の処理をどれくらいの時間をかけて行うかの設定です。単位はミリ秒(1000分の1秒)です。
- 最後のeaseOutCubicは、イージングと呼ばれており、動きにエフェクトをかけることができます。
- callを使うことで、以下のように通常の処理を繋げて非同期で実行させることができます。
.call(function() {
// 空白ピースをタッチされたピースの位置へ
blank.setPosition(touchX, touchY);
}).play();
以上をまとめると
- タッチされたピースを空白ピースの場所に移動させる。
- 空白ピースをタッチされたピースの元位置に移動(入れ替え)させる。
という一連の処理になります。書き方に多少慣れが必要ですが、より複雑な動きもtweenerなら手軽に設定することができます。