当たり判定とダメージ

エイリアンの移動範囲を限定する

いまエイリアンを操作して画面外へ出すと、戻ってくることができません。
画面の外には地面がないので落下しているのです。

画面の外へ出ないように変更します。

<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)    // アニメ再生
        .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");
            }
        })
        .bind('EnterFrame', function () {
            if( this.x < 0 ) this.x = 0;
            if( this.x > 500 - this.w ) this.x = 500 - this.w;
        })
        .gravity('Floor');
</script>

 

追加したのは下の方の4行です。

エイリアンの移動範囲を限定する

これも以前にやりましたね。
これで画面の左右から出なくなります。

ところでなぜ this.w を引いているか覚えてますか?
これは描画の基点が左上にあるからです。
幅を計算に入れないと、画面の外へ出てしまうのです。

エイリアンの幅を考えて範囲を決める

 

 

隕石の当たり判定

エイリアンと隕石の当たり判定を入れます。
ついに当たり判定です。ワクワクしますね。(私だけ?)

変更点が2箇所に分かれたので、全プログラムを載せます。

<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:5, y:50})   // 左端は安全エリア
        .twoway(300)
        .jumper(0)      // ジャンプさせない
        .reel('walk',300,0,0,2) // アニメ設定
        .animate('walk', -1)    // アニメ再生
        .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");
            }
        })
        .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:350, w:500, h:10})
        .color('#a0522d');
    // 隕石の絵を用意
    Crafty.sprite(45,40,"meteor.png",{meteor:[0,0]});
    // 隕石の発生
    Crafty.bind("EnterFrame", function(){
        if (Crafty.frame() % 20 == 0) obj_drop();
    });
    // 隕石を落とす
    function obj_drop(){
        Crafty.e("meteor, 2D, Canvas, Gravity, Collision")
            .attr({x:Crafty.math.randomInt(80, 500-55), y:-50})
            .checkHits("alien")
            .bind("HitOn", function (hitData) {
                Crafty.pause();     // すべてを止める
            })
            .bind('EnterFrame', function () {
                this.y += 10;
                if( this.y > 500 ){
                    this.destroy();
                }
            });
    }
</script>

 

1つ目の変更点です。
エイリアンの初期座標を 50 から 5 に変更しました。
隕石に当たらないエリアを作る必要があったので、ここにしました。

エイリアンの初期位置を左にする

 

2つ目の変更点です。
エイリアン alien との当たり判定を入れます。
とりあえず、当たったときシステムを止めます。

エイリアンとの当たり判定を隕石に入れる

Crafty.e(“meteor, 2D, Canvas, Gravity, Collision”)

オプションに Collision を入れて当たり判定をします。

.checkHits(“alien”)

当たり判定の相手を指定します。

.bind(“HitOn”, function(hitData){

当たったときの処理を無名関数で作ります。

Crafty.pause();

すべての処理を止めます。
あとでここに本当の処理を入れていきます。

.attr({x:Crafty.math.randomInt(80, 500-55), y:-50})

乱数を80から445までとして、隕石の発生範囲を狭めています。
つまりx座標80より左側は安全エリアになります。

 

 

いい加減な当たり判定

いま使っている当たり判定はわりといい加減です。
何度か実行してみれば分かりますが、当たっていなくても処理が止まります。

どうしてでしょうか。
その理由は四角同士で当たり判定をしているからです。

プログラムを一部、下のように変更してください。
分かる人だけでいいです。分からない人は説明だけ読みましょう。

<script>
    // エイリアンの当たり判定
    Crafty.e('2D, Canvas, alien, Gravity, Twoway, SpriteAnimation, Collision, WiredHitBox')
    
    // 隕石の当たり判定
    Crafty.e("meteor, 2D, Canvas, Gravity, Collision, WiredHitBox")
</script>

 

WiredHitBox を入れることで赤い四角が表示されます。
この四角は当たり判定をする範囲を表示します。
実行してみると次のようになります。

四角同士は当たっていても絵だけ見るとすき間がある

赤い四角同士が重なっています。つまり当たっています。
しかし、絵だけを見るとすき間があいてます。

 

なんか「ダメな仕組みだな」と思うかもしれません。
でも初心者のときは、これでいいんです。
もし厳密な当たり判定をしようとしたら、専用のツールが必要になったり
絵を沢山表示したら動きが遅くなったりすることもあります。

なので我慢しましょう。
というか、この仕組みだけでも工夫すれば
かなりの精度の当たり判定をすることも可能です。
そんなにむずかしいことではないので、いつか説明したいと思います。

この話はここまでです。
プログラムを変更した人は元に戻しましょう。

 

 

ダメージを表示する

隕石が当たってもすぐに終わらない、つまり、ダメージ制のゲームにします。

次のように変更しましょう。

<script>
    // 変数
    let damage = 0;
    // 隕石を落とす
    function obj_drop(){
        Crafty.e("meteor, 2D, Canvas, Gravity, Collision")
            .attr({x:Crafty.math.randomInt(80, 500-55), y:-50})
            .checkHits("alien")
            .bind("HitOn", function (hitData) {
                //Crafty.pause();     // すべてを止める
                damage += 10;
                Crafty("Damage").text("DAMAGE: "+damage);   // ダメージ表示更新
            })
            .bind('EnterFrame', function () {
                this.y += 10;
                if( this.y > 500 ){
                    this.destroy();
                }
            });
    }
    // ダメージの表示
    Crafty.e("Damage, DOM, 2D, Text")
        .attr({ x:20, y:20, w:180, h:20 })
        .textFont({size:'20px', weight:'bold'})
        .text("DAMAGE: 0");
</script>

 

実行したら、隕石に当たってみましょう。
ダメージの表示が10ずつ加算されたらOKです。

ダメージ表示をする

 

追加・変更した点を見ていきます。
やり方としては以前やったスコアと同じです。

ダメージの仕組みを追加したところ

let damage = 0;

ダメージの変数です。ここに加算していきます。

//Crafty.pause();

すべてを止める処理をコメントにします。
消してもいいです。私はテストで処理を止めたいときなどに使ったりするので、コメントにしていつでも使えるようにしています。

damage += 10;
Crafty(“Damage”).text(“DAMAGE: “+damage);

変数 damage に 10 加算して、テキスト表示しています。
それから、Damage はテキストを取り扱うための名前であり、
text() の中は表示する文字列を指定しています。
damage という単語が沢山ありますが混乱しないように。

Damageはテキストの名前

Crafty.e(“Damage, DOM, 2D, Text”)

ダメージ表示をするためのテキストを作成しています。
この行以下の設定で、表示位置やサイズを指定しています。

 

 

『隕石を避けて』

ゲームを作ってみよう

  1. 避けるゲームに挑戦
  2. エイリアンと隕石
  3. 当たり判定とダメージ
  4. キャンディとスコア