スコアとゲームオーバー

スコアを表示する

あと少しで完成します。がんばりましょう。

スコアの表示をします。
変数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 キーを押すまでボールを動かさないようにします。
動いているかをdXdYとで調べます。

    // ボール
    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})

今までここでdXdYに移動量を入れてました。
初めは動かさないので0を設定します。

.bind(‘KeyDown’, function(e) {
if( e.key == Crafty.keys.Z ) {

今回は Z キーが押されたかを調べています。

if( this.dX == 0 && this.dY == 0 ){

dXdYが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 の符号を反転させるのではなく、マイナス値にするだけです。
絶対値を算出してからマイナスにします。

 

 

完成です

これで完成です。お疲れさまでした。

次は自分で考えたゲームを作ってみましょう。
何も思いつかない人は、このブロック崩しを改良するのはどうですか?
ブロックを小さくしてもっと沢山表示するとか
ブロックを固く(数回当てないと消せない)するのもいいですね。

 

 

『平凡ブロック崩し』

ゲームを作ってみよう

  1. 平凡ブロック崩しに挑戦
  2. パドル操作とボール移動
  3. 表示と当たり判定
  4. スコアとゲームオーバー