Compartir tecnología

Unity - Detección de rayos - RayCast

2024-07-12

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

Unity–Detección de rayos–RayCast

1.El significado de las pruebas radiográficas.

La detección de rayos, según su nombre, utiliza un rayo para detectar si golpea un objeto o varios objetos.

Las pruebas radiográficas constan de dos partes: rayosyDetección

2. ¿Dónde se pueden utilizar las pruebas radiográficas?

  1. juego de disparos
    • Jugador apuntando y disparando: detecta si la línea de visión del jugador se cruza con un enemigo u otro objetivo.
    • Trayectoria y efectos de la bala: simule la trayectoria de vuelo y el efecto de impacto de las balas.
  2. Interacción y UI
    • Detección de clic del mouse: detecta si el clic del mouse del jugador se cruza con un objeto del juego o elemento de la interfaz de usuario.
    • Interacción con pantalla táctil: detecta si el toque de un jugador cruza un elemento específico del juego en un dispositivo móvil.
  3. Controladores de personajes e IA
    • Detección de visión: los NPC o enemigos detectan jugadores u otros personajes dentro de un rango determinado.
    • Evitación de colisiones: los personajes de IA utilizan la detección de rayos para evitar colisiones cuando se mueven.
  4. Realidad Virtual (VR) y Realidad Aumentada (AR)
    • Seguimiento de ojos o manos: detecta la mirada del jugador o la posición de las manos en realidad virtual.
    • Interacción con objetos: detectar si el jugador se cruza con objetos virtuales en AR.

3.Rayo en Unidad

Los rayos se pueden ver en muchos lugares de la vida diaria, comoLinterna,lápiz láser ppt para pasar páginas, en medio de los años de QingyuCíclope

Un rayo consta de un punto de partida, una dirección y una distancia, a saber: origin , direction ydistance

En física, la distancia del rayo es infinita, por lo que el rayo físico tiene solo un punto de partida y una dirección. En el juego, la distancia máxima del rayo también está limitada por el sistema. Generalmente, personalizamos la distancia, por ejemplo. 1000,0 metros La siguiente es una descripción de Ray.

En Unity, Ray es una estructura. Los miembros básicos del volumen de la estructura incluyen.origin, direction yGetPointEs decir, el punto de partida, la dirección y la posición de un punto a cierta distancia a lo largo del rayo. El siguiente es el código de Ray en 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 Construyendo rayos

Según el código anterior, se puede ver que un rayo se puede construir directamente usando el constructor Ray, por ejemplo:

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

Según el código anterior, podemos ver que el punto inicial del rayo es el origen de las coordenadas mundiales en Unity (0,0,0), y la dirección del rayo es la dirección directa de las coordenadas mundiales.

3.2 Cómo mostrar rayos

En realidad, nuestro puntero láser y nuestra linterna se pueden ver, pero los rayos en Unity son invisibles. Por lo tanto, si queremos mostrar los rayos, podemos usar los siguientes métodos:

  • usarDebug.DrawRay()rayo de visualización
  • usarLineRendererEl componente dibuja el rayo.

4.Detección de rayos en Unity

Simplemente construir o mostrar los rayos no tiene significado. Es equivalente a sostener una herramienta en la mano y trabajar sin ella.

Cómo utilizar rayos para la detección de objetos.

Piénselo detenidamente: en nuestra vida diaria, nuestros punteros láser o linternas pueden mostrar puntos de luz en la pared o iluminar un lugar determinado después de emitir luz láser. Esto muestra que se puede interactuar con la pared, en otras palabras, el rayo choca. el objeto/ El objeto se detecta. Utilice la siguiente API en Unity para determinar si se detecta el objeto.

4.1Función 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

Para utilizar la detección de rayos, debe utilizar elPhysicsBiblioteca, que contiene funciones estáticas relacionadas con la física.

Al ver las funciones sobrecargadas anteriores, a menudo no sabemos cuál usar. De hecho, solo necesitamos las básicas. Para aquellos con muchos parámetros, en realidad usamos diferentes parámetros sobrecargados según las necesidades.

Primer vistazo a la versión básica de la API. public static bool Raycast(Ray ray); Según el valor de retorno, lo que podemos saber es si el rayo impacta en un objeto. Si impacta, devuelve verdadero. Si no, devuelve falso.

4.2Estructura HitInfo

Detección de rayos en otra APIpublic static bool Raycast(Ray ray, out RaycastHit hitInfo);Hay un parámetro más enhitInfo

El parámetro hitInfo es la estructura de información del objeto alcanzado por el rayo. La estructura es relativamente grande, lo que significa que contiene más información. Es el mismo que el resultado del objeto alcanzado por la detección de rayos en Unreal Engine (.FHitResult) son similares desde una perspectiva macro o desde una perspectiva práctica, cuando un rayo golpea un objeto, podemos obtener información sobre el objeto en sí y la información sobre el punto de impacto.

  • Información del objeto, toda la información se puede obtener mediante transformación.
  • Información del punto de vida

Obtener la información de un objeto es fácil de entender: como el nombre del objeto, el componente transfrom del objeto, la etiqueta del objeto...

Obtenga la información del punto de impacto, es decir, la información de un punto (punto de impacto) donde el rayo impacta el objeto: como las coordenadas del punto,Normal(normal), plano normal, el punto decolor del vértice

Lo siguiente esRaycastHitInformación de estructura, las de uso común tienen comentarios agregados. También hay muchas de uso común sin comentarios agregados, como.lightmapCoordSon coordenadas de mapa ligeras que se utilizan para renderizar.

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

Es decirHitInfoGuarda la información sobre el objeto que golpeamos, podemos usar esta información para hacer más cosas

4.3 capaMask

En unidad,layerMask Es un mecanismo de selección de capas de objetos que se utiliza para controlar la colisión física, la proyección de rayos, la detección de rayos y otras operaciones.configurandolayerMask, puede especificar qué capas deben incluirse o excluirse de estas operaciones.

Continúe hasta la API de detección de rayos más utilizada public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);medio.

En la API anterior, no hace falta decir que maxDistance es la distancia del rayo. El sistema también tiene un valor máximo establecido en.Mathf.InfinityTambién hay un parámetro LayerMask en esta API, que es la máscara de capa.

Para distinguir los objetos del juego en Unity, podemos distinguir los objetos del juego agregando etiquetas, es decir, agregando etiquetas. Sin embargo, las etiquetas son más problemáticas. Necesitamos ingresar manualmente el nombre de la etiqueta y es fácil cometer errores al escribir a mano. Al mismo tiempo, debido a que es una cadena, la velocidad de cálculo en la parte inferior de Unity es un poco más lenta. Existe un concepto de capa en Unity.

LayerMask en Unity contiene 32 capas, algunas de las cuales ya son utilizadas por el sistema, comoPlayercapa, UICapas. Todavía hay muchas capas que el sistema no utiliza. Podemos agregar capas y luego clasificarlas agregando capas de trabajo infantil a los objetos del juego.

¿Cómo agregar una capa? Debe hacer clic en Capa en el panel Inspector de cualquier objeto y luego hacer clic.Agregar capaEso es todo, luego especifique manualmente la capa para los objetos que deben modificarse.

Capa adicional de RayCast de Unity

Cómo usarlo después de agregar la capa manualmente.

existirpublic static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);mediolayereMaskAprendimos que hace unintTipo de número entero, sin embargo, no podemos completar los números directamente, las reglas de llenado usan operaciones de desplazamiento,

Cómo rellenar la máscara de capa:

  1. Obtener el valor de máscara de capa

    • Cada capa tiene un valor entero correspondiente, comenzando desde 0. Por ejemplo, la capa predeterminada (Predeterminada) tiene un valor de 0 y la capa de la interfaz de usuario suele ser 5.
    • Para crear una máscara de capa para una capa específica, puede usar 1 << LayerMask.NameToLayer("LayerName") . Esto devuelve un valor entero que representa la máscara de capa para esta capa.
  2. Combina varias capas

    • Si desea combinar varias capas, puede utilizar el operador OR bit a bit | . Por ejemplo,layerMask = LayerMask.GetMask("Layer1", "Layer2") Se creará una máscara de capa, que incluye "Capa1" y "Capa2".
  3. excluir capa

    • Para excluir una capa, primero puede crear una máscara que contenga todas las capas y luego usar el operador XOR bit a bit. ^ para excluir capas específicas. Por ejemplo,layerMask = ~LayerMask.GetMask("ExcludeLayer")
  4. Comprobar capa

    • Para verificar si un objeto está en la máscara de capa especificada, puede usar layerMask.value & (1 << gameObject.layer) . Si el resultado no es 0, significa que el objeto está en 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.Código de detección de rayos

El siguiente es el código que utiliza el parámetro hitInfo sin y con el parámetro 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.Alarma de rayos

Utilice la detección de rayos para implementar un puntero láser giratorio. Cuando se encuentra con un objeto, la longitud del rayo se reduce y es necesario dibujar el rayo.

Habilidades requeridas: Detección de rayos + Rotación + LineRender

El siguiente es el código

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

Código optimizado

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

Las representaciones son las siguientes.

Unity_RayCast.gif

7. Haga clic con el mouse para generar un efecto especial.

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