扇形と点の当たり

概要

最終更新日:2020/02/08

扇形(円弧)と点の当たりの説明をします。
この判定の実装をするとオブジェクトに簡単な視野を持たせることが可能となります。
視野を実装することでより生物らしい動きになります。

注意点

この記事内の座標軸はX軸「右が正、左が負」Y軸「下が正、上が負」で書いています。

サンプル

サンプルはここからダウンロードでき、環境については以下の内容となっています。

開発環境
VSのバージョン VisualStudio 2019
DirectXのバージョン DirectX9
説明 扇と点の当たり判定を行っています
通常の点の色は白く、扇と当たっている間は赤になります。
キーボードの上下左右で点を移動できます。
キーボードのAとDで扇の範囲を変更できます。

判定方法

判定は以下の条件を両方とも満たしていた場合に当たっているとします。
  • 扇の方向ベクトルと点と扇のベクトルのなす角が
    扇の範囲(角度)より小さい
  • 扇と点の長さが扇の長さよりも小さい

判定手順

手順以下の通りです。
  1. 準備
  2. 扇と点のベクトルを求める
  3. 扇と点の長さを測る
  4. 長さを比較する
  5. 扇形の方向ベクトルを求める
  6. 二つのベクトルの内積を求める
  7. 扇の範囲からcosの値を求める
  8. 範囲を比較する
①は準備、②が二つの判定に必要な情報の用意で、
③~④が長さ判定、⑤~⑧が範囲の比較です。

準備

判定を行うためには以下の情報が必要です。

扇形

  • 中心点座標
  • 扇の範囲(角度)
  • 扇の範囲(長さ)
  • 扇の回転角度

  • 座標

扇形と点のベクトルを求める

まずは二つの判定に共通して必要な情報である扇形と点のベクトルを
以下の式を使用してを求めます。
    扇形と点のベクトル = 点の座標 - 扇形の中心座標

扇と点の長さを測る

扇と点のベクトルが出たので、このベクトルの長さを以下の式を使用して求めます。

collision_0057

長さを比較する

扇と点の長さが分かったので扇の長さ情報と比較します。
比較は以下の条件式で行います。
    扇と点の長さ < 扇の範囲の長さ
条件が満たされていた場合は、点が範囲内にあるかを調べる工程に進みますが、
満たされていなかった場合は、ここで判定を終了します。

扇形の方向ベクトルを求める

まずは扇形の方向ベクトルを求めます。
求め方は単純で回転角度0度の単位ベクトルを用意して、
そのベクトルを扇形の回転角度で回転した結果の値を使います。
もし、カメラのように中心点と注視点があるなら二つの点から
ベクトルを算出しても問題ありません。
※ベクトルは必ず単位ベクトルにしてください。

collision_0055

扇形の範囲はこのベクトルを境界として2等分とします。

collision_0054

二つのベクトルの内積を求める

上で求めた二つのベクトルを使用して内積計算を行います。
※扇と点のベクトルも単位ベクトルにします。
    二つのベクトルの内積 = 扇形のベクトル・扇と点のベクトル
               扇形.X * 扇と点.X + 扇形.Y * 扇と点.Y
               | 扇形のベクトル | * | 扇と点のベクトル | * cosθ
単位ベクトル同士の内積なので答えはcosθとなります。
    1 * 1 * cosθ => cosθ

扇の範囲からcosの値を求める

上の内積結果と比較をできるように扇の範囲を示すために使用している
角度のcosの値を求めます。
この時に使用する角度は1/2にしてください。

なぜ1/2にするのかというと、cosの値は扇の方向ベクトルを0度として考えます。
方向ベクトルは扇を2等分にするベクトルとして求めているので、
ベクトルを軸とした場合の扇の角度は「角度 / 2」になります。

collision_0056

範囲を比較する

二つの角度情報が集まったので比較を行います。
比較は以下の条件で行い、これが満たされたら点は扇の範囲内にあると考えます。
    扇の角度 < ベクトルのなす角
    ※角度 = cos(角度)として考えてください。
今回の範囲の判定が満たされていた場合、既に長さの判定は済ませているので
扇と点は当たっていると考えます。
逆に条件が満たされていない場合は当たっていません。

実装

サンプルの判定部分の説明をします。

準備

「1.準備」で書いてある情報は構造体を用意しました。

// 点
struct Point
{
	Vec2 Position;
	float Radius;
};

// 扇
struct Fan
{
	Vec2 Position;
	float ArcDegree;
	float Length;
	float RotateDegree;
};

共通

二つの判定で必要な扇と点のベクトルを求めます。

// 点と扇のベクトル
Vec2 vec_fan_to_point = {
	point.X - fan.Position.X,
	point.Y - fan.Position.Y
};

長さ判定

「共通」で求めたベクトルの長さを出します。

// ベクトルの長さ算出
float vec_length = sqrtf((vec_fan_to_point.X * vec_fan_to_point.X) + (vec_fan_to_point.Y * vec_fan_to_point.Y));

ベクトルの長さがでたので、扇の範囲の長さと比較し、
扇の方が小さい結果になった場合は判定を終了します。

// ベクトルと扇の長さの比較
if (fan.Length < vec_length)
{
	// 当たっていない
	return false;
}

範囲判定

範囲判定をおこなうために、扇の方向ベクトルを出します。
ベクトルは0°の単位ベクトルを用意して、扇の角度分だけ回転させます。

// 円弧の方向ベクトルを求める
float rotate_rad = D3DXToRadian(fan.RotateDegree);
Vec2 arc_dir =
{
	1.0f,
	0.0f
};

// ベクトルを回転させる
Vec2 rotate_arc_dir
{ 
	arc_dir.X * cosf(rotate_rad) + arc_dir.Y * -sinf(rotate_rad),
	arc_dir.X * 1.0f * sinf(rotate_rad) + arc_dir.Y * cosf(rotate_rad)
};

点と扇のベクトルと扇の方向ベクトルが出たので内積を求めます。
※点と扇のベクトルは単位ベクトルにします。

// 扇と点のベクトルを単位ベクトルにする
Vec2 normal_fan_to_point = {
	vec_fan_to_point.X / vec_length,
	vec_fan_to_point.Y / vec_length
};

// 内積計算
float dot = normal_fan_to_point.X* rotate_arc_dir.X + normal_fan_to_point.Y * rotate_arc_dir.Y;

内積がでたので、比較するために扇の範囲もcosの値にします。

// 扇の範囲をcosにする
float fan_cos = cosf(D3DXToRadian(fan.FanDegree / 2.0f));

これで範囲比較をするための情報が集まったので比較を行います。

// 点が扇の範囲内にあるかを比較する
if (cosf(D3DXToRadian(fan.FanDegree / 2.0f)) > dot)
{
	// 当たってない
	return false;
}

点と扇のなす角が扇の範囲よりも大きかった場合は当たっていません。
そして、この条件式が不成立なら、長さと範囲は問題ないということなので
扇と点は当たっています。