[JavaScript] 2Dゲームにおける Math.atan2 の活用法

Math.atan2 とは何か(復習と定義)

Math.atan2(dy, dx) は、2D 空間で 原点 (0,0) から座標 (dx, dy) へのベクトル の角度をラジアンで返す関数。 引数 dy が Y 成分、dx が X 成分で、返り値は –π から +π の範囲になる。これにより、前後左右・斜めなどあらゆる方向に対応できる。

重要な特徴は:

  • 四象限すべてに正しく対応
  • dx が 0 のときも除算エラーが起きず安全
  • 返される角度は、x 軸正方向から反時計回りを 0 にした値

この関数の用途は、オブジェクト同士の角度計算、進行方向の決定、回転など “方向” にまつわるあらゆるロジックにある。

なぜゲーム開発でよく使われるのか

ゲームでは画面上に動くキャラクターや弾、エフェクトなどの多方向への移動・回転が必須。その際、方向を決めるための基礎がこの「角度」を正しく扱うこと。Math.atan2 はそのための標準道具となる理由は以下:

  • マウスクリック → クリック位置に向かって弾を発射
  • 敵をプレイヤーに追尾させる
  • 弾やレーザーの発射方向をキャラクターの向きに合わせる
  • 回転アニメーション(ロケット、ミサイル、矢など)

こういった動きの基盤として、四象限を考慮して安全に角度が取れる atan2 は理想的。

また、三角関数( Math.sin / Math.cos )と組み合わせることで、角度 ⇔ ベクトル の変換が簡潔にできる。これはベクトル運動、放物線、ホーミング、回転弾、弾幕などを構築する際の基本テクだ。

技術詳細:返り値と座標系の扱い、注意点

・返り値の単位と範囲

  • 単位は ラジアン。度数法 (°) が必要なら angle * 180 / Math.PI で変換。
  • 出力範囲は –π … +π。たとえば (-5, 0) の場合は π、 (5, 0) は 0。
  • 上方向のベクトル (0, –1) は –π/2、下方向 (0, +1) は +π/2 ではなく +π/2 か –π/2 のどちらか、使用座標系によって見え方が異なる。

・座標系の前提を確認

Canvas などブラウザの描画では、Y 軸が下方向に増加する。つまり「下が正の Y」。 この座標系では、数学的な「上が正の Y」とは逆なので、角度が直感とずれることがある。

例えば「上に向かって飛ぶ弾」のベクトルが (0, –1) であっても、Canvas 上では上方向が負の y だから dy は負。角度は –π/2。回転や描画時の補正を忘れず。

・単一の atan (逆タンジェント) との違いとリスク

Math.atan(dy / dx) だと、dx = 0 で除算エラー、左右両方向の区別なし、象限判定が難しい。 atan2 を使えばそのようなリスクを回避できる。

・パフォーマンス考慮

  • atan2 は比較的重い計算(逆三角関数)なので、毎フレーム大量に呼ばれる場合は注意。
  • 多数のオブジェクトが角度を更新するようなシステムでは、角度を再利用するか、呼び出し頻度を減らす工夫が必要。
  • 一度計算した角度を保存/再利用する設計(エンティティコンポーネントや状態管理)を意識する。

実例:Canvas で回転弾を撃つサンプル

// プレイヤー座標とクリック座標
const player = { x: px, y: py };
const click  = { x: mx, y: my };

// クリック位置へのベクトル
const dx = click.x - player.x;
const dy = click.y - player.y;

// 角度を計算(ラジアン)
const angle = Math.atan2(dy, dx);

// 弾の速度
const speed = 6;
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed;

// 描画時に回転
ctx.save();
ctx.translate(bullet.x, bullet.y);
ctx.rotate(angle);
// drawImage などで弾画像を向き合わせる
ctx.drawImage(bulletImage, -r, -r, r*2, r*2);
ctx.restore();

この流れで、クリック地点に向かう回転弾・ホーミング弾・誘導弾などのベースが簡潔に作れる。

放物線や重力、速度変化、補間を追加すれば、さらに多彩な挙動も可能。

よくある質問とその答え

質問 回答
dx = 0 のときどうする? atan2(dy, 0) は ±π/2 または ±π を返す。除算エラーにならず、安全に角度が通る。
角度を度数 (°) で扱いたい deg = angle * 180 / Math.PI で変換。逆変換は rad = deg * Math.PI / 180
角度を 0–2π の範囲に統一したい if(angle < 0) angle += 2 * Math.PI; などで正規化可能。
浮動小数点精度が気になる ベクトルや速度の正規化、丸め、角度差の計算には注意。小さなベクトル変化で角度が不安定になりがち。

なぜ「すぐ描ける」と「正しく描ける」は別か

2D ゲームの “見た目の正しさ” や “動きの自然さ” は、数式が正しいだけでは足りない。

  • 座標系(上下・左右、キャンバス vs 数学座標)
  • 角度の補正(弾画像の向き、スプライトの向き)
  • 描画順序、重心の設定、速度制御、物理挙動との整合性

Math.atan2 を正しく使いこなせば、「向き」は決まる。 でもそれを「画面上で自然に見えるように」組み合わせるには、さらに多層の制御と調整が必要になる。

そのため、atan2 は ベースの基礎的武器。 これを使いこなせるようになったら、ゲーム的な物理や演出での自由度がさらに高まる。

1. Math.atan2 の基本的な説明

Math.atan2(dy, dx) は、2D 座標系において、原点(0, 0)から点(dx, dy )までの直線が、x軸との角度を求める関数です。この角度は、直線がx軸の正方向から反時計回りに何度回転したかを示します。

この関数は、特にベクトルの方向を計算する際に役立ちます。具体的には、2D 空間で、指定した座標が原点から見てどの方向にあるのかを求めることができます。

引数

  • dy: 縦方向の差(y座標の差)。 例えば、目標の y 座標が 10 で、開始点の y 座標が 5 の場合、dy = 10 - 5 = 5 となります。

  • dx: 横方向の差(x座標の差)。 同様に、目標の x 座標が 5 で、開始点の x 座標が 0 の場合、dx = 5 - 0 = 5 となります。

Math.atan2(dy, dx)

この関数は、計算された角度を ラジアン 単位で返します。ラジアンとは、角度を計算するための単位で、円周率(π)を基準にした角度の測定法です。

  • 0 ラジアン: x軸の正方向
  • π / 2 ラジアン: y軸の正方向(上方向)
  • π ラジアン: x軸の負方向
  • -π / 2 ラジアン: y軸の負方向(下方向)

角度の範囲

Math.atan2(dy, dx) の返り値は、-π ~ π の範囲です。この範囲の角度は、-180° ~ 180° に相当します。

  • dy が正で、dx が正の場合、返される角度は 0 ~ π/2(右上の範囲)。
  • dy が正で、dx が負の場合、返される角度は π/2 ~ π(左上の範囲)。
  • dy が負で、dx が負の場合、返される角度は -π ~ -π/2(左下の範囲)。
  • dy が負で、dx が正の場合、返される角度は -π/2 ~ 0(右下の範囲)。

例:簡単な使用例

例えば、点 (3, 4) が原点 (0, 0) からどの方向にあるかを調べるとき、このように計算します。

let dx = 3;
let dy = 4;
let angle = Math.atan2(dy, dx);
console.log(angle); // 出力: 0.9272952180016122 (約 53.13°)

この場合、返された角度 0.93 ラジアン(または約 53.13°)は、原点から点(3, 4) までの方向を示しています。

逆に、角度からベクトルを計算する

Math.atan2 を使うことで、与えられた角度を使ってベクトルを計算することもできます。例えば、angle が与えられた場合、その角度に基づいて進行方向の x と y の速度成分を計算できます。

let speed = 10; // 進行速度
let angle = Math.PI / 4; // 45度(π/4 ラジアン)

let velocityX = Math.cos(angle) * speed;
let velocityY = Math.sin(angle) * speed;

console.log(velocityX, velocityY); // 出力: 7.0710678118654755 7.0710678118654755

この例では、45度の方向に向かって進むベクトル(velocityX, velocityY)を求めています。ベクトルの大きさ(速度)は 10 で、x と y の成分が計算されます。

2. 返り値の範囲

Math.atan2(dy, dx) の返り値は、-π ~ π の範囲であるラジアンです。ラジアンは角度の単位で、2π(360度)が一周に相当します。Math.atan2 を使うことで、全方向の角度を求めることができるため、360度のすべての方向に対応できます。

返り値の範囲

  • -π は、x軸の負方向(左方向)を指します。
  • 0 は、x軸の正方向(右方向)を指します。
  • π は、x軸の負方向(左方向)を指します。

これにより、Math.atan2 を使えば、原点から任意の座標に向かう角度を求めることができます。特に、左方向・右方向の区別が重要な場合でも、atan2 を使うことで正確に方向を判断できます。


角度の解釈

  • 0 ラジアン(または 0 度)は、x軸の正方向(右方向)を示します。
  • π / 2 ラジアン(または 90 度)は、y軸の正方向(上方向)を示します。
  • π ラジアン(または 180 度)は、x軸の負方向(左方向)を示します。
  • -π / 2 ラジアン(または -90 度)は、y軸の負方向(下方向)を示します。

具体的な例

Math.atan2(dy, dx) を使って、座標 (dx, dy) の角度を計算することで、実際にどの方向に向かっているかを確認できます。

例1: 右方向(x軸正方向)

  • もし dx が正、dy がゼロの場合、つまり (1, 0) の場合、Math.atan2(0, 1) は 0 ラジアンを返します。これは、x軸の正方向(右方向)を指します。
let dx = 1;
let dy = 0;
let angle = Math.atan2(dy, dx);
console.log(angle); // 出力: 0 (0 ラジアン、右方向)

例2: 上方向(y軸正方向)

  • dx がゼロで、dy が正の場合、つまり (0, 1) の場合、Math.atan2(1, 0) は π / 2 ラジアン(または 90 度)を返します。これは、y軸の正方向(上方向)を指します。
let dx = 0;
let dy = 1;
let angle = Math.atan2(dy, dx);
console.log(angle); // 出力: 1.5707963267948966 (π/2 ラジアン、上方向)

例3: 左方向(x軸負方向)

  • dx が負、dy がゼロの場合、つまり (-1, 0) の場合、Math.atan2(0, -1) は π ラジアン(または 180 度)を返します。これは、x軸の負方向(左方向)を指します。
let dx = -1;
let dy = 0;
let angle = Math.atan2(dy, dx);
console.log(angle); // 出力: 3.141592653589793 (π ラジアン、左方向)

例4: 下方向(y軸負方向)

  • dx がゼロで、dy が負の場合、つまり (0, -1) の場合、Math.atan2(-1, 0) は -π / 2 ラジアン(または -90 度)を返します。これは、y軸の負方向(下方向)を指します。
let dx = 0;
let dy = -1;
let angle = Math.atan2(dy, dx);
console.log(angle); // 出力: -1.5707963267948966 (-π/2 ラジアン、下方向)

角度の範囲と実際の活用

Math.atan2 を使うと、角度の範囲が -π ~ π となるため、次のように便利に使えます。

  • 360度の全方向に対応しているので、左回り、右回り、上下方向の計算が簡単にできます。
  • 座標 (dx, dy) がどの方向にあるのかを判別するために、角度を求めるといった用途で活用できます。

例えば、敵キャラクターがプレイヤーを追いかけるゲームの場合、プレイヤーと敵キャラクターの座標を取得し、Math.atan2 で敵キャラがプレイヤーに向かって移動するための角度を計算できます。

3. 使用例

Math.atan2 を使用すると、点 (dx, dy) の方向を簡単に求めることができます。例えば、ある点から別の点への直線の角度を計算する場合に非常に役立ちます。

例1: 基本的な使用例

let dy = 5;
let dx = 5;

let angle = Math.atan2(dy, dx);
console.log(angle); // 出力: 0.7853981633974483 (π/4)

この例では、点 (5, 5) の方向を計算しています。出力は π/4(45度) となり、点 (5, 5) は 右上 に位置しています。

例2: 左上方向の角度

次に、dx を負の値にして、左上の角度を求めてみましょう。

let dy = 5;
let dx = -5;

let angle = Math.atan2(dy, dx);
console.log(angle); // 出力: 2.356194490192345 (3π/4)

この場合、出力は 3π/4(135度) となり、点 (-5, 5) は 左上 に位置しています。

例3: 原点からの角度計算

Math.atan2 は、原点 (0, 0) から指定した点 (dx, dy) までの角度を計算します。これを利用して、角度を回転させる処理に役立てることができます。

let dy = -10;
let dx = 5;

let angle = Math.atan2(dy, dx);
console.log(angle); // 出力: -1.1071487177940904 (-π/3)

この場合、角度が負であることがわかります。点 (5, -10) は 右下 に位置しています。

4. 実際の利用例

Math.atan2 は、ゲーム開発において非常に役立つツールで、キャラクターやオブジェクトの向きや移動方向を計算するのに使われます。ここでは、プレイヤーと敵キャラの向きの計算を例に、Math.atan2 の具体的な使用方法を紹介します。

例: プレイヤーと敵キャラの向きの計算

ゲーム開発では、例えば敵キャラがプレイヤーを追いかけるときや、弾がプレイヤーに向かって発射される場合、Math.atan2 を使用して敵キャラや弾の進行方向を計算できます。

以下の例では、プレイヤーと敵キャラの位置を元に、敵キャラがプレイヤーに向かって進むための角度を計算します。この角度を使って、敵キャラがプレイヤーに向かって回転したり、弾がプレイヤーを追尾したりします。

コード例

let player = { x: 100, y: 100 };  // プレイヤーの位置
let enemy = { x: 200, y: 150 };   // 敵キャラの位置

// プレイヤーから敵への垂直距離と水平方向の距離を求める
let dy = enemy.y - player.y;      // Y軸の差分
let dx = enemy.x - player.x;      // X軸の差分

// プレイヤーから敵キャラへの角度を計算
let angleToEnemy = Math.atan2(dy, dx);
console.log(angleToEnemy); // 出力: 0.4636476090008061

説明

  • dy: プレイヤーと敵キャラのY座標の差分です。

  • dx: プレイヤーと敵キャラのX座標の差分です。

  • Math.atan2(dy, dx) は、これらの差分をもとに、プレイヤーから敵キャラに向かう角度を計算します。この角度は ラジアン で返され、0 ~ π(360度の範囲) で方向を示します。

    この例では、出力が 0.4636476090008061 ラジアン で、これは約 26.57度 になります。敵キャラはプレイヤーの 右上 の方向にいます。

利用方法

  1. 敵キャラの向きの計算:

    • これで得た角度(angleToEnemy)を使って、敵キャラをプレイヤーに向かせることができます。例えば、rotate() を使って回転させたり、進行方向を更新する際に利用できます。
  2. 弾の追尾:

    • 弾がプレイヤーを追尾する場合も、この角度を利用して、弾をプレイヤーの方向に向けて発射できます。弾の速度(velocityX, velocityY)は、角度を元に更新することができます。

弾の追尾実装例

以下は、敵キャラが弾を発射し、その弾がプレイヤーを追尾するための簡単な例です。

let bullet = { x: enemy.x, y: enemy.y, velocityX: 0, velocityY: 0, speed: 5 };

// プレイヤーの位置に向かって弾を発射
let bulletAngle = Math.atan2(player.y - enemy.y, player.x - enemy.x);
bullet.velocityX = Math.cos(bulletAngle) * bullet.speed;
bullet.velocityY = Math.sin(bulletAngle) * bullet.speed;

// 弾の移動
bullet.x += bullet.velocityX;
bullet.y += bullet.velocityY;

console.log(`Bullet Position: (${bullet.x}, ${bullet.y})`);

説明

  • Math.atan2(player.y - enemy.y, player.x - enemy.x) でプレイヤーに向かう方向を計算。
  • その方向に沿って弾の速度 (velocityXvelocityY) を更新し、次のフレームで弾をその方向に進ませます。

この方法で、弾がターゲットを追尾し続けることができます。


より高度な利用方法

Math.atan2 は、ターゲット追尾や回転に加えて、次のような高度なシナリオにも使用できます:

  • 回転する武器やエフェクト(例えば、ガンのバレルがターゲットに向かって回転)
  • 車やロボットの進行方向(動きと回転を連動させる)
  • AIの移動アルゴリズム(敵キャラがプレイヤーを追いかける動き)
  • 弾の放物線(重力を加えた進行方向の制御)

5. Math.atan2 の便利な使い方

Math.atan2 は非常に多用途で、2D ゲームやシミュレーションの開発において、方向や回転、ターゲットの追尾など、さまざまな場面で活用できます。ここでは、Math.atan2 を使った便利な使い方をいくつか紹介します。

1. 方向を求める

Math.atan2 は、オブジェクトが他のオブジェクトに向かう方向を計算するために非常に役立ちます。例えば、ゲームのキャラクターや弾、レーザーがターゲットに向かって進む場合、Math.atan2 を使ってその角度を簡単に求めることができます。

使用例: 敵キャラクターがプレイヤーに向かって進む方向を計算する

let player = { x: 100, y: 100 };
let enemy = { x: 200, y: 150 };

// プレイヤーから敵への角度を計算
let dy = enemy.y - player.y;
let dx = enemy.x - player.x;

let angleToPlayer = Math.atan2(dy, dx);
console.log(angleToPlayer); // 出力: 0.4636476090008061 (π/4)

このようにして、敵キャラクターがプレイヤーに向かって進む角度を求めることができます。この角度を使用して、敵キャラクターをプレイヤーの方向に動かすことができます。


2. アニメーションや回転

Math.atan2 は、キャラクターやオブジェクトがターゲットに向かって回転する処理を実現する際に役立ちます。例えば、レーザーや弾が進行方向に合わせて回転したり、キャラクターがターゲットに向かって向きを変える場合に使えます。

使用例: キャラクターがターゲットに向かって回転する

let player = { x: 100, y: 100 };
let target = { x: 200, y: 150 };

// プレイヤーからターゲットへの角度を計算
let dy = target.y - player.y;
let dx = target.x - player.x;

let angleToTarget = Math.atan2(dy, dx);

// キャラクターがターゲットに向かって回転する(回転処理を簡略化した例)
let characterAngle = angleToTarget;
console.log(characterAngle); // 出力: 0.4636476090008061 (π/4)

この例では、キャラクターがターゲットに向かって回転するための角度を求め、その角度を使ってアニメーションの回転処理に反映させることができます。


3. 位置計算

Math.atan2 は、自分の位置から他の位置までの角度を知りたいときにも非常に有効です。例えば、弾を発射したい方向を決める場合や、キャラクターがターゲットを追いかける方向を計算する場合に使えます。

使用例: 自分の位置からターゲットの位置までの角度を計算する

let myPosition = { x: 300, y: 400 };
let targetPosition = { x: 600, y: 450 };

// 自分からターゲットへの角度を計算
let dy = targetPosition.y - myPosition.y;
let dx = targetPosition.x - myPosition.x;

let angleToTarget = Math.atan2(dy, dx);
console.log(angleToTarget); // 出力: 0.3217505543966422 (約 18.43°)

このようにして、自分の位置からターゲットへの角度を求めることができます。この角度を使って、弾を発射する方向を決めたり、キャラクターをターゲットに向かって移動させたりできます。


4. 複数のターゲットを追いかける

Math.atan2 を使えば、複数のターゲットに対して方向を計算し、それに基づいてキャラクターや弾を操作することもできます。例えば、複数の敵キャラクターがプレイヤーを追いかけるようなシーンでは、Math.atan2 を使って各敵キャラクターの向きを求め、プレイヤーを追尾させることができます。

使用例: 複数の敵がプレイヤーを追いかける

let player = { x: 400, y: 300 };
let enemies = [
  { x: 100, y: 100 },
  { x: 500, y: 200 },
  { x: 300, y: 500 }
];

// 各敵キャラの向きを計算
enemies.forEach(enemy => {
  let dy = player.y - enemy.y;
  let dx = player.x - enemy.x;
  let angleToPlayer = Math.atan2(dy, dx);
  console.log(`Enemy at (${enemy.x}, ${enemy.y}) has angle: ${angleToPlayer}`);
});

このコードでは、複数の敵キャラクターがそれぞれプレイヤーを追いかけるための方向を計算しています。この方向を使って、各敵キャラクターがプレイヤーに向かって進むことができます。

まとめ

  • Math.atan2(dy, dx) は、座標 (dx, dy) の方向を計算するために使用され、-π ~ π の範囲の角度(ラジアン)を返します。
  • 全方向に対応しているため、左右、上下、斜めの角度も正確に計算できます。これにより、ゲーム内での向きの計算やターゲット追尾、回転などの処理が簡単に実現できます。
  • ゲーム開発や2Dグラフィックスでよく使用され、ターゲット追尾やアニメーション、物理計算など、さまざまな場面で活用されます。

Math.atan2 は、他の計算(例えば、Math.cosMath.sin)と組み合わせて使うことで、非常に強力で柔軟なツールとなります。ぜひこの関数を使って、色々な方向や角度を計算してみてください!