not good but great

プログラミング、アート、映画・本の感想について書きます。

Javascriptでブロック崩しを作ったよ

demo

GitHubにも公開してあります。
GitHub - naoyashiga/BreakBrick: using Paper.js

このブロック崩しを作るのが簡単である理由

ゲーム終了がない

ゲーム終了処理を作るのが面倒だったので、下に壁を作りました。下に壁があるためゲームオーバーになりません!!

開始、終了画面がない

いきなり始まります。終了もしないので、レンガ(ブロック)がすべてなくなっても終了しません。自ら終わる必要があります。

当たり判定が適当

後述しますが、弾丸の下辺中央座標がプレイヤー(操作バー)に当たると跳ね返ります。だからプレイヤーの角が弾丸の左端に当たっても、跳ね返りません。厳密な当たり判定は難しいと思ったので、簡単に書いてます。

使用したライブラリ

Canvasに簡単に描画するためにPaper.jsを使っています。
http://paperjs.org/

ですので、タイトルには「Javascript」と書いてありますが、正確にはJavascriptの拡張版「PaperScript」を使用しています。canvasを用いて、ベクターグラフィックをjsを簡単に描画できるようにしたライブラリです。

Paper.jsについては詳しくはこちらを見てください。
Paper.jsで使うPaperScriptの特徴と、直接Javascriptで実行するときの違い - not good but great

コード解説

以下に書いたコードについて説明していこうと思います。

レンガ

レンガを作成

//レンガを作成
var brick = new Rectangle();
brick.size = new Size(brickWidth, brickHeight);

//レンガのパス
var path = new Path.Rectangle(brick);
path.fillColor = '#444';

RectangleにSizeを指定して長方形を作成します。その長方形をパスに渡して、パスを作成します。

長方形やその他の図形の作り方はこちらを参照して下さい。
Paper.jsで長方形、円、角丸、多角形を描いてみる - not good but great

シンボル化

//レンガをシンボル化
var symbol = new Symbol(path);

同じ形のレンガを一つずつ作成するのはメモリを無駄に使います。メモリ節約のためにシンボル化します。

シンボルについてはこちらを参照して下さい。
Paper.jsでアイテムをたくさん複製したい時はSymbolを使おう - not good but great

レンガを並べる

//レンガを並べる
for(var y = 0;y < rows;y++){
    for(var x = 0;x < cols;x++){
        var center = new Point(x, y) * [brick.size.width + xMargin,brick.size.height + yMargin];
        var placedSymbol = symbol.place(center);
    }
}

symbol.placeで位置を指定すれば、どんどんシンボルが複製されていきます。

レンガを移動

//レンガのレイヤーを中央に移動
project.activeLayer.position = [view.size.width / 2,view.size.height / 4];

レンガのレイヤーごと一気に移動させます。

レイヤー構成

/*-----------------------------------
    レイヤー構成
    -レイヤー0
    --レンガ
    -レイヤー1
    --弾丸
    --プレイヤー
-----------------------------------*/
//レイヤー1を新規作成
var secondLayer = new Layer();
//レイヤー0のレンガを取得
var brickChildren = project.layers[0].children;

レンガ、弾丸、プレイヤーを扱いやすくするために、レイヤーに分けます。

レイヤーについてはこちらを参照して下さい。
Paper.jsでレイヤーの細かい指定、グループ化をやってみる - not good but great

弾丸

/*-----------------------------------
    弾丸
-----------------------------------*/
var bullet = new Path.Circle({
    center:[view.size.width / 2,view.size.height - 50],
    radius:bulletRadius,
    fillColor:"#f39c12"
});

弾丸は円で描画します。一つしかないのでシンボルにはしません。

プレイヤー

/*-----------------------------------
    プレイヤー(バー)
-----------------------------------*/
var playerRect = new Rectangle();
playerRect.size = new Size(100, 10);
playerRect.center = new Point(view.size.width / 2,view.size.height - 50);

var player = new Path.Rectangle(playerRect);
player.fillColor = '#444';

プレイヤー(操作バー)を長方形で描画します。

プレイヤーを動かす

/*-----------------------------------
    マウスの動きに合わせてプレイヤーが移動
-----------------------------------*/
function onMouseMove(event){
    player.position.x = event.point.x;
    //垂直方向の壁
    if(player.bounds.left < 0){//左の壁
        player.position.x = player.bounds.width / 2;
    }else if(player.bounds.right > view.size.width){//右の壁
        player.position.x = view.size.width - player.bounds.width / 2;
    }
}

onMouseMoveはPaper.jsに始めから用意されている関数です。

長方形の細かい位置を取得

playerは長方形となります。長方形の角の座標を取得するにはboundsが便利です。

center
topLeft
topRight
bottomLeft
bottomRight
leftCenter
topCenter
rightCenter
bottomCenter

上のように左端、右端、下辺の中央など細かい座標を取ることが出来ます。

・ドキュメント
http://paperjs.org/reference/rectangle/

当たり判定

//プレイヤーに対する弾丸の当たり判定
var hitResult = player.hitTest(bullet.bounds.bottomCenter);

if(hitResult){
    if(hitResult.type == "fill"){
        //跳ね返る
        vy *= -1;
    }
}

プレイヤーに対する弾丸の当たり判定のコードになります。前述の通り、boundsを使い下辺の中央座標を取得します。当たり判定にはhitTestを使用します。指定した下辺の中央座標がplayerに当たったら、trueを返します。細かい当たり判定を考えると、下辺の中央座標を当たり判定に使うのは違うとは思いますが、そこまでは今回は考慮しないことにします。

当たり判定についての以前書いたエントリはこちらになります。
当たり判定をしてドラッグでアンカーポイントを動かす - not good but great

これからの展望

やっぱり音がないと楽しくありませんね笑。WebAudioで効果音とか作れないんですかね。