言語村 トップページ






作成ノートトップページ






敵の弾の動かし方


シューティングゲームでは、
敵の弾が自機を狙って飛んでくるのをよく見ますよね。
今回はそれを簡単に行う方法を紹介します。
知ってるとアホみたいなことなんですが、
知らないと目からうろこなテクです。
ちなみに私は目からうろこな方でした。(汗)


では、まずシューティングゲームのソースを見てみましょう。

//---------------------------------------------------
// 弾を一定の座標に向けて飛ばす関数
//		hx,hy 飛ばす方向のX,Y座標
//---------------------------------------------------
void setMissileMove( int hx, int hy ) {
	// 弾の移動量を決める
	xx = hx - x;
	yy = hy - y;
	if ( Math.abs(xx) >= Math.abs(yy) ) {
		yy = Math.round( ( (float)yy/(float)Math.abs(xx) ) * 4 ); 
		xx = xx < 0 ? -4 : 4;
	} else {
		xx = Math.round( ( (float)xx/(float)Math.abs(yy) ) * 4 );
		yy = yy < 0 ? -4 : 4;
	}
	// 弾のデータをセット
	Shoot_t.setData( x + (size/2) - 4, y + (size/2) -4, xx, yy );
}

シューティングゲームのソースの1142行あたりに、
敵の弾を任意の方向に飛ばすための関数があります。
ソースはJavaで書いていますが、やってることは算数なんで
他の言語でも理解できると思いますよ。


まず基本的な問題として、
弾の座標をX座標だけ変化させると、どうなるでしょうか?
弾のX座標が変数 X の場合、 X = X + 1; だと、
右移動
右に動きます。

X = X - 1; だと、
左移動
左に動くのですが、解りますか?


弾の座標をY座標だけ変化させると、どうなるでしょうか?
弾のY座標が変数 Y の場合、 Y = Y + 1; だと、
下移動
下に動きます。

Y = Y - 1; だと、
上移動
上に動きます。


じゃあ、斜め45度に動かすにはどうすればいいでしょうか?
X = X + 1;
Y = Y + 1;
とすれば、
斜め45度移動
ってなりますよね。


45度じゃない斜めは、どう動かせばいいでしょうか?
X = X + 2;
Y = Y + 1; だと、
斜め30度まちがい
ってなりますよね。
でも、本当にこれでいいですか?
これだと、X座標は一気に2つワープしちゃいますよ?
斜め30度正解
こんな感じで少しずつ動かさないといけません。

X = X + 1;
Y = Y + 1/2;
にすれば、いいですよね。

この 1/2 はどこから出てきたかというと、
Xが2動いた時にYは1動く、
Xが1動いた時にYはいくつ動くか?という問題からです。
式で書くと、
2 : 1 = 1 : 1/2 ってな比の式になりますよね。


では、
斜め45度下
を少しずつ動かしたい時の式は
どうなるでしょうか?

X = X + 1/2;
Y = Y + 1;
になります。



では、ソースを見て解説しましょう。
上記のことをプログラムにしているだけです。
xx = hx - x;
yy = hy - y;
この部分では、弾を飛ばしたい方向(自機とか)のX,Y座標(hx,hy)と
敵のX,Y座標(x,y)から、お互いがどれぐらい離れているかを計算し、
その値を xx,yy に代入しています。
当然 hx が x より小さいと xx は負の値になりますよね。
それはつまり、左方向に弾を飛ばしたい、ということを
表しています。
yyが負の値だと、上方向に弾を飛ばすことになりますよね。

if ( Math.abs(xx) >= Math.abs(yy) ) {
Math.absというのは、Javaで絶対値を出す関数です。
つまり、xx と yy のどちらの距離が長いかを調べているのです。
で、xxの方が大きいと次の処理に行きます。
  yy = Math.round( ( (float)yy/(float)Math.abs(xx) ) * 4 );
(float)というのは、Javaで、整数しか使えない所を
小数も使えるように変化させる命令です。
この部分の割り算は答えがすべて1以下の値になるので、
このようなことをしています。
Javaは整数と小数の扱いをきっちり区別する言語なんで、
こういう時はちょっと面倒です。(汗)

  (float)yy/(float)Math.abs(xx)
で、xxが1動く時、yyはいくつ動けばいいかを計算しています。
1行全体では、さらにそれに4を掛けて、四捨五入して整数にしています。
なんで4を掛けたかというと、
下で解説していますが、xxを4ずつ動かすことにしているからです。
xx:yy = xx*4:yy*4 の比になりますよね。

  xx = xx < 0 ? -4 : 4;
この式は、3項演算子と言って、xxが0未満の時は-4、
そうじゃない時は4ということを表しています。
if ( xx < 0 ) {
  xx = -4;
} else {
  xx = 4;
}
と同じ意味です。
つまり、xxは、必ず右か左に4つずつ動くことにしています。

} else {
  xx = Math.round( ( (float)xx/(float)Math.abs(yy) ) * 4 );
  yy = yy < 0 ? -4 : 4;
}
これは、xxとyyと比べてyyの方が大きい時の処理です。
つまり、yyは上か下に4動かすのが確定していて、
xxをいくつ動かせば良いのかを計算しているのです。


これで、xx,yy の値が出ました。
弾をX,Y座標ごとにいくつずつ移動させていけば
目的地に到達するのかが出たことになります。
弾を動かす部分で、毎回弾の X,Y 座標に xx,yyずつ加えていけば
いいですよね。
ちなみに、このソースでは整数型の変数を使っているために、
途中で四捨五入をしているので命中精度は悪いです。
そこがまたシューティングにはいいバランスなのですが・・・。(笑)


今回行った計算では、正確な距離と時間から計算しているものでは
ありませんので、弾の移動が、縦か横のみの移動の時に一番遅く、
斜め45度の移動の時一番速くなるという欠点があります。
この問題を克服するには、3平方の定理を使って正確な距離を出し、
時間と距離から時速を計算する、ってな面倒な計算が
必要になります。
そんなわけで、ちょっとしたシューティングの弾移動には、
今回の計算でも十分使えると思いますよ。



作成ノートトップページ