##視線
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
二つのベクトルを渡してあげるだけで内積を返してくれます。(参考リンク)
lhs,rhsはLeft(Right) hand side
の略らしいです。
今回はVector3.Dotを利用して**内積が-0.9(約cos155°)**以下になると見たという判定にしています。
人間の有効視野の上限は20~30度だといわれている1ので
完全に視線が一致(内積が-1(cos180°))するまで判定させないより
だいたいこの辺りを見たと判断させる方が良い方法だと思います。
・ターゲットの位置からカメラの位置へ向けたベクトル
・カメラの正面方向(Z軸正の向き)のベクトル
↑これらのベクトルから内積を計算してます。
##デモ
視線判定の範囲内に入ると赤色に変わるようにしたデモです。
-0.9
を-0.95
のように-1に近づけてあげるともっと厳格な視線判定もできます。
大学はおろか高校ですら ろくに数学をやってこなかったので間違いがあれば
遠慮なくご指摘ください。
##視錐台
2019/11/08 追記
興味深い記事を見つけたので関連として貼っておきます。
【Unity】【数学】視錐台(Frustum)について(第2回)
視錐台の内判定
= 見えている範囲
= Unityのカメラの描画範囲
≠ VR(HMD越し)の視界
なので、VRへの応用は許容誤差が範囲内であれば、、、といった感じでしょうか。
// 視錐台平面を獲得
Plane[] planes = GeometryUtility.CalculateFrustumPlanes( camera );
System.Array.Resize( ref planes, 4 ); // near,far面を排除
// 内外判定
if ( GeometryUtility.TestPlanesAABB( planes, bounds ) ) {
// 入った!!
}