2Dゲームで狙い撃ちとか追尾ミサイルを作る方法
今まで、業務アプリケーション的な話ばっかりしてたんですけど、趣向を変えて2Dゲームで角度指定して弾を打つ方法とか、追尾ミサイルを作る方法をご紹介します。
※数学をモリモリ使います。
パソコンの角度計算は基本的に『ラジアン角』を使う
ラジアン角ってわかりますか?
角度を表す単位の一つで、『角度』が1周360度まであるのに対し、360度はラジアン角で表すと2πとなります。
難しいことは置いておいて、ここで覚えるべきことは、たったの2つ!
- 角度からラジアン角に変換する方法
- ラジアン角から角度に変換する方法
角度からラジアン角に変換する方法
これを例に、javascriptで表現すると以下の通り
// 角度からラジアン角に変換する関数
function rad(deg) {
return deg*(Math.PI/180);
}
// 角度(30度)をラジアン角に変換
let radian = rad(30);
ラジアン角から角度に変換する方法
これを例に、javascriptで表現すると以下の通り
// ラジアン角を角度に変換する関数
function deg(rad) {
return rad*(180/Math.PI);
}
// ラジアン角(4)を角度に変換
let degree = deg(4);
斜めに弾を飛ばしたい
そして、斜めに弾を飛ばしたい場合はどうするか
弾の座標(x, y)を両方加算・減算したら、弾は斜めに動きます。
// 画面の描画は左上が原点となるため、
// 弾は右下に動く
x += 1;
y += 1;
// 弾は左下に動く
x += -1;
y += 1;
// 弾は右上に動く
x += 1;
y += -1;
// 弾は左上に動く
x += -1;
y += -1;
これで斜め移動できますね。
では、角度75度の向きに弾を動かしてください。どうしますか?
そう、直接座標の数字を実数値で変更しているだけだと、角度ベースの移動が難しいです。
0度、45度、90度、135度、180度などは簡単だけど、87度とか言われると難しいですよね?
これにもちゃんと計算式があります。
特定の角度のX軸移動量を求める
X軸移動量を求める場合、コサインを使います。
しばらく数学から離れていると、これだけ見てもなんそれ!ってなるので、プログラムで表します。
// 75度のx軸移動量を出す
let radian = rad(75); // ラジアン角に変換する
let move_x = Math.cos(radian);
x += move_x;
一旦角度をラジアン角に変換して、ラジアン角度をコサイン関数に入れてやります。
しかし、ここで取得できる値は、ベクトルの長さが1に正規化されているため、そのまま足すと、x軸y軸合わせても1しか移動できません。
そのため、取得した値に移動量をかけてやる必要があります。
let speed = 10;
// 75度のx軸移動量を出す
let radian = rad(75); // ラジアン角に変換する
let move_x = Math.cos(radian) * speed; // スピード10をかける
x += move_x;
特定の角度のY軸移動量を求める
Y軸移動量を求める場合はサインを使います。
Javascriptで記述すると以下のようになります。
let speed = 10;
// 75度のY軸移動量を出す
let radian = rad(75); // ラジアン角に変換する
let move_y = Math.sin(radian) * speed; // スピード10をかける
y += move_y;
こちらも先ほどと同じようにスピードをかけておきます。
X軸Y軸合わせて計算すると以下のような感じですね。
let speed = 10;
let radian = rad(75); // ラジアン角に変換する
x += Math.cos(radian) * speed;
y += Math.sin(radian) * speed;
これで弾や敵キャラを任意の角度に動かすことができるようになりました。
これだけでも移動のパターンが360種類できたことになりますよ!
でも、狙い撃ちや追尾しようと思ったら、任意の角度で移動できるだけじゃなく、相手の角度を求めることができないといけませんよね。
対象の座標(x, y)の角度を求める
次に、狙い撃ちなどをする場合、相手の角度を求める必要があります。
ゲームで相手の角度を求める場合、2パターンあって
- インベーダーゲームのようにキャラクターの向きが変わらない場合、画面上の角度を求めるだけ
- キャラクターが向きを変える場合、キャラクターの向きから相手がどの角度にいるかを求める必要がある
まず初めに前者での角度の求め方です。
角度を求めるにはアークタンジェントを使います。
こちらをプログラムで表すと以下の通りです。
// 角度を求める
function get_angle(x, y) {
return Math.atan2(y, x); // ここxとyが逆なので間違えないように
}
// 座標(20, 43)の角度を求める
let angle = deg(get_angle(20, 43)); // 得られる値はラジアン角なので、角度に変換する
これで角度も求めることができます。
狙った角度に弾を打てますね。
2点間の距離を求める場合はピタゴラスの定理
弾やミサイルと敵キャラクターとの当たり判定やプレイヤーキャラクターと敵キャラクターの当たり判定には、距離を求める必要がありますよね。
2点間の距離を求めるには、ピタゴラスの定理を使います。
これをJavascriptで表すと
// 2点間の距離を求める
function get_length(x1, y1, x2, y2) {
return Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));
}
// 自キャラ(x=10, y=42)と敵キャラ(x=20, y=35)の距離を求める
let length = get_length(10, 42, 20, 35);
詳しく説明すると、Javascriptには便利な関数が既に用意されているので、これを使います。
// 値をn乗した値を返してくれます。
Math.pow(値, 何条するか);
powを使用することで、乗算は簡単です。
(x2 – x1)の部分は自キャラの座標x1を原点(0)とし、原点から見た敵キャラの座標x2を出しています。
(y2 – y1)も同じく原点(0)から見た敵キャラのY座標を出しています。
// 平方根(二乗根)を返してくれます。
Math.sqrt(値);
そして、出来上がった(x²+y²)の値をルート2する部分はsqrtを使って、値を取得します。
数学の大事さがわかったか!
私は学生時代、三角関数や連立方程式なんか、どうせ人生で1度も使うことなんてないわ!って思ってました。
でも、今この仕事をしてて、めちゃくちゃ使ってます!
何のために勉強するかわからない勉強も、わかると複雑なゲームやAI(人工知能)が作れるようになると思うとワクワクしませんか?
しっかり勉強しよう!
もしお困りごとがありましたら、お問い合わせフォームよりご相談ください。