スポンサーリンク
当サイトにはプロモーションが含まれています
※広告で得た収益はすべて利用者さんに還元しています。

ゲーム開発室通信 Vol.13 ~弾幕インベーダーの解説その⑤~

ゲーム開発室
スポンサーリンク

インベーダーの誘導弾攻撃編

『弾幕インベーダー』の解説シリーズの5回目はインベーダーの誘導弾攻撃について見ていきたい思います。インベーダーの攻撃は4種類用意しており、どの段にいるインベーダーかによってどの攻撃をするのかをあらかじめ決めています。誘導弾を発射してくるのは上から3段目のオレンジ色のインベーダーです。誘導弾はその名の通り、自機に向かって追いかけてくるミサイル攻撃になっています。自機に向かって誘導されるミサイルのロジックに注目しながら処理の内容を見ていきましょう。


『弾幕インベーダーゲーム』で遊びたい方は以下よりアクセスしてください。


ソースコードはGitHub上にアップしています。


誘導弾制御の定義一覧

誘導弾制御に使用するグローバル変数一覧

配列tamaは各種ミサイルのオブジェクトを共通で管理する二次元配列です。誘導弾のオブジェクトはtamaの一次元配列の2番目の要素(tama[1])に格納しています。


誘導弾のクラス定義(BombBクラス)

誘導弾クラスもメソッドは用意せずにフィールドの情報だけを持った単純なクラスにしています。変数mxとmyが自機に向かって誘導されるようにするメインの変数です。自機はユーザーによって操作されるため、常にX座標は更新されます。よって変数mxとmyも常に更新する必要があります。

他のミサイルも同じですが、一度作成したオブジェクトはプログラムが終了するまで再利用するため、aliveフィールドを用意しています。画面上にミサイルが表示されている間はtrueとし、自機に当たったり、画面外に出たらfalseとしています。aliveフィールドがfalseのオブジェクトは再度、誘導弾が発射できるように制御しています。発射中か未発射かを管理するだけですので、Boolean型のフィールドにしています。


誘導弾オブジェクトの初期化

誘導弾のオブジェクトの初期化はjsファイルが読み込まれた際に行っています。

for (let j = 0; j <= 5; j++) {
  tama[1][j] = new BombB(false, "res/tamat_16x12.png");
}

誘導弾の配列は0~5までの6発分のオブジェクトをあらかじめ用意しています。他のミサイルと同様、この配列の要素数を変えることで同時に発射できる誘導弾の数を制御できます。


誘導弾制御の関数一覧

誘導弾を制御を主としている関数一覧は以下となります。


インベーダーの攻撃の抽選処理(shotchance関数)

他のミサイル同様に誘導弾もランダムに抽選を行っています。この抽選は10msに1回行われ、上から3段目(引数_iが2)にいるインベーダーを対象としています。

shotchance()
function shotchance(_i, _j){
  let ratio;
  switch (_i) {

    ~中略~

    //上から3段目のインベーダーのとき
    case 2:
      ratio = Math.random()*800000; //抽選
      if (ratio < stage*10 + 500 - tcount*10) {  //ステージ数と生き残っているインベーダーの数で抽選
        for (let k = 0; k < tama[1].length; k++) {
          if (!tama[1][k].alive) {  //ミサイルが未発射のとき
            tama[1][k].alive = true;  //ミサイルを発射状態にセット
            tama[1][k].x = teki[_i][_j].x + 12 + tx;  //ミサイルのx座標を対象となるインベーダーのx座標にセット
            tama[1][k].y = teki[_i][_j].y + 16;  //ミサイルのy座標を対象となるインベーダーのy座標にセット
            break;
         }
        }
      }
      break;

      ~中略~
  }
}

誘導弾を発射するときに、発射元になるインベーダーのX座標とY座標を誘導弾オブジェクトのX座標とY座標にコピーしています。これで特定のインベーダーから誘導弾が発射されるようにしています。インベーダーのX、Y座標を誘導弾のX、Y座標にコピーする際に「+ 12」や「+ 16」していますが、これはインベーダーの真ん中から誘導弾を発射するようにオフセットしている値です。


誘導弾制御のメイン関数

誘導弾は自機に向かって飛んでくるインベーダーのミサイルです。自機はユーザー操作によって常に位置が変わるため、誘導弾も自機に向かうように常に軌道を修正しながら移動するようにしています。

tamamoveb()
function tamamoveb(){
  for (let k = 0; k < tama[1].length; k++) {
    if (tama[1][k].alive) { //誘導弾が有効のとき
      if (!miss && !clear) {  //自機が被弾状態でない、かつ、クリア状態でない
        if (tama[1][k].y < 400) { //誘導弾のy座標が400pxより小さいとき
          let x_range = x + 20 - tama[1][k].x;  //自機のx座標との距離を取得
          let y_range = 470 - tama[1][k].y;  //自機のy座標との距離を取得
          let angle = Math.atan2(y_range, x_range); //自機と誘導弾とのラジアン角の取得
          tama[1][k].mx = Math.cos(angle)*2;  //自機に向かう角度でx座標の移動量をセット
          tama[1][k].my = Math.sin(angle)*2;  //自機に向かう角度でy座標の移動量をセット
        }
        //誘導弾の移動
        tama[1][k].y = tama[1][k].y + tama[1][k].my;
        tama[1][k].x = tama[1][k].x + tama[1][k].mx;
      }
      //誘導弾の描画
      tamaimg = new Image();
      tamaimg.src = tama[1][k].image;
      ctx.drawImage(tamaimg, tama[1][k].x, tama[1][k].y, 12, 12);
      //誘導弾の当たり判定
      if (tama[1][k].y >= 460 && tama[1][k].y <= 474) {
        if (tama[1][k].x >= x+14 && tama[1][k].x <= x+20) {
          hit();  //被弾処理
        }
      }
      //誘導弾が流れていったときの処理
      if (tama[1][k].y > 500) { //誘導弾のy座標が500pxを超えたとき
        tama[1][k].alive = false; //誘導弾を無効にする
      }
    }
  }
}
誘導弾の軌道修正処理

自機に向かって誘導しているロジックは以下です。

if (tama[1][k].y < 400) { //誘導弾のy座標が400pxより小さいとき
  let x_range = x + 20 - tama[1][k].x;  //自機のx座標との距離を取得
  let y_range = 470 - tama[1][k].y;  //自機のy座標との距離を取得
  let angle = Math.atan2(y_range, x_range); //自機と誘導弾とのラジアン角の取得
  tama[1][k].mx = Math.cos(angle)*2;  //自機に向かう角度でx座標の移動量をセット
  tama[1][k].my = Math.sin(angle)*2;  //自機に向かう角度でy座標の移動量をセット
}

自機に向かって弾を誘導するためには、誘導弾から自機へ向かう角度(θ)が分かれば、cosθとsinθを使って座標上を移動させることができます。では2点間の座標からどのように角度を求めればいいか?JavaScriptには2点で作る直線とX座標との間の角度を求めることができる「Math.atan2()」という便利なメソッドがあります。atan2()メソッドの概要は下図です。

2点間の角度を求めるatan2()メソッド
Math.atan2(y, x)

「atan2」は「アークタンジェント2」と読みます。Math.atan2()は、Math.atan2(y, x)に対して点(0, 0) から点(x, y)までの半直線と、正の x 軸の間の平面上での角度 (ラジアン単位) を返します。atan2()を使うとき、引数の順番に気を付けましょう。第一引数がY座標、第二引数がX座標の順番で実引数をセットしてあげる必要があります。

y点の y 座標
x点の x 座標

「atan2()」になぜ「2」が付いているかというと「atan()」というメソッドがあるからです。atan()の使い勝手の悪さを改善したのがatan2()です。atan()の詳細については割愛しますので、ご興味のある方は調べてみてください。

誘導弾を自機に向かわせる処理の前に「if (tama[1][k].y < 400) {」という条件を入れています。誘導弾のY座標が400を超えた場合は、もうそれ以上自機に向かって誘導しないようにするための条件です。最後まで誘導してしまうと自機に当たる確率が増えますので、ゲームの難易度を少し調整するための条件です。最後まで誘導したかったらこの条件を外せばOKです。


さいごに

今回はインベーダーの誘導弾の制御についてのコードを見てきました。自機に誘導させるロジックはお分かりいただけましたでしょうか?座標から角度を求めるatan2()はとても便利ですので、ゲーム作りには必須のライブラリです。この機会に覚えておくことをお勧めします。次回もインベーダー攻撃の続きで、ビームについて見ていく予定です。次回もお楽しみに!