「p5.js」というJavaScriptライブラリが最近気になっていたので、勉強も兼ねてちょっと遊んでみました。
そもそも「canvas」を利用した描画処理を簡単にプログラミングできるのが「p5.js」の特徴ですが、機能を拡張するライブラリがとても優秀で、ゲーム、音楽、Webアプリ、IoT…など、幅広いジャンルの開発ができるところに魅力を感じています。
そこで今回は、ゲーム用の拡張ライブリ「p5.play」を使って、シンプルな「ブロック崩し」を作ってみたいと思います!
用意するもの!
ゲームを作るのに必要なファイルは、「HTMLファイル」と「JavaScriptファイル」です。
HTMLファイルは、単純に「p5.js」関連のファイルを読み込んで、今回作る「ブロック崩し」のゲームファイル「app.js」を明記するだけでOK。
<!-- index.html --> <!doctype html> <html lang="ja"> <head> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1" /> <meta charset="UTF-8"> <title>p5サンプル</title> <script src="js/p5.min.js"></script> <script src="js/p5.play.js"></script> <script src="js/app.js"></script> </head> <body> </body> </html>
「p5.js」はコチラから、「p5.play.js」はコチラからダウンロードできます。
また、ダウンロードが難しい場合には、以下のURLから読み込むことも可能です。
<p5.js> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.21/p5.js"></script> <p5.play.js> <script src="http://p5play.molleindustria.org/lib/p5.play.js"></script>
ここまで準備出来たら、もう「index.html」は使いません。
あとは「app.js」ファイルを作成して、ゲームプログラミングを始めていきましょう!
「p5.js」の基本的な使い方!
まずは、「p5.js」を使うための「デフォルトテンプレート」がコチラ!
//app.js function setup() { //ここに初期設定を書く } function draw() { //ここに描画処理を書く }
「setup()」の初期設定は、主にcanvasの設定や画面配置などの初期化処理で、「draw()」の描画処理は、「ループ処理」になっているのでリアルタイムに変化させる処理を書いていくことが多いでしょう。
「p5.js」では、HTML5でお馴染みの「canvas」要素を使って描画するのですが、初期化処理に面倒な手続きは一切必要ありません。
基本はこれだけです!
//app.js function setup() { createCanvas(300, 250); background("#aaaaff"); }
まず、「canvas」の初期設定を行うので、「setup()」の中にコードを書いていきます。
「createCanvas()」は、文字通りcanvasのサイズを指定し描画する準備も同時に実行してくれる命令になっており、「background()」でcanvasの背景色を指定しています。
普通のJavaScriptで、canvasを準備するプログラミングの経験がある方は、このシンプルな命令にまずは驚くと思います。
これで、「index.html」をブラウザで開くと、青色のcanvas要素が確認できるはず。
次に、なにか「スプライト」を表示してみましょう!
これも簡単で、「createSprite()」という命令を使えばOKです。
createSprite(X座標, Y座標, 幅, 高さ);
これを、先ほどの「setup()」内に書いてみます。
//app.js function setup() { //canvasの生成 createCanvas(300, 250); //背景色の設定 background("#aaaaff"); //スプライトを生成 createSprite(100, 100, 50, 50); }
実は、これだけだとスプライトが表示されません。
そこで、必要になるのが「draw()」内に書くことになる「drawSprites()」という命令です。
//app.js function draw() { //スプライトの表示 drawSprites(); }
これを書いてあげることで、スプライトが表示されるようになります。
ちなみに、「createSprite()」を「setup()」ではなく、「draw()」内に書いてみると面白いことが起きます。
//app.js function draw() { //スプライトを生成 createSprite(100, 100, 50, 50); //スプライトの表示 drawSprites(); }
これで、ブラウザを確認すると、先ほどのスプライトがさまざまな色に変化します!
実は、「createSprite()」で生成したスプライトは、初期値としてランダムな色を持っているのです。そのため、ブラウザを更新するか、スプライトを生成する度に色違いのスプライトが表示されるようになっています。(もちろん初期値を設定することも可能…)
「draw()」はループ処理になっているので、ここに「createSprite()」を書くと、色違いのスプライトが毎回生成されるので、カラフルなスプライトが表示されたというわけです。
イベント処理を作ろう!
さて、基本的な描画処理が分かったところで、マウスやタッチ操作の入力を受け取る処理をプログラミングしてみましょう。
ここで必要になるのが、入力を受け取るための新しい関数です!
いくつかあるのですが、試しに「touchStarted()」という関数を使うと、こんな感じになります。
//クリックやタッチ操作の入力受け取り function touchStarted() { //ここに入力を受け取る処理を書く }
さらに、マウスでクリックした時の位置座標や、スマホを指でタッチした時の位置座標を知りたい場合には、あらかじめ用意されている「touchX」と「touchY」を使うことで取得できるようになっています。
これを、先ほど利用した「createSprite()」に適用してみたのがコチラ!
//app.js function touchStarted() { //ユーザーが指定した場所にスプライトを生成する createSprite(touchX, touchY, 50, 50); }
たったこれだけで、クリックかタッチをした場所にスプライトを生成して表示することができるのです!
これを応用すると、「touchMoved()」という関数と「line()」という命令を使うだけで、シンプルな「お絵かきアプリ」にすることも可能です。
//app.js //ドラッグやスワイプ操作の入力受け取り function touchMoved() { //ドラッグかスワイプで線を描く line(touchX, touchY, ptouchX, ptouchY); }
「line()」の「ptouchX」というのは、1フレーム前の「X座標」を取得できるコマンドで、「touchX」と併用することで「フリーライン」が描けるようになります。
ブロック崩しのゲーム画面を作ろう!
それでは、基本を抑えたところで、現在の「app.js」ファイルを以下のようにしておきます!
//app.js function setup() { //canvasの生成 createCanvas(300, 250); //ゲーム初期画面の生成 gameInit(); } function draw() { //背景色の設定 background("#3366ff"); //スプライトの表示 drawSprites(); } function touchMoved() { //ユーザーからの入力受け取り処理 } function gameInit() { //初期画面の生成処理 }
「gameInit()」に関してはこれから作っていくのですが、主に「ブロック崩し」ゲームの最初に表示する画面をプログラミングしていきます。
「setup()」に直接書いても良いのですが、把握しづらくなりそうなので一応分けておきます。
「gameInit()」の中身ですが、まずはボールを跳ね返す「バー」の部分を表示してみましょう!
これも「createSprite()」を使うのですが、ただ表示するだけでなく、マウスやタッチ操作で「バー」を動かす必要があります。
こういう時は、以下のように変数の中へ「スプライト情報」を格納します。
//app.js function gameInit() { var bar; // 「バー」のスプライト情報を格納 // 画面中央下段に「バー」を生成 bar = createSprite(width/2, height-20, 80, 10); }
「width・height」は、画面サイズを自動で取得してくれるので、上記のように書くと画面中央下段に「バー」が表示されるようになります。
そして、変数「bar」を利用することで、「touchMoved()」関数からコントロールすることが可能になります。(詳細は後述します…)
また、変数「bar」にスプライト情報が格納されたことによって、「bar」を利用してカラーや性質も変更することが可能です。
bar = createSprite(width/2, height-20, 80, 10); bar.shapeColor("#ffcc00"); bar.immovable = true;
「shapeColor()」を使うと、ランダムなカラーではなく任意の色に変更することができます。
また、「immovable」を「true」にすると、マウスやタッチ操作などで意図的に位置を変更しない限り「座標位置」が固定化されます。
こうしないと、「p5.play」で用意されている「bounce()」を使ってボールを跳ね返す時に、「バー」も一緒にどこかへ飛んでいってしまう現象が発生するので覚えておきましょう!
以上を踏まえれば、こんな感じになるはず!
//app.js function gameInit() { var ball; // 「バー」のスプライト情報を格納 //「バー」を画面中央下段に生成 bar = createSprite(width/2, height-20, 80, 10); //カラーを変更 bar.shapeColor("#ffcc00"); //座標位置を固定化 bar.immovable = true; }
ちゃんと表示されたでしょうか?
さらに、まったく同じ要領で「ボール」も表示させることができます!
//app.js var ball; //「ボール」のスプライト情報を格納 //「ボール」を画面中央に配置 ball = createSprite(width/2, height/2, 8, 8); //カラーを変更 ball.shapeColor = "#ffffff";
「ボール」は、「バー」や「壁」に当たると、跳ね返って飛んでいくようにしたいので、「immovable」は不要です。
あとは、「ブロック」の表示ですが少しコツがいります。
というのも、通常ブロックは複数存在するはずなので、1つずつ「createSprite()」で作ると大変なことになります。
そこで、一般的には「for()」を使って縦・横のブロックを一気に生成するカタチが取られます。
var block; for(var r=0;r<2;r++) { //縦のブロック数 for(var c=0;c<6;c++) { //横のブロック数 block = createSprite(ブロックのX・Y座標と幅・高さ); block.immovable = true; } }
これで、横方向に6個のブロックが縦に2段生成されて表示されます。
問題は「createSprite()」の中に書くX・Y座標ですが、こんな感じになるかと思います。
block = createSprite( // X座標 c * (ブロックの幅+ブロック間の隙間), // Y座標 r * (ブロックの高さ+ブロック間の隙間), ブロックの幅, ブロックの高さ);
こうすることで、「for()」でループさせているブロックの個数分だけ、上手く生成されるようになります。
ちなみに、p5.jsのスプライトは、座標の基準点が中央にあるので注意が必要です。
そのため、座標を「0, 0」の位置に設定するとブロックが半分隠れてしまうので、「offset値」としてあらかじめX・Y座標にプラスしておきます。
以上を踏まえると、ブロックの生成はこんな感じになります。
//app.js var block; // ブロックのスプライト情報を保持 var blockWidth = 40, blockHeight = 20; // ブロックの幅・高さ var blockMargin = 4, offset = 40; // ブロックの間隔とオフセット値 for(var r=0;r<2;r++) { // 縦のブロック数 for(var c=0;c<6;c++) { // 横のブロック数 // X・Y座標は(オフセット値+ブロック数*(ブロック幅/高さ+間隔)) block = createSprite(offset+c*(blockWidth+blockMargin), offset+r*(blockHeight+blockMargin), blockWidth, blockHeight); // 座標位置の固定化 block.immovable = true; } }
基本はこれでOKなんですが、今度はブロック1つずつにボールが当たった時の「当たり判定」をプログラミングするのが大変です。
そこで、p5.jsには「グループ」という便利な機能があって、複数のスプライトに同じ性質をまとめて追加したいような場合に使えます。
今回の例でいくと、複数のブロックをすべて1つのグループに登録し、このグループにだけ「当たり判定」処理を追加すればOKとなります。
先ほどのコードに追加するとこんな感じ!
var blockGroup; blockGroup = new Group; // グループを生成 for(var r=0;r<2;r++) { for(var c=0;c<6;c++) { block = createSprite(*********); block.immovable = true; // ブロックを1つずつグループに追加 blockGroup.add(block); } }
新しく「blockGroup」というグループを作成し、「blockGroup.add()」することですべてのブロックを1つのグループにまとめています。
こうすれば、以降は「blockGroup」に処理を追加するだけで、全部のブロックに反映されるようになるわけです。
これで、「ブロック崩し」の初期画面は完了です!
「ボール」を跳ね返してみよう!
それでは、いよいよゲームのアクション部分をプログラミングしていきます。
まずは、ボールを跳ね返すために、「バー」をマウスやタッチ操作で移動できるようにコントロールしてみましょう。
これは、あらかじめ用意しておいた「touchMoved()」関数の中に、マウスやタッチ操作の入力を受け取れる「touchX/touchY」を使えば簡単です。
コードはこれだけでOK!
// app.js //マウスのドラッグ、タッチ操作の入力受け取り function touchMoved() { // 横方向の入力を「バー」のX座標に変換 bar.position.x = touchX; // ブラウザ機能を無効化 return false; }
ブロック崩しの「バー」は、横方向にしか動かないので、今回は「touchX」のみ使います。
「touchX」を使えば、マウスやタッチで操作しているX座標を取得できるので、そのまま「バー」のX座標である「bar.position.x」に値を代入しているだけです。
また、最後に「return false」としているのは、スマホブラウザなどでタッチ操作すると、ブラウザ特有の機能である画面遷移や上下に「ビヨ~ン」と画面が伸びたりするのを無効化できるためです。
次に、ボールが「バー」に当たった時に跳ね返る処理が必要なのですが、p5.jsだと非常に簡単なコードで実現できます。
使うのは「bounce()」という命令のみ!
//「バー」接触時に跳ね返る ball.bounce(bar);
これだけで、「ボール(ball)」が「バー(bar)」に接触した時、ボールが上手く跳ね返ってくれるようになります。
同じ要領で、「ブロック」に接触した時にも跳ね返るようにするには以下のとおり!
//「バー」接触時に跳ね返る ball.bounce(bar); //すべての「ブロック」接触時に跳ね返る ball.bounce(blockGroup);
先ほど作成したグループの「blockGroup」だけに設定すれば、すべてのブロックに反映されるわけです。
あとは、ボールをアニメーション移動させるために、「速度」と「方向」を設定すれば良いでしょう。
ball.setSpeed(3, 90);
これで、「速度」が3に設定されて、90度下方向へボールが移動することになります。
以下に、実際のデモを用意したので「RunPen」ボタンをクリックしてみてください!(スマホの場合はコチラから!)
See the Pen http://codepen.io/webhacck/pen/YwEboR/'>p5.play BreakOutデモ1 by webhacck (http://codepen.io/webhacck'>@webhacck) on http://codepen.io'>CodePen.
実際に、マウスやタッチ操作で「バー」が動くことも確かめておいてください。
だいぶ「ブロック崩し」に近づいてきました。
しかし、このままだと「ボール」が上下に移動するだけなのでゲームとは言えません。そこで、「ボール」の方向をランダムに変化するようにしてみます。
ball.setSpeed(3, random(80, 100));
角度の箇所を「random()」を使い「80〜100」の数値で乱数が発生するようにしています。これにより、角度が80度から100度の範囲でランダムに方向が変化するわけです。
さらに、「ボール」が「ブロック」に接触したらブロックが消滅するように修正してみます。
これは「bounce()」の部分に、関数を挿入して「block」を「remove()」すればOKです!
// app.js //ブロックのグループに当たり判定を設定 ball.bounce(blockGroup, function(ball, block) { //ボールが接触した「block」を破棄する block.remove(); });
こうすれば、すべてのブロックに反映され、ボールが接触したら画面から消えるようになります。
実際のデモを以下で確認してみましょう!(スマホの場合はコチラから)
See the Pen http://codepen.io/webhacck/pen/xZPogJ/'>p5.play BreakOutデモ2 by webhacck (http://codepen.io/webhacck'>@webhacck) on http://codepen.io'>CodePen.
これで、かなり「ブロック崩し」に近づいたかと思います。
最後の仕上げ!
先ほどのサンプルを遊んでみると分かるのですが、「ボール」が画面外にはみ出して消えてしまう状態になっています。
そこで、簡単な解決方法として画面外に「当たり判定」用のスプライトを配置してみることにします。
コードにすると、こんな感じ!
// app.js var wallLeft, //左側の壁のスプライト情報保持 wallRight, //右側の壁のスプライト情報保持 wallTop; //上側の壁のスプライト情報保持 //左側の壁を生成 wallLeft = createSprite(-5, height/2, 10, height); wallLeft.immovable = true; //右側の壁を生成 wallRight = createSprite(width+5, height/2, 10, height); wallRight.immovable = true; //上川の壁を生成 wallTop = createSprite(width/2, -5, width, 10); wallTop.immovable = true;
そして、忘れないうちに「当たり判定」も設定しておきましょう!
// app.js ball.bounce(wallLeft); ball.bounce(wallRight); ball.bounce(wallTop);
これでOK!
おつかれさまでした。
全体として、コメントや改行を含めても100行以内で完成できました。
「ブロック崩し」ゲームとしては、これでも十分楽しめるでしょう。
実際の完成デモはコチラ!(スマホの場合はコチラから!)
See the Pen http://codepen.io/webhacck/pen/WrdNNQ/'>p5.play BreakOutデモ3 by webhacck (http://codepen.io/webhacck'>@webhacck) on http://codepen.io'>CodePen.
上記デモは、「ボール」が「バー」に当たった際に、少し角度調整をしていますので、ご興味ある方はコードも覗いてみてください!
また、余裕のある方は「ゲームオーバー処理」や「スコア処理」「音楽」なども組み込んでみると、さらに楽しくなるかと思います。
記事が少し長くなってしまいましたが、p5.jsのコツを掴みさえすれば驚くほど簡単にWeb作品が作れることが分かってくると思いますので、ぜひみなさんもチャレンジしてみてください!
さらに学習を深めるために…
実は、「p5.js」の解説本である【p5.jsプログラミングガイド】がAmazonで購入できるようになっています。
JavaScriptの基本的な解説から始まって、「p5.js」特有の扱い方やAPIの紹介が非常に丁寧に書かれています。
この本だけで、Webサイト、Webアプリ、スマホ対応、ゲーム作り、3D描画、IoT開発…など、幅広いジャンルをカバーできるのでとてもオススメの1冊となっています。
また、書籍を買うのが難しい場合は、「p5.js」の開発者が自ら登場して解説するYouTube動画が勉強になります。
とても陽気なオジサンで、英語ながら見ていて楽しい動画になっているのでありがたいです。
こちらの動画はシリーズになっており、「p5.js」の基本から応用・実践まで、かなりボリュームのある内容になっています。
ご興味ある方は、ぜひ参考にしてみてください!
コメント