プレイヤーの爆弾

プレイヤーの爆弾

プレイヤーから爆弾を投下できるようにします。

z キーを押したとき、爆弾を投下します。
爆弾が沈んでいくとき回転もさせましょう。

変数 enemyCnt は main シーンの中で宣言していますが、変数bomb1_cntは main シーンの外で宣言します。

let bomb1_cnt;  // プレイヤー爆弾カウンター

Crafty.scene("main", function() {
    let enemyCnt = Crafty.frame() +100;     // +100:敵の発生タイミング
    bomb1_cnt = 0;
    
    Crafty.e('Sky, 2D, Canvas').attr({x:0, y:0, w:600});
    Crafty.e('Player, 2D, Canvas, Twoway')
        .attr({x:250, y:45})
        .bind('EnterFrame', function () {
            if( this.x < 0 ) this.x = 0;
            if( this.x > 600-this.w ) this.x = 600 - this.w;
        })
        .bind('KeyDown', function(e) {
            if( e.key == Crafty.keys.Z ) {
                setPlayerBomb(this.x+this.w/2,this.y+30);   // プレイヤー爆弾
            }
        })
        .twoway(100, 0);       // 移動速度, jump速度
    Crafty.e('Sea, 2D, Canvas')
        .bind("EnterFrame", function(){
            if( Crafty.frame() > enemyCnt ){
                enemyCnt += Crafty.math.randomInt(30, 150);  // 敵の発生間隔
                setEnemy();
            }
        })
        .attr({x:0, y:85, w:600});
});

// プレイヤー爆弾
function setPlayerBomb(ix,iy){
    if( bomb1_cnt >= 5 ) return;
    bomb1_cnt++;

    Crafty.e('Bomb1, 2D, Canvas')
        .attr({x:ix, y:iy})
        .bind("EnterFrame", function(){
            this.y += 1;
            this.rotation += 2;
            if( this.y > 350 ){
                bomb1_cnt--;
                this.destroy();
            }
        });
}

 

実行してみましょう。
z キーで爆弾を投下できるようになりました。
なお、一度に投下できるのは5個までです。

zキーで爆弾を投下

爆弾の削除処理が正常に行われるか調べるために、削除座標 y を350としました。
画面下部で消えることを確かめましょう。

 

ここで1つ注意です。
変数bomb1_cntを関数の外で宣言しました。
これでどこからでも利用できるので便利です。
しかし、簡単に利用できるために、うっかり間違った使い方をしてしまう可能性も出てきます。(プログラムの規模が大きくなるほど間違いやすいです)

プログラミングに慣れてきたら、別の方法を考えてみましょう。
興味があるなら「グローバル変数 デメリット」で検索してみてください。

 

 

爆弾の中心点

爆弾は沈みながら回転しています。
よく見ると、違和感のある回転です。
これは回転の中心点が絵の中心ではないため発生しています。

中心点は左上がデフォルトです。
これを絵の中心にします。
ついでに、爆弾の削除座標 y を450に変更します。

// プレイヤー爆弾
function setPlayerBomb(ix,iy){
    if( bomb1_cnt >= 5 ) return;
    bomb1_cnt++;

    Crafty.e('Bomb1, 2D, Canvas')
        .attr({x:ix, y:iy})
        .origin("center")
        .bind("EnterFrame", function(){
            this.y += 1;
            this.rotation += 2;
            if( this.y > 450 ){
                bomb1_cnt--;
                this.destroy();
            }
        });
}

 

 

爆弾のストック表示

爆弾は同時に5つまで使えます。
これをプレイヤーに知らせるためにストック(残数)を表示します。

let bomb1_cnt;  // プレイヤー爆弾カウンター
let stock_cnt;  // 爆弾のストック

Crafty.scene("main", function() {
    let enemyCnt = Crafty.frame() +100;     // +100:敵の発生タイミング
    bomb1_cnt = 0;
    stock_cnt = 0;
    
    Crafty.e('Sky, 2D, Canvas').attr({x:0, y:0, w:600});
    Crafty.e('Player, 2D, Canvas, Twoway')
        .attr({x:250, y:45})
        .bind('EnterFrame', function () {
            if( this.x < 0 ) this.x = 0;
            if( this.x > 600-this.w ) this.x = 600 - this.w;
        })
        .bind('KeyDown', function(e) {
            if( e.key == Crafty.keys.Z ) {
                setPlayerBomb(this.x+this.w/2,this.y+30);   // プレイヤー爆弾
            }
        })
        .twoway(100, 0);       // 移動速度, jump速度
    Crafty.e('Sea, 2D, Canvas')
        .bind("EnterFrame", function(){
            if( Crafty.frame() > enemyCnt ){
                enemyCnt += Crafty.math.randomInt(30, 150);  // 敵の発生間隔
                setEnemy();
            }
            stockBomb();
        })
        .attr({x:0, y:85, w:600});
});

// 爆弾のストック表示
function stockBomb(){
    let cnt = bomb1_cnt + stock_cnt;
    while( cnt < 5 ){
        Crafty.e('Bomb1, Stock, 2D, Canvas')
            .attr({x:260+stock_cnt*20, y:10, rotation:90, no:stock_cnt});
        stock_cnt++;
        cnt++;
    }
}

// プレイヤー爆弾
function setPlayerBomb(ix,iy){
    if( bomb1_cnt >= 5 ) return;
    bomb1_cnt++;

    Crafty("Stock").each(function () {  // ストック表示を1つ消す
        if( this.no == stock_cnt-1 ){
            stock_cnt--;
            this.destroy();
        }
    });

    Crafty.e('Bomb1, 2D, Canvas')
        .attr({x:ix, y:iy})
        .origin("center")
        .bind("EnterFrame", function(){
            this.y += 1;
            this.rotation += 2;
            if( this.y > 450 ){
                bomb1_cnt--;
                this.destroy();
            }
        });
}

 

実行しましょう。
画面の上部に爆弾が表示されます。

爆弾のストック表示

まず表示の説明です。
投下中の爆弾数bomb1_cntと足して5より少ないとき表示します。

rotation:90 にして縦に表示しています。
変数noを作り stock_cnt を入れておきます。
何番目に表示されているのか分かるので、消すときに使います。

// 爆弾のストック表示
function stockBomb(){
    let cnt = bomb1_cnt + stock_cnt;
    while( cnt < 5 ){
        Crafty.e('Bomb1, Stock, 2D, Canvas')
            .attr({x:260+stock_cnt*20, y:10, rotation:90, no:stock_cnt});
        stock_cnt++;
        cnt++;
    }
}

Crafty.e(‘Bomb1, Stock, 2D, Canvas’)

オブジェクトを作るとき Stock という名前を追加しておきます。
これもまた消すときに使います。

 

消す処理です。
爆弾を投下させる処理の中で、ストック表示を1つ消します。

Stockの名前を持つオブジェクトの中から、一番右側に表示されている爆弾を見つけて消しています。

    Crafty("Stock").each(function () {  // ストック表示を1つ消す
        if( this.no == stock_cnt-1 ){
            stock_cnt--;
            this.destroy();
        }
    });

 

 

爆弾の当たり判定

爆弾と敵の当たり判定を入れます。

当たり判定には Collision ですね。
今回はだいたいの判定でいいので、爆弾のみ入れます。

// プレイヤー爆弾
function setPlayerBomb(ix,iy){
    if( bomb1_cnt >= 5 ) return;
    bomb1_cnt++;

    Crafty("Stock").each(function () {  // ストック表示を1つ消す
        if( this.no == stock_cnt-1 ){
            stock_cnt--;
            this.destroy();
        }
    });

    Crafty.e('Bomb1, 2D, Canvas, Collision')
        .attr({x:ix, y:iy})
        .origin("center")
        .onHit('Enemy', function (hitDatas) {  // 敵に当たったら
            for( let i=0, l=hitDatas.length; i<l; i++ ){
                let obj = hitDatas[i].obj;     // 当たった相手
                Crafty.e('Explosion, 2D, Canvas')   // 爆発演出
                    .attr({x:obj.x, y:obj.y, cnt:20})
                    .bind("EnterFrame", function(){
                        this.cnt--;
                        if( this.cnt%4 <2 ){
                            this.flip("X");
                        } else {
                            this.unflip("X");                        
                        }
                        if( this.cnt == 0){
                            this.destroy();
                        }
                    });
                obj.destroy();      // 当たったobjを消す
            }
            bomb1_cnt--;
            this.destroy();
        })
        .bind("EnterFrame", function(){
            this.y += 1;
            this.rotation += 2;
            if( this.y > 450 ){
                bomb1_cnt--;
                this.destroy();
            }
        });
}

Collision については以前の講座で説明しているので省略します。

当たった敵を消す前に、その敵の座標を使って爆発オブジェクト Explosion を作っています。
変数 cnt に20を入れて、0になったら消滅させます。
途中、この cnt を使い2フレーム単位で絵を反転しています。
しょぼい演出なので、プログラミングに慣れてきたらちゃんとした演出を入れましょう。

 

それから、もっとしっかりした当たり判定をしたいのなら、敵 Enemy にも Collision を付けます。
そして、当たりの範囲を絵に合わせて作ります。
くわしくは『壁を飛び越せ』で説明しています。

 

 

スコアの表示

敵を倒したときのスコアを表示します。

まずは表示部分です。

let bomb1_cnt;  // プレイヤー爆弾カウンター
let stock_cnt;  // 爆弾のストック
let score;

Crafty.scene("main", function() {
    let enemyCnt = Crafty.frame() +100;     // +100:敵の発生タイミング
    bomb1_cnt = 0;
    stock_cnt = 0;
    score = 0;
    
    Crafty.e('Sky, 2D, Canvas').attr({x:0, y:0, w:600});
    Crafty.e('Player, 2D, Canvas, Twoway')
        .attr({x:250, y:45})
        .bind('EnterFrame', function () {
            if( this.x < 0 ) this.x = 0;
            if( this.x > 600-this.w ) this.x = 600 - this.w;
        })
        .bind('KeyDown', function(e) {
            if( e.key == Crafty.keys.Z ) {
                setPlayerBomb(this.x+this.w/2,this.y+30);   // プレイヤー爆弾
            }
        })
        .twoway(100, 0);       // 移動速度, jump速度
    Crafty.e('Sea, 2D, Canvas')
        .bind("EnterFrame", function(){
            if( Crafty.frame() > enemyCnt ){
                enemyCnt += Crafty.math.randomInt(30, 150);  // 敵の発生間隔
                setEnemy();
            }
            stockBomb();
        })
        .attr({x:0, y:85, w:600});
    // スコア
    Crafty.e("Score, DOM, 2D, Text")
        .attr({ x:10, y:3, w:280, h:20 })
        .textFont({size:'20px', weight:'bold'})
        .textColor('#222222')
        .text("SCORE: 0");
});

 

スコアの加算部分です。
先ほど組み込んだ、プレイヤー爆弾の当たり判定に入れます。

    Crafty.e('Bomb1, 2D, Canvas, Collision')
        .attr({x:ix, y:iy})
        .origin("center")
        .onHit('Enemy', function (hitDatas) {  // 敵に当たったら
            for( let i=0, l=hitDatas.length; i<l; i++ ){
                let obj = hitDatas[i].obj;     // 当たった相手
                Crafty.e('Explosion, 2D, Canvas')   // 爆発演出
                    .attr({x:obj.x, y:obj.y, cnt:20})
                    .bind("EnterFrame", function(){
                        this.cnt--;
                        if( this.cnt%4 <2 ){
                            this.flip("X");
                        } else {
                            this.unflip("X");                        
                        }
                        if( this.cnt == 0){
                            this.destroy();
                        }
                    });
                obj.destroy();      // 当たったobjを消す
                score += 100;
                Crafty("Score").text("SCORE: "+score);
            }
            bomb1_cnt--;
            this.destroy();
        })

 

実行してみます。
敵を倒すごとに100点入ります。

敵を倒したときのスコア表示

スコアの説明は、これまでに何度もやってきた事なので省略します。

 

 

『サブマリンバスター』

ゲームを作ってみよう

  1. サブマリンバスター挑戦
  2. タイトルとキャラ表示
  3. プレイヤーの爆弾
  4. 敵の爆弾と当たり判定