Обмен технологиями

Unity — Обнаружение лучей — RayCast

2024-07-12

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

Unity – Обнаружение лучей – RayCast

1. Значение рентгенологического контроля.

Обнаружение лучей, согласно названию, использует луч для определения того, попадает ли он на объект/несколько объектов.

Рентгенологическое исследование состоит из двух частей: ЛучииОбнаружение

2. Где можно использовать рентгенографическое исследование?

  1. стрелялка
    • Прицеливание и стрельба игрока: определяет, пересекается ли линия обзора игрока с врагом или другой целью.
    • Траектория и эффекты пули: имитируйте траекторию полета и эффект попадания пуль.
  2. Взаимодействие и пользовательский интерфейс
    • Обнаружение щелчка мыши: определите, пересекается ли щелчок мыши игрока с игровым объектом или элементом пользовательского интерфейса.
    • Взаимодействие с сенсорным экраном: определите, пересекает ли прикосновение игрока определенный игровой элемент на мобильном устройстве.
  3. Контроллеры персонажей и искусственный интеллект
    • Обнаружение зрения: NPC или враги обнаруживают игроков или других персонажей в определенном диапазоне.
    • Предотвращение столкновений: ИИ-персонажи используют обнаружение лучей, чтобы избежать столкновений при движении.
  4. Виртуальная реальность (VR) и дополненная реальность (AR)
    • Отслеживание глаз или рук: распознавайте взгляд или положение рук игрока в виртуальной реальности.
    • Взаимодействие с объектами: определение того, пересекается ли игрок с виртуальными объектами в AR.

3.Рэй в Unity

Лучи можно увидеть во многих местах повседневной жизни, например, вфонарик,PPT лазерная ручка для переворота страниц, в середине правления ЦинъюйЦиклоп

Луч состоит из начальной точки, направления и расстояния, а именно: origin , direction иdistance

В физике расстояние луча бесконечно, поэтому физический луч имеет только одну начальную точку и одно направление. В игре максимальное расстояние луча также ограничено системой. Обычно мы настраиваем расстояние, например. 1000,0 метров Ниже приводится описание Рэя.

В Unity Ray представляет собой структуру. Основные элементы структуры тома включают в себя.origin, direction иGetPointТо есть начальная точка, направление и положение точки на определенном расстоянии вдоль луча. Ниже приведен код Ray в 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 Построение лучей

По приведенному выше коду видно, что луч можно построить напрямую с помощью конструктора Ray, например:

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

Согласно приведенному выше коду мы видим, что начальной точкой луча является начало мировых координат в Unity (0,0,0), а направление луча — это прямое направление мировых координат.

3.2 Как отображать лучи

В действительности нашу лазерную указку и фонарик видно, но лучи в Unity невидимы. Поэтому, если мы хотим отобразить лучи, мы можем использовать следующие методы:

  • использоватьDebug.DrawRay()дисплейный луч
  • использоватьLineRendererКомпонент рисует луч

4.Обнаружение лучей в Unity

Просто создавать или отображать лучи не имеет смысла. Это все равно, что держать инструмент в руке и работать без него.

Как использовать лучи для обнаружения объектов.

Подумайте об этом внимательно: в нашей повседневной жизни наши лазерные указки или фонарики могут отображать световые пятна на стене или освещать определенное место после излучения лазерного света. Это показывает, что со стеной можно взаимодействовать, другими словами, луч сталкивается с ней. объект/ Объект обнаружен. Используйте следующий API в Unity, чтобы определить, обнаружен ли объект.

4.1 Функция Raycat

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

Чтобы использовать обнаружение лучей, вам необходимо использоватьPhysicsБиблиотека, содержащая статические функции, связанные с физикой.

Видя приведенные выше перегруженные функции, мы часто не знаем, какую из них использовать. На самом деле нам нужны только базовые функции. Для тех, у кого много параметров, мы фактически используем разные перегруженные параметры в зависимости от потребностей.

Сначала взгляните на базовую версию API public static bool Raycast(Ray ray); Согласно возвращаемому значению мы можем узнать, попадает ли луч в объект. Если он попадает, он возвращает true. Если нет, он возвращает false.

4.2 Структура HitInfo

Обнаружение лучей в другом APIpublic static bool Raycast(Ray ray, out RaycastHit hitInfo);Есть еще один параметрhitInfo

Параметр hitInfo — это информационная структура объекта, на который попадает луч. Структура относительно большая, что означает, что она содержит больше информации. Это то же самое, что и результат обнаружения луча в Unreal Engine ().FHitResult) схожи с макро- или с практической точки зрения: когда луч попадает в объект, мы можем получить информацию о самом объекте и информацию о точке попадания.

  • Информация об объекте, всю информацию можно получить посредством преобразования
  • Информация о хит-пойнтах

Получение информации об объекте легко понять: например, имя объекта, компонент преобразования объекта, тег объекта...

Получите информацию о точке попадания, то есть информацию о точке (точке попадания), где луч попадает в объект: например, координаты точки,Нормальный(нормальная), нормальная плоскость, точкацвет вершин

Ниже приводитсяRaycastHitИнформация о структуре, часто используемые из них имеют добавленные комментарии. Есть также много часто используемых без добавленных комментариев, например.lightmapCoordЭто координаты карты освещения, используемые для рендеринга.

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

То естьHitInfoСохраняет информацию об объекте, который мы ударили, мы можем использовать эту информацию, чтобы делать больше вещей

4.3 слойМаска

В Единстве,layerMask Это механизм выбора объектного слоя, используемый для управления физическими столкновениями, приведением лучей, обнаружением лучей и другими операциями.установивlayerMask, вы можете указать, какие слои следует включить или исключить из этих операций.

Вернитесь к часто используемому API обнаружения лучей. public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);середина.

В приведенном выше API само собой разумеется, что maxDistance — это расстояние луча. В системе также установлено максимальное значение.Mathf.InfinityВ этом API также есть параметр LayerMask, который является маской слоя.

Чтобы различать игровые объекты в Unity, мы можем различать игровые объекты, добавляя теги, однако с тегами сложнее вводить имя тега, и при вводе вручную легко допустить ошибку. В то же время, поскольку это строка, скорость вычислений в нижней части Unity немного медленнее. В Unity есть концепция слоя.

LayerMask в Unity содержит 32 слоя, некоторые из которых уже используются системой, напримерPlayerслой, UIСлои Еще есть много слоев, которые не используются системой. Мы можем добавлять слои, а затем классифицировать их, добавляя слои детского труда к игровым объектам.

Как добавить слой? Вам нужно нажать «Слой» на панели «Инспектор» любого объекта, а затем нажать «Слой».ДобавитьСлойВсе, далее вручную укажите слой для объектов, которые необходимо изменить.

Unity_RayCast_AddLayer

Как его использовать после добавления слоя вручную.

существоватьpublic static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);серединаlayereMaskМы узнали, что это делаетintТип целого числа, однако мы не можем заполнять числа напрямую, правила заполнения используют операции сдвига,

Как заполнить маску слоя:

  1. Получить значение маски слоя

    • Каждый слой имеет соответствующее целочисленное значение, начиная с 0. Например, слой по умолчанию (Default) имеет значение 0, а слой пользовательского интерфейса обычно равен 5.
    • Чтобы создать маску слоя для определенного слоя, вы можете использовать 1 << LayerMask.NameToLayer("LayerName") . Возвращает целочисленное значение, представляющее маску слоя для этого слоя.
  2. Объединение нескольких слоев

    • Если вы хотите объединить несколько слоев, вы можете использовать побитовый оператор ИЛИ. | . Например,layerMask = LayerMask.GetMask("Layer1", "Layer2") Будет создана маска слоя, включающая «Слой1» и «Слой2».
  3. исключить слой

    • Чтобы исключить слой, вы можете сначала создать маску, содержащую все слои, а затем использовать побитовый оператор XOR. ^ для исключения определенных слоев. Например,layerMask = ~LayerMask.GetMask("ExcludeLayer")
  4. Проверить слой

    • Чтобы проверить, находится ли объект в указанной маске слоя, вы можете использовать layerMask.value & (1 << gameObject.layer) . Если результат не равен 0, это означает, что объект находится в 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. Код обнаружения лучей

Ниже приведен код, который использует параметр hitInfo без параметра 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. Лучевая сигнализация

Используйте обнаружение лучей для реализации вращающейся лазерной указки. При встрече с объектом длина луча уменьшается, и его необходимо нарисовать.

Требуемые навыки: Обнаружение лучей + Вращение + LineRender.

Ниже приведен код

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

Оптимизированный код

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

Визуализации следующие

Unity_RayCast.gif

7. Щелкните мышью, чтобы создать специальный эффект.

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