🐦
【phina.js】マリオのようなブロック破壊処理
鋭意執筆中
はじめに
子供の頃始めてスーパーマリオブラザーズをプレイした時、ブロックが壊せることが斬新で、必要のないところまで壊しまくったりしたものです。今回は、phina.js でその再現を試みてみました。
ブロックの動きを観察する
マリオのブロックの壊れ方を自分なりに観察してみましたが、大まかには以下のように捉えることができると思います。
- 4つのかけらに分かれる
- かけらのグラフィックは斜めになっている
- 上のかけらと下のかけらは違う角度で飛んでいる
スプライトを4つのかけらに分ける
この表現に対しては、元のスプライトを部分的に描画した4つの新たなスプライトを作ることにしました。
_marioLike: function(sprite) {
// 縦横の分割数
var divX = 2;
var divY = 2;
var image = sprite.image.domElement;
// 分割サイズ
var sizeX = sprite.width / divX;
var sizeY = sprite.height / divY;
// グリッド
var grid = phina.util.Grid(sprite.width, divX);
var srcRect = sprite.srcRect;
// 分割スプライト作成
(divX * divY).times(function(i) {
// インデックス位置設定
var xIndex = i % divX;
var yIndex = (i / divX).floor();
// 新規canvas作成
var canvas = phina.graphics.Canvas().setSize(sizeX, sizeY);
var x = srcRect.x + xIndex * sizeX;
var y = srcRect.y + yIndex * sizeY;
// canvasに描画
canvas.context.drawImage(image, x, y, sizeX, sizeY, 0, 0, sizeX, sizeY);
// 破片作成
var piece = phina.display.Sprite(canvas).addChildTo(this);
// 位置指定
var px = grid.span(xIndex) + sprite.x - piece.width / 2;
var py = grid.span(yIndex) + sprite.y - piece.height / 2;
piece.setPosition(px, py);
piece.rotation = 45;
}, this);
}
かけらのグラフィックは斜めにする
単純にかけらのrotationを45に設定しただけです。
上のかけらと下のかけらは違う角度で飛ばす
この辺りは、試行錯誤で調整していくほかありません。
// 破片を散らす
[[-120, 35], [-60, 35], [-150, 20], [-30, 20]].each(function(elem, i) {
var piece = this.children[i];
piece.physical.gravity.y = 4.6;
piece.physical.velocity = phina.geom.Vector2().fromDegree(elem[0], elem[1]);
}, this);
拡張ファイル化
汎用性を高めるためにphina.effect.Breakというクラスにしてみました。
サンプルコード
コードを見る
phina.globalize();
// アセット
var ASSETS = {
// 画像
image: {
'tiles': 'https://cdn.jsdelivr.net/gh/alkn203/assets_etc/tiles.png',
},
};
// 定数
var GRID_SIZE = 64; // グリッドのサイズ
// メインシーン
phina.define('MainScene', {
superClass: 'DisplayScene',
// コンストラクタ
init: function() {
// 親クラス初期化
this.superInit();
// 背景色
this.backgroundColor = 'skyblue';
var sprite = Sprite('tiles', GRID_SIZE, GRID_SIZE).addChildTo(this);
sprite.setPosition(this.gridX.center(), this.gridY.center());
sprite.setFrameIndex(4);
var self = this;
this.onpointstart = function() {
phina.effect.Break(sprite).addChildTo(self);
};
},
});
// メイン
phina.main(function() {
var app = GameApp({
startLabel: 'main',
// アセット読み込み
assets: ASSETS,
});
app.run();
});
runstantプロジェクト
Discussion