第 28 章 Unity の光線検出

この章では光線について紹介します。光線は固定点から一方向に直線を発射するものですが、その際に光線がゲームオブジェクトに衝突するかどうかを判断する必要があります。光線は、シューティング ゲームで武器がターゲットを向いているかどうかを検出するために使用できます。また、マウスがゲーム オブジェクトを向いているかどうかを判断することもできます。光線を作成する方法は通常、コードを使用して実装されます。次に、「SampleScene3.unity」シーンを新規作成してみましょう。ここで、レイ検出は物理システムに基づいているため、衝突体コンポーネントを持つゲーム オブジェクトのみがレイによって検出されることに注意してください。幸いなことに、Unity では、作成された Cube または Sphere には、対応する衝突ボディ コンポーネントが自動的に付属します。

Sphere1、Sphere2、Sphere3 という 3 つの球を作成し、Sphere1 から X 軸の負の方向 (上の図の左側) に光線を発射しました。そうすれば、この光線は Sphere2 と Sphere3 を検出できるはずです。

次に、「RayScript.cs」スクリプトファイルを作成してSphere1にアタッチします。内容は次のとおりです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayScript : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            // 射线的起点(Sphere1的位置)
            Vector3 origin = transform.position;
            // 射线的方向(X轴负方向)
            Vector3 direction = Vector3.left;
            // 射线碰撞的目标对象
            RaycastHit hitInfo;
            // 射线的最大长度
            float maxDistance = 100;

            // 创建射线,返回是否检测到碰撞对象
            bool raycast = Physics.Raycast(origin, direction, out hitInfo, maxDistance);

            // 如果发生碰撞,碰撞信息就被存储到 hitInfo 中
            if (raycast)
            {
                // 获取碰撞点坐标
                Vector3 point = hitInfo.point;
                Debug.Log("碰撞点坐标:" + point);

                // 获取碰撞目标的名称
                string name = hitInfo.collider.name;
                Debug.Log("碰撞对象名称:" + name);

                // 获取目标的碰撞体组件
                Collider coll = hitInfo.collider;

                //获取目标的Transgorm组件
                Transform trans = hitInfo.transform;
            }
        }
    }
}

上記のコードは非常に単純で、4 つのパラメーターを使用して Physics.Raycast メソッドでレイを作成し、3 番目のパラメーター RaycastHit hitInfo を必要な衝突ターゲットの情報として使用します。このオブジェクトを使用して、レイとターゲットの間の衝突点の位置情報、ターゲットのゲーム オブジェクトの名前、およびそのコライダー コンポーネントまたはトランスフォーム コンポーネントを取得できます。実際、Physics.Raycast メソッドには 5 番目のパラメーター int LayerMask もあり、これは検出レイヤーを指定し、他のレイヤーを無視するために使用されます。前の章では、衝突検出がレイヤーによって制限される可能性があると述べました。レイヤー間のゲーム オブジェクトが衝突することを指定でき、同じことがレイの衝突検出にも当てはまります。ここでは、このパラメーターについて詳しくは説明しません。

ゲーム開発では、レイは目に見えないため、レイの衝突の有効性を判断できない場合があります。現時点では、Debug.DrawLine() 関数と Debug.DrawRay() を使用して光線をシミュレートできます。まずDrawLineを紹介します。

Debug.DrawLine(Vector3 開始、Vector3 終了、Color color=Color.white、floatduration=0.0f、bool DepthTest=true);

パラメータは、線の始点、線の終点、color 線の色、duration 線の継続時間です。

DepthTest ラインがカメラの近くのオブジェクトによって遮られているかどうか。

Debug.DrawRay(Vector3 start, Vector3 dir, Color color=Color.white, float period=0.0f, bool DepthTest=true);

パラメータは、開始レイの開始点、ディレクトリ レイの方向と長さ、カラー レイの色、デュレーション レイの継続時間、およびカメラの近くのオブジェクトによってカメラがブロックされているかどうかの DepthTest です。

次に、Debug.DrawRay メソッドを使用して上記のコードの場合にカメラをシミュレートし、次のコードを追加します。

// 画一条蓝线来模拟射线
Debug.DrawRay(origin, direction * 100, Color.blue, 100);

次に、Play プロジェクトを再起動し、A キーを押してシーン ビュー (ゲーム ビューではなく) に戻って表示します。

これで、Sphere1 から放出された青色のシミュレートされた光線が確認できます。

上の図から、光線は黄色のボール Sphere2 を通過しただけでなく、緑色のボール Sphere3 も通過したことがわかります。緑色のSphere3はどうやって入手できますか? これには、Physics.RaycastAll 関数の助けが必要です。この関数は Physics.Raycast 関数に似ていますが、返される結果は異なります。この関数の戻り値は RaycastHit の配列です。次に、次のようにコードを再度変更しましょう。

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            // 射线的起点(Sphere1的位置)
            Vector3 origin = transform.position;
            // 射线的方向(X轴负方向)
            Vector3 direction = Vector3.left;
            // 射线碰撞的目标对象
            //RaycastHit hitInfo;
            // 射线的最大长度
            float maxDistance = 100;

            // 创建射线,返回是否检测到碰撞对象
            //bool raycast = Physics.Raycast(origin, direction, out hitInfo, maxDistance);
            RaycastHit[] hitInfos = Physics.RaycastAll(origin, direction, maxDistance);

            // 如果发生碰撞,碰撞信息就被存储到 hitInfo 中
            //if (raycast)
            foreach(RaycastHit hitInfo in hitInfos)
            {
                // 获取碰撞点坐标
                Vector3 point = hitInfo.point;
                Debug.Log("碰撞点坐标:" + point);

                // 获取碰撞目标的名称
                string name = hitInfo.collider.name;
                Debug.Log("碰撞对象名称:" + name);

                // 获取目标的碰撞体组件
                Collider coll = hitInfo.collider;

                //获取目标的Transgorm组件
                Transform trans = hitInfo.transform;
            }

            // 画一条蓝线来模拟射线
            Debug.DrawRay(origin, direction * 100, Color.blue, 100);
        }
    }

プロジェクトを再生してみましょう。コンソール出力のスクリーンショットは次のとおりです。

実際のゲーム開発では、特定の範囲内で衝突が発生したかどうかを検出する必要がある場合があります。たとえば、周囲 100 メートル以内にゲーム オブジェクトがあるかどうかを検出し、存在する場合は積極的に攻撃します。現時点では、単一の光線を使用してそのような要件を満たすことはできません。Unity は、豊富なさまざまな形状の光線検出を提供します。ここでは、球体の衝突検出には Physics.OverlapSphere を、立方体検出には Physics.OverlapBox を使用できます。これらは Collider[] 配列を返します。たとえば、レイを使用して近くの 100 メートル以内にあるすべてのオブジェクトを検出します。

Collider[] コライダー = Physics.OverlapSphere(transform.postion, 100.0f);

foreach(コライダー内のコライダーコライダー){ …… }

最後に、カメラの別の使用方法を紹介します。それは、マウスをクリックしてシーン内のゲーム オブジェクトを選択することです。原理は非常に単純で、カメラ位置からマウスクリック位置まで光線を発射し、衝突検出を行うというものです。次に、「RayClickScript.cs」スクリプトを作成してカメラに添付しましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayClickScript : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        // 鼠标左键按下
        if (Input.GetMouseButtonDown(0))
        {
            // 从相机位置发射一条射线经过屏幕上的鼠标点击位置
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            // 声明一个射线碰撞信息类
            RaycastHit hit;

            // 进行碰撞检测
            bool res = Physics.Raycast(ray, out hit);

            // 如果产生了碰撞
            if(res){
                Debug.Log("碰撞点:" + hit.point);
                Debug.Log("碰撞目标:" + hit.transform.name);
            }
        }
    }
}

次に、プロジェクトを再生し、マウスを使用して緑色のボール Sphere3 をクリックします。

次は何か面白いことをやってみましょう。Plane 平面上の点をクリックし、緑色のボール Sphere3 をその点に移動させます。この興味深いことを実現するにはどうすればよいでしょうか? コードの変更は次のとおりです

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayClickScript : MonoBehaviour
{
    // 绿球Sphere3
    private GameObject sphere3;

    // 是否移动
    private bool isMove = false;

    // 目标点
    private Vector3 target = Vector3.zero;

    // Start is called before the first frame update
    void Start()
    {
        // 获取绿球Sphere3
        sphere3 = GameObject.Find("Sphere3");
    }

    // Update is called once per frame
    void Update()
    {
        // 鼠标左键按下
        if (Input.GetMouseButtonDown(0))
        {
            // 从相机位置发射一条射线经过屏幕上的鼠标点击位置
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            // 声明一个射线碰撞信息类
            RaycastHit hit;

            // 进行碰撞检测
            bool res = Physics.Raycast(ray, out hit);

            // 如果产生了碰撞
            if (res && hit.transform.name == "Plane")
            {
                // 目标点(Y轴上保持不变)
                isMove = true;
                target = new Vector3(hit.point.x, sphere3.transform.position.y, hit.point.z);
                //Debug.Log("碰撞点:" + hit.point);
                //Debug.Log("碰撞目标:" + hit.transform.name);
            }
        }

        // 如果发生碰撞就让绿球移动到目标点
        if (isMove)
        {
            // 绿球朝向目标点
            sphere3.transform.LookAt(target);

            // 角色移动到目标点的距离
            float distance = Vector3.Distance(target, sphere3.transform.position);

            // 没有到达目标点就一直移动下去
            if (distance > 0.1f)
            {
                // 旋转后向前移动即可
                sphere3.transform.Translate(transform.forward * 0.2f);
            }
            else
            {
                // 移动结束
                isMove = false;
            }
        }
    }
}

上記のコードについては説明しません。ただ再生して実行して効果を確認してください。

このコースに含まれるコンテンツは Baidu Netdisk に共有されています: https://pan.baidu.com/s/1e1jClK3MnN66GlxBmqoJWA?pwd=b2id

おすすめ

転載: blog.csdn.net/konkon2012/article/details/130479844