Help us understand the problem. What is going on with this article?

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

視線

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. 参考リンク  

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした