プレイヤーの爆弾
プレイヤーから爆弾を投下できるようにします。
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個までです。

爆弾の削除処理が正常に行われるか調べるために、削除座標 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点入ります。

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