LoginSignup
20

More than 3 years have passed since last update.

【Unity(C#)】Rayではなく内積(Vector3.Dot)で視線判定を行う

Last updated at Posted at 2019-05-18

視線

VRで視線判定をけっこう使うのでメモしときます。

Rayを使用した視線の判定も十分簡単にできます。
"目(カメラ)からビーム出してビームがターゲットに衝突したら処理を行う"
ただこれだけです。

ただし、障害物があるとLayerやTagで衝突を避ける設定をする必要があります。

今回は会社の上司に教えてもらった
LayerやTagを一切気にしなくてもいい方法で実装してみようと思います。

内積

まずは内積とは何かを一言で説明したいのですが、
一言で表すとなると全く思いつきませんでした。

私の理解もその程度だということなので、複数のリンクを貼っておきます。
個人的に動画の解説が一番しっくりきました。

Qiita記事 :【数学】「内積」の意味をグラフィカルに理解すると色々見えてくる その1

Youtube : 喋り方の癖だけ耐えればめちゃめちゃわかりやすい動画

Youtube : シンプルにわかりやすい動画

コード

たったこれだけです。

カメラにアタッチ
using UnityEngine;

public class Dot : MonoBehaviour
{
    [SerializeField]
    Transform targetTransform;

    Transform cameraDirection;

    void Start()
    {
        cameraDirection = this.gameObject.GetComponent<Transform>();
    }

    void Update()
    {
        //ターゲットからカメラの方向へ正規化したベクトルを作成
        Vector3 targetToCameraDirection_N = (cameraDirection.position - targetTransform.position).normalized;

        //正規化したベクトルの内積が一定以下なら見たことにする
        if (Vector3.Dot(targetToCameraDirection_N, cameraDirection.forward.normalized) < -0.9)
        {
            print("見た!");
        }
    }
}

normarized

ベクトルを正規化してくれます。
問答無用で値を1にしてくれるってことです。(ただし小さすぎると0になるので注意)

normarizedを利用することで、2点の位置から算出したベクトルを
常に一定の大きさで取得することができます。

Vector3.Dot

二つのベクトルを渡してあげるだけで内積を返してくれます。(参考リンク)
B292E9AE-1C23-44DB-8684-C87F4EEC9C84.png

lhs,rhsはLeft(Right) hand sideの略らしいです。

今回はVector3.Dotを利用して内積が-0.9(約cos155°)以下になると見たという判定にしています。

人間の有効視野の上限は20~30度だといわれている1ので
完全に視線が一致(内積が-1(cos180°))するまで判定させないより
だいたいこの辺りを見たと判断させる方が良い方法だと思います。

・ターゲットの位置からカメラの位置へ向けたベクトル
・カメラの正面方向(Z軸正の向き)のベクトル
↑これらのベクトルから内積を計算してます。

デモ

視線判定の範囲内に入ると赤色に変わるようにしたデモです。

Gif_Dot.gif

-0.9-0.95のように-1に近づけてあげるともっと厳格な視線判定もできます。
  

大学はおろか高校ですら ろくに数学をやってこなかったので間違いがあれば
遠慮なくご指摘ください。

視錐台

2019/11/08 追記

興味深い記事を見つけたので関連として貼っておきます。
【Unity】【数学】視錐台(Frustum)について(第2回)

視錐台の内判定 = 見えている範囲 = Unityのカメラの描画範囲VR(HMD越し)の視界
なので、VRへの応用は許容誤差が範囲内であれば、、、といった感じでしょうか。

logicalbeat様 記事引用
// 視錐台平面を獲得
Plane[] planes = GeometryUtility.CalculateFrustumPlanes( camera );
System.Array.Resize( ref planes, 4 );       // near,far面を排除

// 内外判定
if ( GeometryUtility.TestPlanesAABB( planes, bounds ) ) {
    // 入った!!
}

  1. 参考リンク  

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20