インベーダーのビーム攻撃編
『弾幕インベーダー』の解説シリーズの6回目はインベーダーのビーム攻撃について見ていきたい思います。インベーダーの攻撃は4種類用意しており、どの段にいるインベーダーかによってどの攻撃をするのかをあらかじめ決めています。ビームを発射してくるのは上から2段目の赤色のインベーダーです。ビームはいきなり発射されるのではなく、エネルギーをチャージしてから発射されるようにしています。エネルギーの貯まり具合からビームが発射されるタイミングが分かりますので、プレイヤーはそれを見ながら避けることになります。誘導弾のような複雑なロジックはありませんが、どのようにビームを制御しているかを見ていきましょう。
『弾幕インベーダーゲーム』で遊びたい方は以下よりアクセスしてください。
ソースコードはGitHub上にアップしています。
ビーム制御の定義一覧
ビーム制御に使用するグローバル変数一覧
変数名 | 内容 |
---|---|
tama | インベーダーの各ミサイルのオブジェクトを管理する二次元配列 |
ビームのクラス定義(BombCクラス)
ビームはエネルギーのチャージ中と発射の2段階で制御しています。チャージ量はchargeフィールドで管理し、ビームの有効時間はlimitフィールドで管理しています。
フィールド名 | 型 | 場所 | 内容 |
---|---|---|---|
imagea | String | インスタンス | エネルギーチャージ中の画像のファイルパス |
imageb | String | インスタンス | ビームの画像のファイルパス |
alive | Boolean | インスタンス | ビームが発射中かどうか |
x | Number | インスタンス | ビームのX座標 |
charge | Number | インスタンス | エネルギーのチャージ量 |
limit | Number | インスタンス | ビームの有効時間 |
ビームは他のミサイルのように落ちてはこないのでY座標を管理するフィールドは用意していません。その代わりチャージしたり、一定期間ビームが有効だったりと他のミサイルにはないchargeやlimitといったフィールドを用意しています。
ビームオブジェクトの初期化
誘導弾のオブジェクトの初期化はjsファイルが読み込まれた際に行っています。
for (let j = 0; j <= 3; j++) {
tama[2][j] = new BombC(false, "res/tamaL4_32x24.png", "res/tamaL5_32x24.png");
}
誘導弾の配列は0~3までの4発分のオブジェクトをあらかじめ用意しています。他のミサイルと同様、この配列の要素数を変えることで同時に発射できるビームの数を制御できます。
ビーム制御の関数一覧
誘導弾を制御を主としている関数一覧は以下となります。
クラス名 | 内容 |
---|---|
shotchance() | インベーダーの攻撃の抽選 |
tamamovec() | ビーム制御のメイン関数 |
respone() | 自機のリスポーン処理(配列内のビームオブジェクトのaliveをすべてfalseにセット) |
インベーダーの攻撃の抽選処理(shotchance関数)
他のミサイル同様にビームもランダムに抽選を行っています。この抽選は10msに1回行われ、上から2段目(引数_iが1)にいるインベーダーを対象としています。
shotchance()
function shotchance(_i, _j){
let ratio;
switch (_i) {
~中略~
//上から2段目のインベーダーのとき
case 1:
ratio = Math.random()*200000; //抽選
if(ratio < stage*10 + 500 - tcount*10) { //ステージ数と生き残っているインベーダーの数で抽選
for(k = 0; k < tama[2].length; k++) {
let beemnow = false; //ビーム発射中でない状態をセット
if (!tama[2][k].alive) { //ミサイルが未発射のとき
tama[2][k].alive = true; //ビーム発射状態にセット
tama[2][k].x = _j; //ビームを発射するx座標をセット
tama[2][k].charge = 0; //チャージ中をクリア
tama[2][k].limit = 0; //リミットをクリア
break;
}
}
}
break;
~中略~
}
}
ビームを発射するときには、chargeとlimitフィールドをそれぞれゼロにクリアしています。
ビーム制御のメイン関数
ビームはチャージ中の状態と発射している状態があります。それぞれchargeフィールドとlimitフィールドで管理しています。
tamamovec()
function tamamovec(){
let xx = 0;
let yy = 0;
let body = 0;
for (let k = 0; k < tama[2].length; k++) {
if (tama[2][k].alive) { //ビーム有効のとき
body = tama[2][k].x; //ビームのx座標を取得
xx = teki[1][body].x + tx; //ビームを発射するインベーダーのx座標をセット
yy = teki[1][body].y; //ビームを発射するインベーダーのy座標をセット
if (!teki[1][body].alive) { //インベーダーが倒されたとき
tama[2][k].alive = false; //ビームの発射を止める
}
if (tama[2][k].charge < 40) { //ビームのチャージ中のとき
if (!miss && !clear) { //自機が被弾状態でない、かつ、クリア状態でない
tama[2][k].charge = tama[2][k].charge + 0.05 * stage; //ステージ数に応じたチャージを行う
}
//チャージ量に応じて大きさを変えて描画
tamaaimg = new Image();
tamaaimg.src = tama[2][k].imagea;
ctx.drawImage(tamaaimg, xx+16-tama[2][k].charge/2, yy+28-tama[2][k].charge/2, tama[2][k].charge, tama[2][k].chage);
} else if (tama[2][k].limit < 120 + stage * 20) { //制限時間を超えていないとき
if (!miss && !clear) { //自機が被弾状態でない、かつ、クリア状態でない
tama[2][k].limit++; //制限時間をカウントアップ
}
//ビームの描画
tamabimg = new Image();
tamabimg.src = tama[2][k].imageb;
ctx.drawImage(tamabimg, xx, yy+28, 24, 500);
if (xx >= x-4 && xx <= x + 20) { //自機に当たったとき
hit(); //被弾処理
}
} else {
tama[2][k].alive = false; //ビームを無効にする
}
}
}
}
チャージもビームの有効時間もどちらも単純なカウンタを使って制御しているだけですので、処理自体はシンプルだと思います。工夫している点は以下です。
- チャージのスピードをステージ数が増えるにしたがって短くしている
- チャージ中の画像をチャージ時間とともに大きくしている
実際にどのように制御しているかはコードを見れば一目瞭然ですね。
さいごに
今回はインベーダーのビーム攻撃の制御についてのコードを見てきました。エネルギーをチャージするという演出を加えることで、見た目にも面白く、またユーザーにとっては避け易くなるためゲームの難易度を調整するのに役立っています。ちょっとした工夫ですが、こういった工夫が良いUXへと繋がっていると思います。次回で4種類あるインベーダー攻撃の4つ目の解説となり、インベーダーの攻撃に関しては最後になります。星形で回転しながら飛んでくる弾幕攻撃について見ていく予定です。弾幕はゲームのタイトルにもなっているように4種類の攻撃の中で一番派手な攻撃となります。どのように制御しているのか?次回もお楽しみに!
配列tamaは各種ミサイルのオブジェクトを共通で管理する二次元配列です。ビームのオブジェクトはtamaの一次元配列の3番目の要素(tama[2])に格納しています。