ゲームバランスと効果音の追加
ブロック崩しの基本的なプログラムは完成しましたが、このままでは単調であまりおもしろくありません。ルールが単純なミニゲームは、ちょっとした工夫や調整次第でおもしろさが変わります。
たとえば、アーケードゲームのブロック崩しでは、ボールを何回も打ち返しているとボールの速度が速くなっていきます。さらにゲームが進行すると、今度はパドルのサイズが小さくなります。ボールの速度が上がり、パドルのサイズが小さくなることでゲームの難易度がだんだん高くなっていくわけです。
特にアーケードゲームの場合、家庭用ゲームとは違って1回100円でプレイするわけですから、一定時間経過したらゲームオーバーになってもらわないと儲かりません。
今回は、パドルでボールを跳ね返すたびにボールの移動が速くなるように改造します。ただし、無制限に速度をあげるとゲームとして破綻してしまいますので、最高でも初期速度の3倍までとします。
以下のようにボールの速さを定義する変数「ball.speed」を用意します。
ball.speed = 1; // ★★★ボールの速さ
ボールとパドルの当たり判定の際に以下のような処理を追加して、ボールが加速するようにします。
// ★★★一回跳ね返すごとに移動速度を速くする。3倍まで。
ball.speed = ball.speed + 0.025;
if (ball.speed >= 3){ ball.speed = 3; }
ブロック崩しをさらに改造して効果音を鳴らしてみましょう。効果音は画像と同様にあらかじめファイルを読み込ませておきます。Sound.load()を使い、引数にサウンドファイルのURLを指定すると効果音のファイルを読み込めます。
var mySE1 = Sound.load("sound/se1.mp3");
var mySE2 = Sound.load("sound/se2.mp3");
実際に効果音を鳴らすにはplay()メソッドを使います。以下のようにすると、ボールとパドルが当たったときに効果音が鳴ります。
mySE2.play();
ただし、この方法では現在のところスマートフォンでは再生できないようです。Android 4.0の「ブラウザ」ではSound.load()の代わりにnew Audio()とすれば音は鳴りますが、かなり遅延します。
最後に、スマートフォンだけでなくパソコンでも遊べるように、カーソルキーによる操作にも対応させます。カーソルキーの入力があったら移動量を変数nに入れます。スマートフォン向けのgame.input.analogXはパソコンではundefinedとなるため、カーソルキーの入力がない状態ではX座標にundefinedが加算されてしまいます。そこで、isNaN()で値を調べ、値が数値でない場合は変数nに0を設定します。
var n = game.input.analogX / 4;
if (game.input.left){ n = -6; } // ★★★パドルを左に移動
if (game.input.right){ n = 6; } // ★★パドルを右に移動
if (isNaN(n)){ n = 0; } // ★★★パソコン用の対応
pad.x = pad.x + n; // パドルを移動
以上でブロック崩しの完成です。実際のプログラムはサンプル4です。なお、サンプル3から追加/変更した部分には★★★をコメントに入れてあります。
- 実際のゲームのURL
- http://www.openspc2.org/reibun/enchant.js/v0.4.3/asc/0001/04_breakblock_sound/
- 実際のゲームのURL(new Audio使用バージョン)
- http://www.openspc2.org/reibun/enchant.js/v0.4.3/asc/0001/05_breakblock_sound/
- 実際のゲームのURL(ややオブジェクト指向版)
- http://www.openspc2.org/reibun/enchant.js/v0.4.3/asc/0001/06_breakblock_obj/
■サンプル4
enchant(); // ライブラリーの初期化
window.onload = function(){
var game = new Game(240, 320); // 240×320画面(Canvas)を作成
game.fps = 60; // フレームレートの設定。60fpsに設定
// 画像データをあらかじめ読み込ませる
game.preload("images/pad.png","images/ball.png", "images/block.png");
game.rootScene.backgroundColor = "blue"; // ゲームの背景色を青色に設定
game.score = 0; // スコアを入れる変数を用意する
// スコアを表示するラベルを作成
var scoreLabel = new Label("SCORE : 0");
scoreLabel.font = "16px Tahoma";
scoreLabel.color = "white";
scoreLabel.x = 10; // X座標
scoreLabel.y = 5; // Y座標
game.rootScene.addChild(scoreLabel);
// ブロックの総数を入れるカウンタ変数
var count = 0;
// ブロックを格納する配列
var block = new Array();
// 消したブロックの総数を入れる変数
var total = 0;
// ★★★効果音を読み込み
var mySE1 = Sound.load("sound/se1.mp3");
var mySE2 = Sound.load("sound/se2.mp3");
// データの読み込みが完了したら処理
game.onload = function(){
// ボールの設定
var ball = new Sprite(16, 16);
ball.image = game.assets["images/ball.png"];
ball.x = 0; // X座標
ball.y = 130; // Y座標
ball.dx = 1.5; // X方向の移動量
ball.dy = 2.5; // Y方向の移動量
ball.speed = 1; // ★★★ボールの速さ
game.rootScene.addChild(ball);
// パドルの設定
var pad = new Sprite(32, 16);
pad.image = game.assets["images/pad.png"];
pad.x = game.width/2; // X座標
pad.y = game.height - 40; // Y座標
game.rootScene.addChild(pad);
// ブロックを描く
drawBlock();
// フレームイベントが発生したら処理
game.rootScene.addEventListener(Event.ENTER_FRAME, function(){
moveBall(); // ボールを移動させる
movePaddle(); // パドルを移動させる(キーボード対応)
hitCheck_paddle_ball(); // パドルとボールの接触判定
hitCheck_block_ball(); // ボールとブロックの接触判定
// =============== 各種処理 ==================
// ------------ ■ボールを移動させる -----------------
function moveBall(){
ball.x = ball.x + ball.dx * ball.speed; // ★★★X方向の移動量を加算
ball.y = ball.y + ball.dy * ball.speed; // ★★★Y方向の移動量を加算
// 画面外かどうか調べる
if ((ball.x < 0) || (ball.x > (game.width-ball.width))){ ball.dx = -ball.dx; }
if (ball.y < 0){ ball.dy = -ball.dy; }
// ボールが下まで行ったらゲームオーバー
if(ball.y > game.height){
game.stop();
alert("スコアは"+game.score+"点でした");
}
}
// ------------ ■パドルを移動させる -----------------
function movePaddle(){
var n = game.input.analogX / 4;
if (game.input.left){ n = -6; } // ★★★パドルを左に移動
if (game.input.right){ n = 6; } // ★★パドルを右に移動
if (isNaN(n)){ n = 0; } // ★★★パソコン用の対応
pad.x = pad.x + n; // パドルを左に移動
if (pad.x < 0){ pad.x = 0; } // 左端かどうか調べる
if (pad.x > (game.width-pad.width)){ pad.x = game.width-pad.width; } // 右端かどうか調べる
}
// ------------ ■パドルとボールの接触判定を行う -----------------
function hitCheck_paddle_ball(){
if (pad.intersect(ball)){
ball.dy = -ball.dy; // 接触した場合はボールのY方向の移動量を逆にする
ball.y = pad.y - ball.height - 1; // うまく跳ね返るように調整
game.score = game.score + 10; // スコアを加算(10点)
scoreLabel.text = "SCORE : "+game.score;
// ★★★一回跳ね返すごとに移動速度を速くする。3倍まで。
ball.speed = ball.speed + 0.025;
if (ball.speed >= 3){ ball.speed = 3; }
// ★★★効果音を鳴らす
mySE2.play();
}
}
// ------------ ■ブロックとボールの接触判定を行う -----------------
function hitCheck_block_ball(){
var flag = false;
for(var i=0; i<count; i++){
if (ball.intersect(block[i])){
ball.dy = -ball.dy; // 接触した場合はボールのY方向の移動量を逆にする
block[i].y = -9999; // 見えない場所に移動
game.score = game.score + 5; // スコアを加算(5点)
total = total - 1; // 総ブロック数から1を引く
if (total < 1){ // 全部消したか調べる
drawBlock(); // ブロックを再描画
}
flag = true;
}
}
scoreLabel.text = "SCORE : "+game.score;
// ★★★当たった場合には効果音を鳴らす
if (flag){ mySE1.play(); }
}
});
}
// 傾きセンサーを設定(Android/iOS共通)
window.addEventListener("deviceorientation", function(evt){
game.input.analogX = evt.gamma; // 横方向の傾斜角度
}, false);
// ゲーム処理開始
game.start();
// ------------ ブロックを描く -----------------
function drawBlock(){
count = 0; // ブロックの総数を示すカウンタを0にする
// ボールの設定を縦横の数だけ繰り返し生成
for(var y=0; y<5; y++){
for(var x=0; x<7; x++){
block[count] = new Sprite(24, 12);
block[count].image = game.assets["images/block.png"];
block[count].x = x * 32+12; // X座標
block[count].y = y * 18 + 30; // Y座標
game.rootScene.addChild(block[count]);
count = count + 1; // ブロックの総数を示すカウンタを増やす
}
}
total = count; // 消すブロックの総数を変数に入れる
}
}
処理速度が速くて難しいという人はフレームレートを30や15に落としてみてください。また、このプログラムを改良してオリジナルのブロック崩しを作成してもよいでしょう。ちなみに、ブロック崩しが正統に進化しパワーアップしたのがタイトーの「アルカノイド」です。どのように進化したのか研究してみるのもおもしろいと思います。
- アルカノイド
- http://ja.wikipedia.org/wiki/アルカノイド
- アルカノイド(YouTube)
- http://www.youtube.com/watch?v=g6wXuFNJgEQ