Berbagi teknologi

Unity--Deteksi Sinar--RayCast

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Persatuan–Deteksi Sinar–RayCast

1.Pengertian pemeriksaan radiografi

Deteksi sinar, sesuai dengan namanya, menggunakan sinar untuk mendeteksi apakah mengenai suatu objek/beberapa objek

Pengujian radiografi terdiri dari dua bagian: sinarDanDeteksi

2. Dimana pengujian radiografi dapat digunakan?

  1. permainan menembak
    • Membidik dan Menembak Pemain: Mendeteksi apakah garis pandang pemain bersinggungan dengan musuh atau target lainnya.
    • Lintasan dan efek peluru: Mensimulasikan jalur penerbangan dan efek pukulan peluru.
  2. Interaksi dan UI
    • Deteksi klik mouse: Mendeteksi apakah klik mouse pemain bersinggungan dengan objek game atau elemen UI.
    • Interaksi layar sentuh: Mendeteksi apakah sentuhan pemain bersinggungan dengan elemen game tertentu di perangkat seluler.
  3. Pengontrol karakter dan AI
    • Deteksi penglihatan: NPC atau musuh mendeteksi pemain atau karakter lain dalam jarak tertentu.
    • Penghindaran Tabrakan: Karakter AI menggunakan deteksi sinar untuk menghindari tabrakan saat bergerak.
  4. Realitas Virtual (VR) dan Realitas Tertambah (AR)
    • Pelacakan mata atau tangan: Mendeteksi pandangan atau posisi tangan pemain di VR.
    • Interaksi objek: Mendeteksi apakah pemain berpotongan dengan objek virtual di AR.

3. Sinar dalam Persatuan

Sinar dapat dilihat di banyak tempat dalam kehidupan sehari-hari, sepertisenter,pena pembalik halaman laser ppt, di pertengahan tahun QingyuCyclops

Sinar terdiri atas titik pangkal, arah dan jarak, yaitu: origin , direction Dandistance

Dalam fisika, jarak sinar tidak terbatas, sehingga sinar fisik hanya memiliki satu titik awal dan satu arah. Dalam permainan, jarak maksimum sinar juga dibatasi oleh sistem 1000,0 meter Berikut ini adalah deskripsi Ray.

Dalam Unity, Ray adalah sebuah struktur. Anggota dasar dari volume struktur meliputiorigin, direction DanGetPointYaitu titik awal, arah dan kedudukan suatu titik pada jarak tertentu sepanjang sinar

using System;

namespace UnityEngine
{
    public struct Ray : IFormattable
    {        
        public Ray(Vector3 origin, Vector3 direction);
        public Vector3 origin { get; set; } // 起点(向量)
        public Vector3 direction { get; set; }// 方向(向量)
        public Vector3 GetPoint(float distance);// 沿着射线一定距离的点(向量)
        public override string ToString();
        public string ToString(string format);
        public string ToString(string format, IFormatProvider formatProvider);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3.1 Membangun sinar

Berdasarkan kode di atas terlihat bahwa sebuah sinar dapat dikonstruksikan langsung dengan menggunakan konstruktor Ray, misalnya:

Ray ray = new Ray(Vector3.zero, Vector3.forward); // 射线的起点 + 射线的方向
  • 1

Berdasarkan kode di atas, kita dapat melihat bahwa titik awal sinar adalah asal koordinat dunia dalam Unity (0,0,0), dan arah sinar adalah arah maju koordinat dunia.

3.2 Cara menampilkan sinar

Sebenarnya laser pointer dan senter kita bisa terlihat, tapi sinar di Unity tidak terlihat. Oleh karena itu, jika kita ingin menampilkan sinarnya, kita bisa menggunakan cara berikut:

  • menggunakanDebug.DrawRay()sinar tampilan
  • menggunakanLineRendererKomponen menggambar sinar

4.Deteksi sinar di Unity

Membangun atau menampilkan sinar saja tidak ada artinya. Hal ini setara dengan memegang alat di tangan Anda dan bekerja tanpa alat tersebut.

Cara menggunakan sinar untuk mendeteksi objek.

Pikirkan baik-baik: Dalam kehidupan kita sehari-hari, penunjuk laser atau senter kita dapat menampilkan titik cahaya di dinding atau menerangi tempat tertentu setelah memancarkan sinar laser. Hal ini menunjukkan bahwa dinding dapat berinteraksi dengan, dengan kata lain, sinar tersebut bertabrakan objek/Objek terdeteksi. Gunakan API berikut di Unity untuk menentukan apakah objek terdeteksi.

4.1 Fungsi kucing pari

// 基础版API
public static bool Raycast(Ray ray);

public static bool Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask);
public static bool Raycast(Ray ray, out RaycastHit hitInfo);
public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance);
                           
// 常用版API
public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);
                           
public static bool Raycast(Ray ray, float maxDistance);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Untuk menggunakan deteksi sinar, Anda perlu menggunakanPhysicsLibrary, yang berisi fungsi-fungsi statis yang berhubungan dengan fisika

Melihat fungsi-fungsi yang kelebihan beban di atas, seringkali kita tidak tahu harus menggunakan yang mana, padahal kita hanya membutuhkan yang dasar saja, untuk yang parameternya banyak, sebenarnya kita menggunakan parameter kelebihan yang berbeda-beda sesuai kebutuhan.

Pertama lihat versi dasar API public static bool Raycast(Ray ray); Berdasarkan nilai yang dikembalikan, yang dapat kita ketahui adalah apakah sinar mengenai suatu benda. Jika mengenai, maka akan menghasilkan nilai benar.

4.2Struktur Info Hit

Deteksi sinar di API lainpublic static bool Raycast(Ray ray, out RaycastHit hitInfo);Ada satu parameter lagi di dalamnyahitInfo

Parameter hitInfo merupakan struktur informasi dari objek yang terkena sinar. Strukturnya relatif besar yang berarti mengandung lebih banyak informasi. Hal ini sama dengan hasil deteksi objek yang terkena sinar di Unreal Engine (FHitResult) serupa. Dari sudut pandang makro atau dari sudut pandang praktis, ketika sinar mengenai suatu benda, kita dapat memperoleh informasi tentang benda itu sendiri dan informasi tentang titik tumbukannya.

  • Informasi objek, semua informasi dapat diperoleh melalui transformasi
  • Informasi titik sasaran

Memperoleh informasi suatu objek mudah untuk dipahami: seperti nama objek, komponen transfrom objek, Tag objek...

Memperoleh informasi mengenai hit point yaitu informasi suatu titik (hit point) dimana sinar mengenai benda: seperti koordinat titik,Normal(normal), bidang normal, titikwarna simpul

Berikut ini adalahRaycastHitInformasi struktur, yang biasa digunakan ada komentar tambahanlightmapCoordItu adalah koordinat peta cahaya, yang digunakan untuk rendering.

namespace UnityEngine
{
    public struct RaycastHit
    {
        public Collider collider { get; }						// 碰撞器
        public int colliderInstanceID { get; }
        public Vector3 point { get; set; }						// 击中的点(命中点)
        public Vector3 normal { get; set; }						// 命中点的法线
        public Vector3 barycentricCoordinate { get; set; }		 // 重心坐标
        public float distance { get; set; }						// 命中点距离射线起点的距离
        public int triangleIndex { get; }
        public Vector2 textureCoord { get; }
        public Vector2 textureCoord2 { get; }
        public Transform transform { get; }						// Transform组件
        public Rigidbody rigidbody { get; }						// 刚体组件
        public ArticulationBody articulationBody { get; }
        public Vector2 lightmapCoord { get; }
        public Vector2 textureCoord1 { get; }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

ArtinyaHitInfoMenyimpan informasi tentang objek yang kita tabrak, kita dapat menggunakan informasi ini untuk melakukan lebih banyak hal

4.3 lapisanMask

Dalam Persatuan,layerMask Ini adalah mekanisme pemilihan lapisan objek yang digunakan untuk mengontrol tabrakan fisik, pengecoran sinar, deteksi sinar, dan operasi lainnya.dengan mengaturlayerMask, Anda dapat menentukan lapisan mana yang harus disertakan atau dikecualikan dari operasi ini.

Lanjutkan kembali ke API deteksi sinar yang umum digunakan public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);tengah.

Dalam API di atas, sudah jelas bahwa maxDistance adalah jarak sinar. Sistem juga memiliki nilai maksimum yang disetelMathf.InfinityAda juga parameter layerMask di API ini, yaitu layer mask.

Untuk membedakan objek game di Unity, kita dapat membedakan objek game dengan menambahkan tag, yaitu menambahkan Tag. Namun, Tag lebih merepotkan. Kita perlu memasukkan nama tag secara manual, dan mudah untuk membuat kesalahan saat mengetik dengan tangan. Pada saat yang sama, karena ini adalah string, kecepatan perhitungan di bagian bawah Unity sedikit lebih lambat. Ada konsep lapisan di Unity.

LayerMask di Unity berisi 32 layer, beberapa di antaranya sudah digunakan oleh sistem, sepertiPlayerlapisan, UILapisan. Masih banyak lapisan yang tidak digunakan oleh sistem. Kita dapat menambahkan lapisan dan kemudian mengklasifikasikannya dengan menambahkan lapisan pekerja anak ke objek permainan.

Bagaimana cara menambahkan lapisan? Anda perlu mengklik Lapisan pada panel Inspektur objek apa pun, lalu klikTambahkan LapisanItu saja, lalu tentukan secara manual layer untuk objek yang perlu dimodifikasi.

Unity_RayCast_TambahLapisan

Cara menggunakannya setelah menambahkan Layer secara manual.

adapublic static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);tengahlayereMaskKami belajar bahwa itu membuat aintJenis bilangan bulat, namun angkanya tidak bisa kita isi secara langsung, aturan pengisiannya menggunakan operasi shift,

Cara mengisi layerMask:

  1. Dapatkan nilai Layer Mask

    • Setiap lapisan memiliki nilai integer yang sesuai, dimulai dari 0. Misalnya, lapisan default (Default) memiliki nilai 0, dan lapisan UI biasanya 5.
    • Untuk membuat layerMask untuk lapisan tertentu, Anda dapat menggunakan 1 << LayerMask.NameToLayer("LayerName") . Ini mengembalikan nilai integer yang mewakili layerMask untuk lapisan ini.
  2. Gabungkan beberapa lapisan

    • Jika Anda ingin menggabungkan beberapa lapisan, Anda dapat menggunakan operator bitwise OR | . Misalnya,layerMask = LayerMask.GetMask("Layer1", "Layer2") LayerMask akan dibuat, termasuk "Layer1" dan "Layer2".
  3. kecualikan lapisan

    • Untuk mengecualikan suatu lapisan, pertama-tama Anda dapat membuat topeng yang berisi semua lapisan dan kemudian menggunakan operator XOR bitwise ^ untuk mengecualikan lapisan tertentu. Misalnya,layerMask = ~LayerMask.GetMask("ExcludeLayer")
  4. Periksa lapisan

    • Untuk memeriksa apakah suatu objek ada di layerMask yang ditentukan, Anda dapat menggunakan layerMask.value & (1 << gameObject.layer) . Jika hasilnya bukan 0 berarti objek tersebut ada di layerMask.
    // 示例代码
    // 创建一个包含Layer1和Layer2的layerMask
    int layerMask = LayerMask.GetMask("Layer1", "Layer2");
    
    // 排除Layer3
    layerMask = ~LayerMask.GetMask("Layer3");
    
    // 使用layerMask进行射线检测
    RaycastHit hit;
    if (Physics.Raycast(ray, out hit, maxDistance, layerMask))
    {
        // 处理射线击中的对象
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

5. Kode deteksi sinar

Berikut kode yang menggunakan parameter hitInfo tanpa dan dengan parameter hitInfo:

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

public class RayCast : MonoBehaviour
{
    /
    // 构建一条射线 : 射线产生的起始点 + 射线的方向.
    private Ray ray1 = new Ray(Vector3.zero, Vector3.forward);
    // 射线距离
    private float rayDistance = 100.0f;
   
    // 击中的判定结果
    bool hitResult = false;
    // 射线击中的物体
    private RaycastHit hitInfo;

    void Start()
    {
        // 不含有hitInfo的函数
        bool result1 = Physics.Raycast(Vector3.zero + new Vector3(0,0,10), Vector3.forward, 1000.0f, 1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);
        if (result1)
        {
            Debug.Log("射线击中物体");
        }
        // 含有hitInfo的函数
		hitResult =  Physics.Raycast(ray1, out hitInfo, rayDistance, 1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);
        if (hitResult == true)
        {
            print(hitInfo.collider.name);
            print(hitInfo.transform);
            print(hitInfo.point);
        }
    }       
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

6.Sinar alarm

Gunakan deteksi sinar untuk menerapkan penunjuk laser yang berputar. Saat bertemu dengan suatu objek, panjang sinar berkurang, dan sinar tersebut perlu ditarik.

Keterampilan yang dibutuhkan: Deteksi Sinar + Rotasi + LineRender

Berikut ini adalah kodenya

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

public class RotateRay : MonoBehaviour
{
    // LinerRender组件
    private LineRenderer lineRenderer = null;

    // 构建一条射线 : 射线产生的起始点 + 射线的方向.
    private Ray ray = new Ray(Vector3.zero, Vector3.forward);
    // 射线距离
    private float rayDistance = 1000.0f;
    // 击中的判定结果
    bool hitResult = false;
    // 射线击中的物体
    private RaycastHit hitInfo;

    void Start()
    {
        // 添加线条绘制组件
        lineRenderer = this.gameObject.AddComponent<LineRenderer>();
        InitLineRenderer(lineRenderer);

        // 设置射线的起点和方向
        ray.origin = this.transform.position;
        ray.direction = this.transform.forward;
    }

    // Update is called once per frame
    void Update()
    {
        // 重新设置射线的位置
        ray.origin = this.transform.position;
        ray.direction = this.transform.forward;

        // 旋转游戏对象 -- 每秒旋转60°
        Quaternion quaternion = Quaternion.AngleAxis(60f * Time.deltaTime, this.transform.up);
        this.transform.rotation *= quaternion;        

        // 判断击中的物体
        hitResult =  Physics.Raycast(ray, out hitInfo, rayDistance, 1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);
        if (hitResult == true)
        {
            print(hitInfo.collider.name);
        }
        // 显示并更新射线
        UpdateLineRendererByRay(lineRenderer, ray, hitResult,hitInfo, rayDistance);
    }

    /// <summary>
    /// 初始化线条渲染组件
    /// </summary>
    void InitLineRenderer(LineRenderer lineRenderer)
    {
        // lineRenderer = this.gameObject.AddComponent<LineRenderer>();
        lineRenderer.positionCount = 2;
        lineRenderer.startWidth = 0.2f;
        lineRenderer.endWidth = 0.2f;
        lineRenderer.startColor = Color.red;
        lineRenderer.endColor = Color.green;
    }

    /// <summary>
    /// 击中物体的时候修改射线的长度
    /// </summary>
    /// <param name="lineRenderer">lineRenderer组件</param>
    /// <param name="ray">射线</param>
    /// <param name="hitResult">是否命中物体</param>
    /// <param name="hitInfo">命中物体信息</param>
    /// <param name="rayDistance">射线距离</param>
    void UpdateLineRendererByRay(LineRenderer lineRenderer,Ray ray, bool hitResult, RaycastHit hitInfo, float rayDistance)
    {
        if (lineRenderer == null || lineRenderer.positionCount < 2)
        {
            Debug.Log("LineRender组件不可以使用");
            return;
        }

        // 修改起点位置
        lineRenderer.SetPosition(0, ray.origin);
        // 修改终点位置
        if (hitResult == true)
        {
            lineRenderer.SetPosition(1, hitInfo.point);
        }
        else 
        {
            lineRenderer.SetPosition(1, ray.GetPoint(rayDistance));
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

Kode yang dioptimalkan

using UnityEngine;

public class RotateRay : MonoBehaviour
{
    private LineRenderer lineRenderer;
    private Ray ray;
    private float rayDistance = 1000.0f;
    private RaycastHit hitInfo;

    void Start()
    {
        lineRenderer = this.gameObject.AddComponent<LineRenderer>();
        InitLineRenderer(lineRenderer);
        ray = new Ray(Vector3.zero, Vector3.forward);
    }

    void Update()
    {
        UpdateRayPosition();
        RotateObject();
        PerformRaycast();
        UpdateLineRenderer();
    }

    void InitLineRenderer(LineRenderer lineRenderer)
    {
        lineRenderer.positionCount = 2;
        lineRenderer.startWidth = 0.2f;
        lineRenderer.endWidth = 0.2f;
        lineRenderer.startColor = Color.red;
        lineRenderer.endColor = Color.green;
    }

    void UpdateRayPosition()
    {
        ray.origin = this.transform.position;
        ray.direction = this.transform.forward;
    }

    void RotateObject()
    {
        Quaternion rotation = Quaternion.AngleAxis(60f * Time.deltaTime, this.transform.up);
        this.transform.rotation *= rotation;
    }

    void PerformRaycast()
    {
        int layerMask = 1 << LayerMask.NameToLayer("Default");
        hitInfo = new RaycastHit(); // 初始化hitInfo,避免未击中时的错误
        Physics.Raycast(ray, out hitInfo, rayDistance, layerMask, QueryTriggerInteraction.UseGlobal);
    }

    void UpdateLineRenderer()
    {
        if (lineRenderer == null || lineRenderer.positionCount < 2)
        {
            Debug.LogError("LineRenderer component is not available or not properly initialized.");
            return;
        }

        lineRenderer.SetPosition(0, ray.origin);
        lineRenderer.SetPosition(1, hitInfo.collider != null ? hitInfo.point : ray.GetPoint(rayDistance));
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

Rendernya adalah sebagai berikut

Unity_RayCast.gif

7. Klik mouse untuk menghasilkan efek khusus

using UnityEngine;

public class  CameraRay: MonoBehaviour
{
    // 射线距离
    private float rayDistance = 1000.0f;
    // 特效Prefab--外部可以自定义特效
    public GameObject effectPrefab;

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // 从摄像机发出一条射线
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hitInfo;

            // 如果射线击中了指定层级的物体
            if (Physics.Raycast(ray, out hitInfo, rayDistance, LayerMask.GetMask("Wall")))
            {
                // 生成特效 --格局法线来计算特效位置
                GameObject effectObject = Instantiate(effectPrefab, hitInfo.point, Quaternion.LookRotation(hitInfo.normal));
                // 销毁特效,参数为延迟时间
                Destroy(effectObject, EffectPrefab.GetComponent<ParticleSystem>().main.duration);
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29