ボスを作ってみよう(攻撃編)

ボスを配置し、そのボスに当たり判定をつけ、さらに動きまでつけることが出来るようになりました。
では最後にボスに攻撃技を仕込みましょう。


setEnemy()メソッドを用いた方法

setEnemy()メソッドはsetEnemy(x座標,y座標,敵コード)と記述することで
指定した座標に敵を配置することができるメソッドです。
この座標はブロック単位毎の座標です。
敵コードは1〜40あり、16以降からは敵の攻撃のみを配置することができます。
詳しくはこちらをご覧ください。


座標を決めよう

ボスが静止している場合は固定位置にしても問題ありませんが、
ボスが動く場合、固定の位置に攻撃のみのコードを配置するのは不自然です。
しかし、ボスの座標はドット単位なので、ボスの座標をそのまま利用することはできません。


ではどうしたらよいのか

このドット単位の座標をブロック単位の座標に変換することで解決することができます。


変換方法教えろよ

急かすんじゃない!
ドット単位の座標はx座標の左端は0では無く17です。またy座標の最上部は0では無くもっと大きい値です。
「てめぇ…言ってることが違うじゃねぇか」と急かしたくなると思いますが、本当の話です。
サンプルプログラムではこのように記述しています。

nx = Math.floor(boss_x/32)-1;
ny = Math.floor(boss_y/32)-10;
1ブロック=32ドットなので、ボスの座標を示す変数を32で割り、Math.floor()というjavascriptで用意されている
もっとも小さい整数を返すメソッドを使います。
そうすることで無理矢理ブロック単位の座標に変換させることが出来ます。
が、先ほど言ったとおり、端が0でないので、ズレが生じます。特にy座標は相当ずれます。
そこでx座標は得られた値から1減らし、y座標では10減らしています。
この値を別の変数を用意し、その変数に代入します。
こうすることでブロック単位の座標に変換することができます。
この記述は福田さんが書かれたものなので、正確にブロック単位に変換されていると思います。


ブロック単位に変更出来たら

あとはこの値をうまく使ってsetEnemy()メソッドを使ってどんどん攻撃技を配置しましょう。


おい、馬鹿みたいに攻撃が出てきて止まらんぞ!

それはそうです。このままsetEnemy()メソッドを記述したら関数が呼び出される度にこのメソッドも実行され、
一定時間毎に敵を配置してしまい、どうすることもできません。


止め方?

これも条件を指定し、ある条件を満たしたときに敵を配置するというようにしてあげればできます。
そこでサンプルプログラムでは射撃カウンターboss_scという変数を使っています。


射撃カウンターってどう処理すればいい?

サンプルプログラムでは、潰れカウンター、射撃カウンター、爆発カウンターなどを使って次の行動に移るための
時間間隔を設定しています。
もともと用意されているものでは無く、自分で定義します。
例えば0.1秒毎に呼び出される関数内で射撃カウンターboss_scの値を関数が呼び出される度に1減らし、
この値が0以下になったときに敵を配置し、射撃カウンターの値を変更します。
例えば射撃カウンターboss_scの値が30だったとします。
この場合、0.1*30=3秒となり、3秒に1回、敵を配置します。
参考までにプログラム例

…
…
	else if(boss_jyoutai == 200) {
		boss_sc = 30;
		boss_jyoutai = 300;
	}
	else {
		//boss_jyoutaiの値は300です
		boss_sc--;
		if(boss_sc <= 0) {
			Applet1.setEnemy(nx,ny,16);
			boss_sc = 30;
	}
…
…
「…」はそれ以前、以降に書かれているプログラムの省略です。
上のプログラム例ではboss_jyoutaiの値が200の時に、ボスの射撃カウンターに30を代入し、
boss_jyoutaiの値を300に変更します。
すると次にこの関数が呼び出される時はboss_jyoutaiが200でない時の動作をします。
はじめに「boss_sc--;」という記述がでてきます。
これはデクリメントといい、変数boss_scの値を1減らします。これを別の書き方にすると
boss_sc = boss_sc -1;
となります。
何これ?どういうこと?と思われう方がおられるかと思います。
算数の「=」とプログラムの「=」の働きは違います。
算数の「=」はプログラムでは「==」と書きます。
プログラムで「=」と書くと、「代入」という働きになり、「等しい」という意味にはなりません。
if文の条件文をご覧ください。変数boss_jyoutaiのあとに「=」が2個ついていますよね?
これが「等しい」という意味になります。
プログラムのエラーでよくある例としてよく取り上げられることなので、しっかりと覚えておきましょう。
話はそれましたが、上の記述は、「変数boss_scの値を1減らした値を変数boss_scに変更する」という命令です。
関数が呼び出される度に値が減ると、やがて変数の値が0以下になります。
このときを条件にして、敵を配置するsetEnemy()メソッドを実行し、boss_scの値を30にします。
すると再び、boss_scの値が30から1ずつ減っていき、またboss_scの値が0以下になるので、同じ処理を一定時間毎に
繰り返し実行する処理を行うことができます。


まとめ

ドット単位の座標では扱えないメソッドが存在するので、ブロック単位の座標に変換する。
射撃カウンターを用いて短時間に連続して攻撃を出させないようにする。
ただし、これを使いたい場合は除く。

また、攻撃する状態を別に用意し、その中で処理することもできます。


インクリメントとデクリメント

最後にもう一度確認しておきたいことがあります。
今回デクリメントという言葉を使いました。実は踏み判定の時もこれを使っています。
ここでインクリメントとデクリメントの詳しい説明を行いたいと思います。

インクリメントは「a++;」「++a;」と書き、変数の値を1増やします。
デクリメントはその反対で変数の値を1減らし、「a--;」「--a;」と書きます。

インクリメント演算子:++
デクリメント演算子:--
しかしこの演算子を変数の前に置くか、後ろに置くかによって動作が若干違います。
特にインクリメント、またはデクリメントした値を別の変数に代入する時には注意が必要です。
次の表をご覧ください。
aの値の初期値を1とします。
処理aの値bの値
a++2-
++a2-
b=a++21
b=++a22
a--0-
--a0-
b=a--01
b=--a00
別の変数を用意し、その変数に代入する時には動作が違っているということが分かると思います。
まず前に演算子を置いた「b=++a」「b=--a」について説明します。
これを別の書き方にすると次のようになります。
/* b=++a */
a = a+1;	//aに1加算した値をaに代入
b = a;		//aの値をbに代入

/* b=--a */
a = a-1;	//aに1減算した値をaに代入
b = a;		//aの値をbに代入
となります。つまり先に加算、減算を行ってから、その値を変数bに代入していいます。
次に演算子を後ろに置いた「b=a++」「b=a--」について説明します。
これを別の書き方にすると次のようになります。
/* b=a++ */
b = a;		//aの値をbに代入
a = a+1;	//aに1加算した値をaに代入

/* b=a-- */
b = a;		//aの値をbに代入
a = a-1;	//aに1減算した値をaに代入
となります。先にaの値を変数bに代入してから加算、減算を行っています。
すると前者ではaとbの値が同じになり、後者ではaとbの値が異なります。
これで演算子を前に置くか、後ろに置くかで動作が異なるということが理解できたかと思います。