Edited at

Physics.Raycastにつまった件が解決しました

More than 1 year has passed since last update.

UnityのPhysics.Raycastにつまった件。昨日の夕方以降ずっと悩んでおりましたが解決しました。

結論としては座標とベクトルの違いをちゃんと抑えておこうってことでした。


UnityのPhysics.Raycast とは?

特定のベクトル方向に対して物体が存在するかを検証する機能です。

例えばOculusGoのコントローラに対して設定することでコントローラの指し示した先のオブジェクトの特定や

距離の測定などが可能となり、かなり利用率が高い機能だと思われます。


何が起こったか?

先ずは以下のコードをご覧ください


RayCastSample.cs

    [SerializeField] private LineRenderer Laser;

[SerializeField] private GameObject FoucusPoint;

// Update is called once per frame
void Update()
{
RaycastHit hit;
var t = GetComponent<Transform>();
var ray = new Ray(t.position, t.TransformDirection(Vector3.forward));
if (Physics.Raycast(ray, out hit))
{
var target = ray.direction * hit.distance;

// Debug
Debug.DrawRay(t.position, target, Color.red);

Laser.SetPositions(new Vector3[] { t.position, target });
FoucusPoint.SetActive(true);
FoucusPoint.transform.position = target;

Debug.Log("ray = " + ray);
Debug.Log("start = " + t.position);
Debug.Log("target = " + target);

Debug.Log("Did Hit");
}
}


イメージとしてはレーザーを照射して当たった部分に突起ができるような状況を作ろうとしています。

ポイントはここです。

var target = ray.direction * hit.distance;

rayの方向ベクトルにhit迄の距離をかけ合わせてレーザーの接触点の座標をつくろうとしています。

上記の出力結果です。

Unity_2018_1_0f2_Personal__64bit__-_SampleScene_unity_-_raycast_simple_-_PC__Mac___Linux_Standalone__Personal___Metal_.png

赤いボールがレーザーの発射口です。DrawRayは上手く描画できていますが、レーザーの照射方向に間違いがあります。

ログを確認してみます。

Unity_2018_1_0f2_Personal__64bit__-_SampleScene_unity_-_raycast_simple_-_PC__Mac___Linux_Standalone__Personal___Metal_.png

targetの想定座標が異なりました。Y軸の値が違います。

Debug.DrawRayは想定通りの挙動なのに、、、、、

そこでDebug.DrawRayの仕様を調べてみました。

Debug_DrawRay_-_Unity_スクリプトリファレンス.png

やっちまいました。 Debug.DrawRay の第2引数は 座標ではなく方向ベクトルだったのです。

先程の

var target = ray.direction * hit.distance;

rayの方向ベクトルにhit迄の距離をかけ合わせてレーザーの接触点の座標をつくろうとしています。

というのは

間違いですね

正解はこうです

var target = ray.orgin + ray.direction * hit.distance;

rayの方向ベクトルにhit迄の距離をかけ合わせても方向ベクトルができるだけで

発射地点の座標を足して初めてターゲットの座標が算出できます。

ちゃんと動くコードを書きます。


RayCastSample.cs

using System.Collections;

using System.Collections.Generic;
using UnityEngine;

public class RaycastSample : MonoBehaviour
{

[SerializeField] private LineRenderer Laser;
[SerializeField] private GameObject FoucusPoint;

// Update is called once per frame
void Update()
{
RaycastHit hit;
var t = GetComponent<Transform>();
var ray = new Ray(t.position, t.TransformDirection(Vector3.forward));
if (Physics.Raycast(ray, out hit))
{
var dir = ray.direction * hit.distance;
var target = ray.origin + dir;
// Debug
Debug.DrawRay(t.position, dir, Color.red);

Laser.SetPositions(new Vector3[] { t.position, target });
FoucusPoint.SetActive(true);
FoucusPoint.transform.position = target;
Debug.Log("Did Hit");
}

}
}


レンダリング結果

Unity_2018_1_0f2_Personal__64bit__-_SampleScene_unity_-_raycast_simple_-_PC__Mac___Linux_Standalone__Personal___Metal_.png

うまくいきました。


結論

座標なのかベクトルなのかちゃんと意識しよう。後マニュアルちゃんと読むべし