【コピペでUnity】内積を爆速で実装できます

Unity
Unity

Unityで内積を計算したい人

内積のコード教えて!!!

 

こちらです。

//直角かどうかを扱いたい場合の内積
_float = Vector3.Dot(_vectorA, _vectorB);
//角度を扱いたい場合の内積
_float = Vector3.Dot(_vectorA.normalized, _vectorB.normalized);

 

本記事の内容

●内積のコード説明
●内積の直感的な使い方
●数学的な内積

 

この記事を書いている私は12才からフルスクラッチでゲームを作っています。
またコンパイラを専門として情報理工学の博士まで進学、中退をしており、プログラミング言語の文法にも関わる論文を書いていました。

実用数学技能検定1級も所持しています。

 

※実行環境
Unity2021.3.1f1
Windows 10

 

内積のコード説明

●_vectorA, _vectorB
内積を求めたい2つのベクトル

●_float
内積の値

 

どんな計算をしているか

Vector3クラスのDot関数は2つのVector3インスタンスを与えると、内積の計算結果が返ってきます。

_vectorA = new Vector3(1, 2, 3);
_vectorB = new Vector3(2, 4, 6);

_float = Vector3.Dot(_vectorA, _vectorB);
//28

 

数学だと以下のような計算式なので、確かに計算出来ていることが分かります。

$$\begin{align}
\vec{a} \cdot \vec{b} &=
\begin{pmatrix}a_1  \\ a_2 \\ a_3 \end{pmatrix} \cdot \begin{pmatrix}a_1 \\ a_2 \\ a_3 \end{pmatrix} \\ &=
\begin{pmatrix}1 \\ 2 \\ 3 \end{pmatrix} \cdot \begin{pmatrix}2 \\ 4 \\ 6 \end{pmatrix} \\ &=
1 \times 2 + 2 \times 4 + 3 \times 6 \\ &=
2 + 8 + 18 \\ &=
28
\end{align}$$

 

変数normalizedはベクトルの向きが管理されており、normalized同士の内積を取ると-1~1の間で値が返ってきます。
値の範囲が絞られるだけで、数学的な内積という意味では全く同じ計算をしています。

 

 

内積の直感的な使い方

前、横、後ろを判断する

フィールド上のキャラクターの向きなど、2次元で考える場合の内積は「前、横、後ろ」を判断することに使えます。
この時絶対に覚えたいのは、垂直の時に内積は0になる、ということです。

 

よって内積が0より大きい場合は視界に入っている、内積が0の場合は真横にいる、内積が0未満の場合は背後にまわっている、などの実装に活用できるワケです。

 

角度を判定する

もう少し細かい角度を表せないかと思うかもしれませんが、これがなんと表せます。
サンプルコードのnormalizedがその作業にあたり、Vector3の向きを求める計算です。

後述しますがこの数値こそ数学でいう\(\cos\theta\)です。
代表的な角度と値の組み合わせを小数第3位まで、以下に並べておきます。

 

●0度:1
●30度:0.866
●45度:0.707
●60度:0.5●90度:0●120度:-0.5
●135度:-0.707
●150度:-0.866
●180度:-1

 

これを活用すれば、キャラクターの視野が狭いので回り込むのが簡単、広いので真横に立ってもすぐ見つかってしまう、などの機能が実現できるでしょう。
もっと細かい角度を気にしたい方は「cos [角度]」で検索すれば値が出てくるので、そのまま判定に使ってしまいましょう。

 

平面のどちら側にいるかを判断する

3次元空間に対しても、実は似たような考え方が出来ます。
キーワードは、垂直の時に内積は0になる、です。

 

キャラクターが地面に立っているとして、立っている向きに対して内積を計算することを考えてみましょう。
特にこの立っている向きのことを、数学では平面に対する法線方向と呼んだりします。

●地面より上…内積が0より大きい
●地面の高さ…内積が0
●地面より下…内積が0より小さい

このような計算が出来ます。

 

キャラクターの向きと地面の上が直角というのは、現実世界でも壁と床は大体直角と言われれば納得できるかと思います。
くり返しですが、垂直の時に内積は0です。

 

数学的な内積

内積を計算する要素

図形的な、ゲーム的な理解をしたうえで、内積を数学的に見てみましょう
内積の数式での表し方は色々ありますが、今回は以下の式を扱います。

$$\vec{a} \cdot \vec{b} = |\vec{a}||\vec{b}|\cos\theta$$

●\(|\vec{a}|\)…\(\vec{a}\)の長さ
●\(|\vec{b}|\)…\(\vec{b}\)の長さ
●\(\cos\theta\)…角度\(\theta\)に対応する、単位円上のx座標を返す関数

 

\(\cos\theta\)の定義は耳慣れないかもしれませんが特に複雑なものでもなく、下図の通りです。

 

これによって、垂直の時に内積は0ということが納得できると思います。

 

normalizedで上手くいくのはなぜか

normalizedは向きを求める作業だと書きましたが、それはベクトルの向きを保ったまま、長さを1にする計算だからです。
一応のこと、ベクトルは「向き」と「長さ」の2つの要素が大事なので、ピンと来ない方は今一度ご確認ください。
参考:【コピペでUnity】ややこしい実装もVector3の標準機能でサクっと解決!

 

長さが1のベクトルということは、内積の定義式である\(|\vec{a}|\)と\(|\vec{b}|\)には1が代入されることを意味します。
よって、

$$\begin{align}
|\vec{a}||\vec{b}|\cos\theta
&= 1 \times 1 \times \cos\theta \\ &= \cos\theta
\end{align}$$

より、normalizedをDot関数の引数に与えると、実は\(\cos\theta\)の値が返ってきていることが分かります。
だから細かい角度を判定することが出来るワケですね。

 

今回は以上です。

三角関数はいいぞ。

 

コメント

タイトルとURLをコピーしました