はじめに
皆さんは弾幕シューティングといったゲームの種類を知っているでしょうか?「弾」といわれる図形が飛んでくるのを、ひたすら「自機」と呼ばれる操作可能な図形に当たらないように避けて相手を撃破するゲーム――――の中でも避けることに大きなフォーカスを当てたゲームの事です。本記事では弾幕シューティングを想定して書きますが、(筆者が特に弾幕シューティングが好きなだけ)一般的なシューティングゲームにも大体適用されます。
...この記事はクリスマスに公開されました......まぁ、弾幕シューティングが恋人みたいなものなので!
本記事ではJavaを使用しますが、Java特有の書き方はできるだけ省いていきます。
三平方の定理
「弾」と呼ばれる図形や、「自機」と呼ばれる図形は大体が円となっています。よって、「当たった」という判定は、二つの円が重なっているかという検査となります。円同士が重なっている...という検査は、一見難しそうに見えますが、円の中心同士の距離が、それぞれの半径の合計以下かという確認で済むのです。で、二点間の距離を求めるには...そうです、三平方の定理です!
**「自機」**の中心を(xp, yp)、半径をrpとし、
**「弾」**の中心を(xb, yb)、半径をrbとします。
この時、その求め方は、
public class CheckHit{
public static void main(String[] args){
final int xp = 1, yp = 3, rp = 3;
final int xb = 1, yb = 3, rb = 3;
int xdelta = xp - xb;
int ydelta = yp - yb;
int radiusSum = rp + rb;
//距離の 2乗が 半径の合計の 2乗よりも 小さい ならば
if(xdelta * xdelta + ydelta * ydelta <= radiusSum * radiusSum){
//ヒットした!
}else{
//ヒットしてない!
}
}
}
sine cosine
「弾」と呼ばれる図形はそれぞれ決められた角度、速さで動くものがほとんどです。では、ある点が、角度θ、速さvで進むとき、次はどの点に移動するのでしょうか?それを計算するには...三角関数なんですよね。(tangentは除く)
単位円(半径1の円)の上で、ある角度に進むとき、そのx座標はcosθ、y座標はsinθとなります。
で、ここで重要なのが、この進んだ距離が1ということです。つまり、これをv倍にして、最終的なx,y座標の進んだ長さは
x = vcosθ
y = vsinθ
になるということですね!(絶対図があったほうが良かった)
**「弾」**の中心を(xb, yb)、進む角度(degree)をangle、進む速さをvとします。
この時、その求め方は、
import static java.lang.Math.*;
public class BulletSpeed{
public static void main(String[] args){
final int xb = 1, yb = 3, angle = 90, v = 2;
//angleをラジアンに変更したもの
double rad = angle * PI / 180;
//xbにx方向に進んだ距離を加える
xb += (int)(v * cos(rad));
//ybにy方向に進んだ距離を加える
yb += (int)(v * sin(rad));
//計算完了
}
}
arctangent
「弾」と呼ばれる図形は、自機の方向へ真っすぐ飛んでくるものがあります。
では、ある点から、ある点までの角度を求めるには、どうすればいいでしょうか?そうです。三角関数...のうちでもarctangentさんです!
二点を斜辺の端とし、x軸、y軸にそれぞれ平行な辺を持つ直角三角形を書きます。求める角度(向き)をθとし、x軸、y軸それぞれに対応する辺の長さをdx,dyとします。この時、tanθ = dy / dxが成り立ちますよね。逆関数をとってθ = arctangent(dy / *dx)*となります。...なお、純粋なarctangentでは、x方向が0だとエラーが出ますので、大体の言語に用意されている、改善版関数atan2を使います。
**「自機」**の中心を(xp, yp)とし、
**「弾」**の中心を(xb, yb)とします。
この時、その求め方は、
import static java.lang.Math.*;
public class BulletAngle{
public static void main(String[] args){
final int xp = 1, yp = 3;
final int xb = 1, yb = 3;
int degAngle = (int)(atan2(yp - yb, xp - xb) / PI * 180);
//計算完了
}
}
レーザー(参考)
「弾」の中でも、「レーザー」と呼ばれるものは、当たり判定が長方形(傾きあり)となっています。長方形と、円が重なっているかを判定する...やっぱり三角関数なんですよね。(tangentは再び除く)
今回は細かい説明はややこしいので省きます。
「レーザー」の中心を( xl , yl )とし、向いている角度をangle、幅をlaserwidth、長さをlaserlengthとします。
「自機」の中心を( xp , yp )、半径をrpとします。
この時、その求め方は、(角度が90の倍数の場合には別計算)
import static java.lang.Math.*;
public class Laser{
public static void main(String[] args){
final int xl = 3, yl = 5;
final int xp = 6, yp = 3;
final int rp = 10;
final int angle = 30;
final int laserwidth = 10;
final int laserlength = 300;
int deltax = xl - xp;
int deltay = yl - yp;
double c = cos(angle * PI / 180);
double s = sin(angle * PI / 180);
//Width方向
double distance = abs(s *
deltax - c * deltay);
//Length方向
double distancel = abs(c * deltax + s * deltay);
if(distance <= laserwidth / 2.0 + rp &&
distancel <= lazerlength / 2.0 + rp){
//当たった!
}else{
//当たってない!
}
}
}
おわりに
まぁ、ほんの少しだけ紹介しましたが、これだけでも数学は弾幕シューティングにとって非常に重要な要素であることが分かってもらえたでしょうか。
弾幕シューティングは、やるのも、作るのも楽しいですよ!