弾を撃てるようにする
エイリアンから弾を撃てるようにします。
だんだんとプログラムが長くなってきました。
変更点を間違えないように注意しましょう。
<script>
// プレイヤー
Crafty.sprite(70,78,"alien2.png",{alien:[0,0]});
Crafty.e('2D, Canvas, alien, Gravity, Twoway, SpriteAnimation')
.attr({x:50, y:50})
.twoway(300)
.jumper(0) // ジャンプさせない
.reel('walk',300,0,0,2) // アニメ設定
.animate('walk', -1) // アニメ再生
.origin(35, 0) // 中心点のオフセットを指定
.bind('KeyDown', function(e) {
if( e.key == Crafty.keys.LEFT_ARROW ){
this.flip("X"); // 絵をx軸反転させる
}
if( e.key == Crafty.keys.RIGHT_ARROW ){
this.unflip("X");
}
if( e.key == Crafty.keys.Z ) { // 弾を発射
Crafty.e("2D, Canvas, Color, bullet") // 弾を作成
.attr({x:this.ox, y:this.oy,w:5, h:10}) // alien の座標を代入
.color("rgb(255, 0, 0)")
.bind("EnterFrame", function() {
this.y -= 5;
if( this.y < 100 ) { // この座標を超えると消える
//if( this.y < -20 ) {
this.destroy(); // 弾を消す
}
});
}
})
.bind('EnterFrame', function () {
if( this.x < 0 ) this.x = 0;
if( this.x > 500 - this.w ) this.x = 500 - this.w;
})
.gravity('Floor');
</script>
2箇所、追加しています。
zキーが押されたら bullet という名前の四角を作成して、上へ移動させます。

実行してzキーを押してみましょう。
下図のように途中で弾が消えます。

if( this.y < 100 ) {
この条件で弾を消す命令 this.destroy() を実行しています。
作成したオブジェクトを消す処理を、しっかり見てもらうために入れました。
確認したらこの行を消して、下の行のコメントを外します。
if( this.y < -20 ) {
これで画面の上まで弾が移動します。
origin の効果
先ほど追加した origin をコメントにしてみて下さい。
コメントにするとは、実行されない行にするということです。
次のようにします。
// .origin(35, 0) // 中心点のオフセットを指定

これでこの行は無視されます。
origin の働きは、基点にオフセットの値を足しています。
これを無効にすると基点がそのまま弾のセット座標になるため、下図のようにエイリアンの左上から発射されます。
(赤い枠はエイリアンの実際の描画サイズ)

origin(35, 0) とはつまり、x座標に35、y座標に0を足して中心点を変更しています。
確認が終わったら、origin のコメントを外して有効にしましょう。
エイリアンの座標を受けとる方法
エイリアンが弾を撃っているように見せるため座標が必要です。
エイリアンの座標を受け取っているのは次のところです。

this.ox、this.oy にはオフセットで変更した座標が入ってます。
this.x、this.y との違いは下図のようになってます。

実は今回のように座標を変えたいだけなら、計算でもOKです。
origin を使わないで、次のようにしても動きます。
.attr({x:this.x+35, y:this.y,w:5, h:10})
origin なんていらないじゃん
と思うかもしれませんが、絵を回転させたい時には必要です。
絵の中心で回転したいなら origin で中心点を変更しなければいけません。
UFOを表示する
UFO を表示します。
下のプログラムを入力しましょう。
プレイヤーのプログラム、地面のプログラム、その下に追加します。
<script>
// UFO
Crafty.sprite(168,76,"UFO.png",{UFO:[0,0]});
Crafty.e('2D, Canvas, UFO')
.bind("EnterFrame", function(){
this.x += 5;
if( this.x > 500 ) this.x = -200;
})
.attr({x:200, y:50});
</script>
500 とは画面のサイズです。
つまり、右側の画面の外まで移動したら x に -200 を代入しています。
-200 としているのは、UFOの幅を考え絵が隠れるようにするためです。
実行してみると、左からUFOが出現し右へ移動します。
これを繰り返します。

UFOに当たり判定を入れる
弾と当たったかを調べるため、UFOに当たり判定を入れます。
それからUFOが大きすぎたので半分のサイズに変更します。
<script>
// UFO
Crafty.sprite(168,76,"UFO.png",{UFO:[0,0]});
Crafty.e('2D, Canvas, UFO, Collision')
.bind("EnterFrame", function(){
this.x += 5;
if( this.x > 500 ) this.x = -200;
})
.attr({x:200, y:50, w:84, h:38}) // 1/2 に縮小する
.onHit('bullet', function () { // 弾に当たったら
console.log("hit"); // 弾が消えていない為、何度もヒットする
});
</script>
とりあえずコンソールを見ながら実行してみましょう。
そしてzキーを押して弾を撃ってください。
この時コンソールにzzzと表示されたら、ゲーム画面をクリックしてからテストしましょう。
コンソールを見ると、撃った弾よりもヒットした回数が多くなります。

これはUFOに当たった弾を消していないため、すれ違うまで何度も当たっていたのです。
変更したプログラムを見てみましょう。

.attr({x:200, y:50, w:84, h:38})
表示位置だけでなく、wとhを指定することでサイズも変更できます。
絵の大きさが w:168 h:76 なのでちょうど半分のサイズです。
Crafty.e(‘2D, Canvas, UFO, Collision’)
Collision を入れることで当たり判定を行います。
.onHit(‘bullet’, function () {
当たり判定の相手を指定して、当たった時の処理を登録します。
つまり function の中が実行されます。
当たった弾を消す
UFOに当たった弾を消します。
これで1発の弾は、1回のみ当たります。
UFOの.onHitのところを変更します。
<script>
// UFO
Crafty.sprite(168,76,"UFO.png",{UFO:[0,0]});
Crafty.e('2D, Canvas, UFO, Collision')
.bind("EnterFrame", function(){
this.x += 5;
if( this.x > 500 ) this.x = -200;
})
.attr({x:200, y:50, w:84, h:38}) // 1/2 に縮小する
.onHit('bullet', function (hitDatas) { // 弾に当たったら
for( let i=0, l=hitDatas.length; i<l; i++ ){
console.log("hit");
hitDatas[i].obj.destroy(); // 当たったobjを消す
}
});
</script>
変更点を見てみましょう。

hitDatas[i].obj.destroy()
destroy() を使って消しています。
ここでいう obj とは、UFO に当たった弾 bullet です。
.onHit(‘bullet’, function (hitDatas)
function のカッコ内に hitDatas を入れました。
こうすることで当たった時の情報を活用できます。
for( let i=0, l=hitDatas.length; i<l; i++ )
なぜ for を使うのかな?
と思う人いますよね。理由は、複数の弾が同時に当たる可能性があるからです。
これで当たったものはすべて削除します。
実行したらコンソールを確認しながらプレイします。
UFOに当たった数が正しいか、弾が消えるかをみましょう。
ここまでのプログラム
<script>
Crafty.init(500,400, document.getElementById('game'));
Crafty.background('#f0fff0');
// プレイヤー
Crafty.sprite(70,78,"alien2.png",{alien:[0,0]});
Crafty.e('2D, Canvas, alien, Gravity, Twoway, SpriteAnimation')
.attr({x:50, y:50})
.twoway(300)
.jumper(0) // ジャンプさせない
.reel('walk',300,0,0,2) // アニメ設定
.animate('walk', -1) // アニメ再生
.origin(35, 0) // 中心点のオフセットを指定
.bind('KeyDown', function(e) {
if( e.key == Crafty.keys.LEFT_ARROW ){
this.flip("X"); // 絵をx軸反転させる
}
if( e.key == Crafty.keys.RIGHT_ARROW ){
this.unflip("X");
}
if( e.key == Crafty.keys.Z ) { // 弾を発射
Crafty.e("2D, Canvas, Color, bullet") // 弾を作成
.attr({x:this.ox, y:this.oy,w:5, h:10}) // alien の座標を代入
.color("rgb(255, 0, 0)")
.bind("EnterFrame", function() {
this.y -= 5;
if( this.y < -20 ) {
this.destroy(); // 弾を消す
}
});
}
})
.bind('EnterFrame', function () {
if( this.x < 0 ) this.x = 0;
if( this.x > 500 - this.w ) this.x = 500 - this.w;
})
.gravity('Floor');
// 地面
Crafty.e('Floor, 2D, Canvas, Color')
.attr({x:0, y:370, w:500, h:30})
.color('#a0522d');
// UFO
Crafty.sprite(168,76,"UFO.png",{UFO:[0,0]});
Crafty.e('2D, Canvas, UFO, Collision')
.bind("EnterFrame", function(){
this.x += 5;
if( this.x > 500 ) this.x = -200;
})
.attr({x:200, y:50, w:84, h:38}) // 1/2 に縮小する
.onHit('bullet', function (hitDatas) { // 弾に当たったら
for( let i=0, l=hitDatas.length; i<l; i++ ){
console.log("hit");
hitDatas[i].obj.destroy(); // 当たったobjを消す
}
});
</script>