Technologieaustausch

Einheit – Strahlenerkennung – RayCast

2024-07-12

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

Unity–Ray Detection–RayCast

1. Die Bedeutung der Röntgenprüfung

Die Strahlenerkennung nutzt, wie der Name schon sagt, einen Strahl, um zu erkennen, ob er auf ein Objekt/mehrere Objekte trifft

Die Röntgenprüfung besteht aus zwei Teilen: StrahlenUndErkennung

2. Wo können Röntgenuntersuchungen eingesetzt werden?

  1. Schießspiel
    • Zielen und Schießen des Spielers: Erkennt, ob sich die Sichtlinie des Spielers mit einem Feind oder einem anderen Ziel schneidet.
    • Geschossflugbahn und -effekte: Simulieren Sie die Flugbahn und die Trefferwirkung von Geschossen.
  2. Interaktion und Benutzeroberfläche
    • Mausklickerkennung: Erkennen Sie, ob der Mausklick des Spielers ein Spielobjekt oder ein UI-Element schneidet.
    • Touchscreen-Interaktion: Erkennen Sie, ob die Berührung eines Spielers ein bestimmtes Spielelement auf einem mobilen Gerät schneidet.
  3. Charaktercontroller und KI
    • Sichterkennung: NPCs oder Feinde erkennen Spieler oder andere Charaktere innerhalb einer bestimmten Reichweite.
    • Kollisionsvermeidung: KI-Charaktere nutzen die Strahlenerkennung, um Kollisionen beim Bewegen zu vermeiden.
  4. Virtuelle Realität (VR) und Augmented Reality (AR)
    • Augen- oder Handverfolgung: Erkennen Sie die Blick- oder Handposition des Spielers in VR.
    • Objektinteraktion: Erkennen, ob der Spieler virtuelle Objekte in AR schneidet.

3.Strahl in Einheit

Strahlen sind im täglichen Leben an vielen Orten zu sehen, zTaschenlampe,PPT-Laser-Umblätterstift, in der Mitte der Qingyu-JahreZyklop

Ein Strahl besteht aus einem Startpunkt, einer Richtung und einer Entfernung, nämlich: origin , direction Unddistance

In der Physik ist die Entfernung des Strahls unendlich, daher hat der physische Strahl nur einen Startpunkt und eine Richtung. Im Spiel ist die maximale Entfernung des Strahls auch durch das System begrenzt 1000,0 Meter Das Folgende ist eine Beschreibung von Ray.

In Unity ist Ray eine Struktur. Die grundlegenden Elemente des Strukturvolumens umfassenorigin, direction UndGetPointDas heißt, der Startpunkt, die Richtung und die Position eines Punktes in einer bestimmten Entfernung entlang des Strahls. Das Folgende ist der Code für Ray in Unity

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 Strahlen konstruieren

Anhand des obigen Codes ist ersichtlich, dass ein Strahl direkt mit dem Ray-Konstruktor erstellt werden kann, zum Beispiel:

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

Gemäß dem obigen Code können wir sehen, dass der Startpunkt des Strahls der Ursprung der Weltkoordinaten in Einheit (0,0,0) ist und die Richtung des Strahls die Vorwärtsrichtung der Weltkoordinaten ist.

3.2 So zeigen Sie Strahlen an

In Wirklichkeit sind unser Laserpointer und unsere Taschenlampe sichtbar, aber die Strahlen in Unity sind unsichtbar. Wenn wir die Strahlen anzeigen möchten, können wir daher die folgenden Methoden verwenden:

  • verwendenDebug.DrawRay()Anzeigestrahl
  • verwendenLineRendererDie Komponente zeichnet den Strahl

4.Strahlenerkennung in Unity

Nur die Strahlen zu konstruieren oder darzustellen hat keinen Sinn. Es ist gleichbedeutend damit, ein Werkzeug in der Hand zu halten und ohne das Werkzeug zu arbeiten.

So nutzen Sie Strahlen zur Objekterkennung.

Denken Sie sorgfältig darüber nach: In unserem täglichen Leben können unsere Laserpointer oder Taschenlampen Lichtpunkte an der Wand anzeigen oder einen bestimmten Ort beleuchten, nachdem sie Laserlicht ausgesendet haben. Dies zeigt, dass mit der Wand interagiert werden kann, mit anderen Worten, dass der Strahl mit ihr kollidiert Das Objekt/ Das Objekt wurde erkannt. Verwenden Sie die folgende API in Unity, um festzustellen, ob das Objekt erkannt wurde.

4.1 Raycat-Funktion

// 基础版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

Um die Strahlenerkennung nutzen zu können, müssen Sie die verwendenPhysicsBibliothek, die statische Funktionen im Zusammenhang mit der Physik enthält

Wenn wir die oben genannten überladenen Funktionen sehen, wissen wir oft nicht, welche wir verwenden sollen. Tatsächlich benötigen wir nur die grundlegenden Funktionen. Für diejenigen mit vielen Parametern verwenden wir je nach Bedarf unterschiedliche überladene Parameter.

Schauen Sie sich zunächst die Basisversion der API an public static bool Raycast(Ray ray); Anhand des Rückgabewerts können wir erkennen, ob der Strahl ein Objekt trifft. Wenn nicht, wird „true“ zurückgegeben.

4.2HitInfo-Struktur

Ray-Erkennung in einer anderen APIpublic static bool Raycast(Ray ray, out RaycastHit hitInfo);Es gibt noch einen weiteren ParameterhitInfo

Der hitInfo-Parameter ist die Informationsstruktur des vom Strahl getroffenen Objekts. Die Struktur ist relativ groß, was bedeutet, dass sie mehr Informationen enthält. Dies ist das gleiche wie das Ergebnis der Strahlerkennung des Objekts in der Unreal Engine ().FHitResult) sind ähnlich, wenn ein Strahl auf ein Objekt trifft, können wir Informationen über das Objekt selbst und Informationen über den Trefferpunkt erhalten.

  • Objektinformationen, alle Informationen können durch Transformation erhalten werden
  • Informationen zu Trefferpunkten

Das Abrufen der Informationen eines Objekts ist leicht zu verstehen: z. B. der Name des Objekts, die Transfrom-Komponente des Objekts, das Tag des Objekts ...

Erhalten Sie die Informationen des Trefferpunkts, dh die Informationen eines Punktes (Trefferpunkts), an dem der Strahl auf das Objekt trifft: z. B. die Koordinaten des Punktes,Normal(normal), normale Ebene, der Punkt vonScheitelpunktfarbe

Das Folgende istRaycastHitStrukturinformationen, die häufig verwendeten haben Kommentare hinzugefügt. Es gibt auch viele häufig verwendete ohne hinzugefügte Kommentare, wie zlightmapCoordEs handelt sich um Lichtkartenkoordinaten, die zum Rendern verwendet werden.

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

Das heißtHitInfoSpeichert die Informationen über das Objekt, auf das wir stoßen. Wir können diese Informationen verwenden, um weitere Dinge zu tun

4.3 Schichtmaske

In der Einheit,layerMask Dabei handelt es sich um einen Mechanismus zur Auswahl der Objektebene, der zur Steuerung von physischen Kollisionen, Raycasting, Rayerkennung und anderen Vorgängen verwendet wird.indem man es einstelltlayerMaskkönnen Sie angeben, welche Ebenen in diese Vorgänge einbezogen oder ausgeschlossen werden sollen.

Fahren Sie mit der häufig verwendeten Strahlenerkennungs-API fort public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);Mitte.

In der obigen API ist maxDistance selbstverständlich die Entfernung des Strahls. Das System hat auch einen Maximalwert festgelegtMathf.InfinityIn dieser API gibt es auch einen Parameter „layerMask“, bei dem es sich um die Ebenenmaske handelt.

Um Spielobjekte in Unity zu unterscheiden, können wir Spielobjekte durch Hinzufügen von Tags unterscheiden, d. Gleichzeitig ist die Berechnungsgeschwindigkeit am unteren Ende von Unity etwas langsamer, da es sich um eine Zeichenfolge handelt. In Unity gibt es ein Ebenenkonzept.

Die LayerMask in Unity enthält 32 Layer, von denen einige bereits vom System verwendet werden, wie zPlayerSchicht, UIEs gibt immer noch viele Ebenen, die vom System nicht verwendet werden. Wir können Ebenen hinzufügen und sie dann klassifizieren, indem wir den Spielobjekten Kinderarbeitsebenen hinzufügen.

Wie füge ich eine Ebene hinzu? Sie müssen im Inspektorfenster eines beliebigen Objekts auf „Ebene“ klicken und dann auf „Ebene“ klickenEbene hinzufügenGeben Sie dann manuell die Ebene für die Objekte an, die geändert werden müssen.

Unity_RayCast_AddLayer

So verwenden Sie es, nachdem Sie die Ebene manuell hinzugefügt haben.

existierenpublic static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);MittelayereMaskWir haben gelernt, dass es ein machtintGanzzahltyp, wir können die Zahlen jedoch nicht direkt eingeben, die Füllregeln verwenden Schiebeoperationen,

So füllen Sie LayerMask aus:

  1. Holen Sie sich den Wert der Ebenenmaske

    • Jede Ebene hat einen entsprechenden ganzzahligen Wert, beginnend bei 0. Beispielsweise hat die Standardebene (Default) den Wert 0 und die UI-Ebene normalerweise den Wert 5.
    • Um eine LayerMask für eine bestimmte Ebene zu erstellen, können Sie verwenden 1 << LayerMask.NameToLayer("LayerName") . Dies gibt einen ganzzahligen Wert zurück, der die LayerMask für diese Ebene darstellt.
  2. Kombinieren Sie mehrere Schichten

    • Wenn Sie mehrere Ebenen kombinieren möchten, können Sie den bitweisen ODER-Operator verwenden | . Zum Beispiel,layerMask = LayerMask.GetMask("Layer1", "Layer2") Es wird eine LayerMask erstellt, einschließlich „Layer1“ und „Layer2“.
  3. Ebene ausschließen

    • Um eine Ebene auszuschließen, können Sie zunächst eine Maske erstellen, die alle Ebenen enthält, und dann den bitweisen XOR-Operator verwenden ^ um bestimmte Ebenen auszuschließen. Zum Beispiel,layerMask = ~LayerMask.GetMask("ExcludeLayer")
  4. Ebene prüfen

    • Um zu überprüfen, ob sich ein Objekt in der angegebenen LayerMask befindet, können Sie verwenden layerMask.value & (1 << gameObject.layer) . Wenn das Ergebnis nicht 0 ist, bedeutet dies, dass sich das Objekt in LayerMask befindet.
    // 示例代码
    // 创建一个包含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.Ray-Erkennungscode

Im Folgenden sehen Sie den Code, der den hitInfo-Parameter ohne und mit dem hitInfo-Parameter verwendet:

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.Ray-Alarm

Verwenden Sie die Strahlerkennung, um einen rotierenden Laserpointer zu implementieren. Wenn Sie auf ein Objekt stoßen, wird die Strahllänge reduziert und der Strahl muss gezeichnet werden.

Erforderliche Fähigkeiten: Ray Detection + Rotation + LineRender

Das Folgende ist der Code

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

Optimierter Code

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

Die Darstellungen sind wie folgt

Unity_RayCast.gif

7. Klicken Sie mit der Maus, um einen Spezialeffekt zu erzeugen

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