23
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Unity(C#)】オブジェクトに当たると途切れるレーザーの実装方法

Last updated at Posted at 2019-07-14

##レーザー
RayLineRendererで実装します。

Rayがオブジェクトに当たったかどうかの判定を行い、LineRendererの終点の位置を変えることで
レーザーの途切れを表現します。

そのためにはRayLineRendererの始点、終点を合わせておく必要があります。

RayとLineRendererの位置を合わせる
 void OnRay()
    {      
        float lazerDistance = 10f;
        Vector3 direction = hand.transform.forward * lazerDistance;
        Vector3 pos = hand.transform.position;

        RaycastHit hit;
        Ray ray = new Ray(pos, hand.transform.forward);
        lineRenderer.SetPosition(0,hand.transform.position);

        if (Physics.Raycast(ray, out hit, lazerDistance))
        {
            lineRenderer.SetPosition(1, pos + direction);
        }
        
        Debug.DrawRay(ray.origin, pos + direction, Color.red, 0.1f);
    }

画像のようにぴったりと合わせることができました。(赤い線=Ray,青い線=LineRenderer)
ray_Line.PNG

##貫通してしまう
先程のコードのままでは画像のようにオブジェクトを貫通してしまいます。
Penetration.PNG

この問題を解決するうえで、RayLineRendererをぴったりと合わせたことが鍵となります。

Rayはオブジェクトとの衝突判定を行うことができます。
また、オブジェクトと衝突した座標を取得することもできます。

つまり、LineRendererの終点にRayが衝突した座標を当てはめれば
オブジェクトに当たった箇所でレーザー(LineRenderer)が途切れているように見せることができる
ということです。

コードに落とし込むとこうです。

        if (Physics.Raycast(ray, out hit, lazerDistance))
        {
            hitPos = hit.point; //オブジェクトとの衝突座標を取得
            lineRenderer.SetPosition(1, hitPos); //LineRendererの終点に当てはめる
        }

##デモ
OculusQuestでビルドしたデモになります。
Ray_LineRenderer.gif

遮蔽物でレーザーが途切れているように見えます。ばっちりです。

デモにおいて、始点も少しずらしているので
始点のずらし方に関しても最終的なコードと合わせて記述していきます。

##最終的なコード

using UnityEngine;

[RequireComponent(typeof(LineRenderer))]
public class Laser : MonoBehaviour
{
    [SerializeField]
    GameObject hand;

    LineRenderer lineRenderer;
    Vector3 hitPos;
    Vector3 tmpPos;

    float lazerDistance = 10f;
    float lazerStartPointDistance = 0.15f;
    float lineWidth = 0.01f;

     void Reset()
    {
        lineRenderer = this.gameObject.GetComponent<LineRenderer>();
        lineRenderer.startWidth = lineWidth;
    }

    void Start()
    {
        lineRenderer = this.gameObject.GetComponent<LineRenderer>();
        lineRenderer.startWidth = lineWidth;
    }


    void Update()
    {
        OnRay();      
    }

    void OnRay()
    {      
        Vector3 direction = hand.transform.forward * lazerDistance;
        Vector3 rayStartPosition = hand.transform.forward*lazerStartPointDistance;
        Vector3 pos = hand.transform.position;
        RaycastHit hit;
        Ray ray = new Ray(pos+rayStartPosition, hand.transform.forward);

        lineRenderer.SetPosition(0,pos + rayStartPosition);

        if (Physics.Raycast(ray, out hit, lazerDistance))
        {
            hitPos = hit.point;
            lineRenderer.SetPosition(1, hitPos);
        }
        else
        {      
            lineRenderer.SetPosition(1, pos + direction);
        }

        Debug.DrawRay(ray.origin, ray.direction * 100, Color.red, 0.1f);

    }
}

##始点をずらす
始点をずらしているのは下記の箇所です。

    Vector3 rayStartPosition = hand.transform.forward*lazerStartPointDistance;
    Ray ray = new Ray(pos+rayStartPosition, hand.transform.forward);
    Vector3 pos = hand.transform.position;
    lineRenderer.SetPosition(0,pos + rayStartPosition);

Shaderで始点をずらしているようにみせる1こともできますが、
今回は始点そのものの座標を調節してます。

手にコライダーが付いている状態で手のTransformを参照してRayを出した場合、
手のPivot、コライダーの大きさによっては
Rayが手のコライダーに衝突してしまって判定が取れない場合があります。
そもそものRayの始点をずらすことで意図しない衝突を回避できます。

  1. 参考リンク

23
18
0

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
23
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?