スコアを表示する
あと少しで完成します。がんばりましょう。
スコアの表示をします。
変数scoreを作り、テキストとして表示します。
プログラムを変更しましょう。
追加した行は色を変えています。
let blockcnt = 0;
let score = 0;
// スコア
Crafty.e("Score, DOM, 2D, Text")
.attr({ x:10, y:3, w:280, h:20 })
.textFont({size:'20px', weight:'bold'})
.textColor('#eeeeee')
.text("score: 0");
// ボール
Crafty.e("Ball, 2D, Canvas, Color, Collision")
.color('#eeeeee')
.attr({x:100, y:200, w:10, h:10,
dX:Crafty.math.randomInt(2,5), dY:5 })
.onHit('Paddle', function () { // パドルに当たったらy軸の方向転換
this.dY *= -1;
if( blockcnt == 0 ){
initBlock();
}
})
.onHit('Block', function (hitDatas) { // ブロックに当たったら
this.dY *= -1;
console.log("block len="+hitDatas.length);
// 複数のブロックに当たったときを考慮して全部調べる
for( let i=0,l=hitDatas.length; i<l; i++ ){
hitDatas[i].obj.destroy();
blockcnt--;
score += 100;
Crafty("Score").text("score: "+score);
}
})
.bind('EnterFrame', function () {
if (this.x <= 0 || this.x >= 390){ // 画面の左右に当たれば跳ね返る
this.dX *= -1;
}
if (this.y < 10) { // 上の壁
this.dY *= -1;
}
if (this.y > 380) { // 下の壁
this.dY *= -1;
}
this.x += this.dX;
this.y += this.dY;
});
これまでに何度かスコアを入れてきたので問題ないですね。
スコアありの画面は次のようになります。
ゲームオーバーの表示
ゲームオーバーの表示をします。
ゲームオーバーの条件はただ1つ、パドルで跳ね返せないときです。
下の壁の跳ね返り処理を変更します。
// ボール
Crafty.e("Ball, 2D, Canvas, Color, Collision")
.color('#eeeeee')
.attr({x:100, y:200, w:10, h:10,
dX:Crafty.math.randomInt(2,5), dY:5 })
.onHit('Paddle', function () { // パドルに当たったらy軸の方向転換
this.dY *= -1;
if( blockcnt == 0 ){
initBlock();
}
})
.onHit('Block', function (hitDatas) { // ブロックに当たったら
this.dY *= -1;
console.log("block len="+hitDatas.length);
// 複数のブロックに当たったときを考慮して全部調べる
for( let i=0,l=hitDatas.length; i<l; i++ ){
hitDatas[i].obj.destroy();
blockcnt--;
score += 100;
Crafty("Score").text("score: "+score);
}
})
.bind('EnterFrame', function () {
if (this.x <= 0 || this.x >= 390){ // 画面の左右に当たれば跳ね返る
this.dX *= -1;
}
if (this.y < 10) { // 上の壁
this.dY *= -1;
}
if (this.y > 380) { // 下の壁
//this.dY *= -1; // 今後のテストの為に残す
Crafty.pause();
Crafty.e('2D, DOM, Text').attr({x:85, y:200, w:300})
.text("GAME OVER").textColor('#00ff7f')
.textFont({size:'36px', weight:'bold'});
}
this.x += this.dX;
this.y += this.dY;
});
Crafty.pause();で他の処理を止めています。
そのあとは GAME OVER を表示するだけです。
跳ね返りの処理は今後のために残します。
ゲームオーバーの画面は次のようになります。
プレイヤーによる開始
ゲームオーバーの作業中に、ちょっと「イラっ」としませんでしたか?
ボールがすぐに動き出し、気がついたらゲームオーバーになるからです。
ここはプレイヤーが操作して開始する方がベストですね。
ゲーム作りでは、
制作中に感じた違和感や不快感を逃さないようにしましょう。
これらの欠点を取り除くことで品質が上がっていきます。
今回の対策は、Z キーを押すまでボールを動かさないようにします。
動いているかをdXとdYとで調べます。
// ボール
Crafty.e("Ball, 2D, Canvas, Color, Collision")
.color('#eeeeee')
.attr({x:100, y:200, w:10, h:10, dX:0, dY:0})
.bind('KeyDown', function(e) {
if( e.key == Crafty.keys.Z ) {
if( this.dX == 0 && this.dY == 0 ){ // 停止状態なら移動開始
this.dX = Crafty.math.randomInt(2,5);
this.dY = 5;
}
}
})
.onHit('Paddle', function () { // パドルに当たったらy軸の方向転換
this.dY *= -1;
if( blockcnt == 0 ){
initBlock();
}
})
.onHit('Block', function (hitDatas) { // ブロックに当たったら
this.dY *= -1;
console.log("block len="+hitDatas.length);
// 複数のブロックに当たったときを考慮して全部調べる
for( let i=0,l=hitDatas.length; i<l; i++ ){
hitDatas[i].obj.destroy();
blockcnt--;
score += 100;
Crafty("Score").text("score: "+score);
}
})
.bind('EnterFrame', function () {
if (this.x <= 0 || this.x >= 390){ // 画面の左右に当たれば跳ね返る
this.dX *= -1;
}
if (this.y < 10) { // 上の壁
this.dY *= -1;
}
if (this.y > 380) { // 下の壁
//this.dY *= -1; // 今後のテストの為に残す
Crafty.pause();
Crafty.e('2D, DOM, Text').attr({x:85, y:200, w:300})
.text("GAME OVER").textColor('#00ff7f')
.textFont({size:'36px', weight:'bold'});
}
this.x += this.dX;
this.y += this.dY;
});
.attr({x:100, y:200, w:10, h:10, dX:0, dY:0})
今までここでdXとdYに移動量を入れてました。
初めは動かさないので0を設定します。
.bind(‘KeyDown’, function(e) {
if( e.key == Crafty.keys.Z ) {
今回は Z キーが押されたかを調べています。
if( this.dX == 0 && this.dY == 0 ){
dXとdYが0ならば動いていないと判断します。
ゲーム中なら移動量が入っているので、このif文の中は実行されません。
リトライの機能を入れる
いまゲームオーバーになると、再びプレイすることはできません。
また遊ぶにはページを更新することになります。
これは面倒なのでリトライできるようにします。
一番簡単な方法は、すべてを破棄してもう一度作り直すことです。
これをCrafty.sceneを使ってやってみましょう。
変更箇所は3つです。ただし、
これまで作ってきたゲーム部分をすべて scene の中に入れます。
Crafty.init(400,400, document.getElementById('game'));
Crafty.background('#111111');
Crafty.scene("main", function() {
// パドル
Crafty.e("Paddle, 2D, Canvas, Color, Multiway")
.color('#00ff00')
.attr({x:200, y:350, w:100, h:10})
.multiway(300, { LEFT_ARROW:180, RIGHT_ARROW:0, A:180, D:0 });
let blockcnt = 0;
let score = 0;
// スコア
Crafty.e("Score, DOM, 2D, Text")
.attr({ x:10, y:3, w:280, h:20 })
.textFont({size:'20px', weight:'bold'})
.textColor('#eeeeee')
.text("score: 0");
// ボール
Crafty.e("Ball, 2D, Canvas, Color, Collision")
.color('#eeeeee')
.attr({x:100, y:200, w:10, h:10, dX:0, dY:0})
.bind('KeyDown', function(e) {
if( e.key == Crafty.keys.Z ) {
if( this.dX == 0 && this.dY == 0 ){ // 停止状態なら移動開始
this.dX = Crafty.math.randomInt(2,5);
this.dY = 5;
}
if( this.y > 380 ){ // プレイ中でないならリセット
Crafty.pause(); // ポーズ解除
Crafty.scene("main");
}
}
})
.onHit('Paddle', function () { // パドルに当たったらy軸の方向転換
this.dY *= -1;
if( blockcnt == 0 ){
initBlock();
}
})
.onHit('Block', function (hitDatas) { // ブロックに当たったら
this.dY *= -1;
console.log("block len="+hitDatas.length);
// 複数のブロックに当たったときを考慮して全部調べる
for( let i=0,l=hitDatas.length; i<l; i++ ){
hitDatas[i].obj.destroy();
blockcnt--;
score += 100;
Crafty("Score").text("score: "+score);
}
})
.bind('EnterFrame', function () {
if (this.x <= 0 || this.x >= 390){ // 画面の左右に当たれば跳ね返る
this.dX *= -1;
}
if (this.y < 10) { // 上の壁
this.dY *= -1;
}
if (this.y > 380) { // 下の壁
//this.dY *= -1; // 今後のテストの為に残す
Crafty.pause();
Crafty.e('2D, DOM, Text').attr({x:85, y:200, w:300})
.text("GAME OVER").textColor('#00ff7f')
.textFont({size:'36px', weight:'bold'});
}
this.x += this.dX;
this.y += this.dY;
});
// ブロック
function initBlock() {
const col = ["red","orange","green","blue"];
for( let j=0; j<4; j++){
for( let i=0; i<8; i++ ){
Crafty.e("Block, 2D, Canvas, Color")
.color(col[j])
.attr({x:1+i*50, y:50+j*30, w:48, h:25});
blockcnt++;
}
}
}
initBlock();
});
Crafty.scene("main");
Crafty.scene(“main”, function() {
main というシーン名を付けました。
このシーンを実行するときにこの名前が必要になります。
シーンが切り替わるとき function の中身が破棄されます。
Crafty.scene(“main”);
この命令でシーン main を実行しています。
2箇所にあることが分かりますか?
プログラムの最後にあるのが、初ゲーム用です。
Z キーが押されたかチェックしている部分がリトライ用です。
Crafty.pause();
1回目はポーズになり、2回目は解除になります。
変更したらテストします。
ゲームオーバー後に Z キーを押せば、一瞬でブロックが戻り初期状態になります。
その後、また Z キーを押してゲームできるか確認しましょう。
最後の調整
最後にちょっと調整します。
今はパドルが長くてゲームが簡単です。
パドルを短くします。
// パドル
Crafty.e("Paddle, 2D, Canvas, Color, Multiway")
.color('#00ff00')
.attr({x:200, y:350, w:50, h:10})
.multiway(300, { LEFT_ARROW:180, RIGHT_ARROW:0, A:180, D:0 });
パドルを半分の長さにしました。
これでゲームが難しくなります。
ときどきパドル内で発生するボールの跳ね返りを直します。
これはボールがパドルに当たったとき、必ず上に移動するようにすれば解決です。
.onHit('Paddle', function () { // パドルに当たったらy軸の方向転換
//this.dY *= -1;
this.dY = -Math.abs(this.dY);
if( blockcnt == 0 ){
initBlock();
}
})
this.dY = -Math.abs(this.dY);
dY の符号を反転させるのではなく、マイナス値にするだけです。
絶対値を算出してからマイナスにします。
完成です
これで完成です。お疲れさまでした。
次は自分で考えたゲームを作ってみましょう。
何も思いつかない人は、このブロック崩しを改良するのはどうですか?
ブロックを小さくしてもっと沢山表示するとか
ブロックを固く(数回当てないと消せない)するのもいいですね。