Unity: simula la visión de IA

El sistema visual humano tiene las siguientes características:

  1. La distancia es limitada. Puede ver claramente de cerca, pero no claramente de lejos.
  2. Se oscurece fácilmente. No puede atravesar ningún obstáculo opaco.
  3. El campo de visión es de aproximadamente 90 grados. Date cuenta de que la parte frontal es rica en información, con colores y detalles; date cuenta de que la parte exterior sólo tiene información de contorno y movimiento.
  4. La atención es limitada. Cuando se enfoca en un lugar u objeto específico, se ignoran otras partes. Por ejemplo, la venda en los ojos en la magia siempre puede engañar a la audiencia.

La simulación de la visión de IA se basa en las características básicas anteriores, y existen varias ideas de implementación sobre esta base. Si se parte de la segunda característica, es fácil pensar que los rayos también tienen la característica de no atravesar objetos, y se sospecha que también se puede establecer la distancia de lanzamiento. La mayor dificultad radica en el hecho de que el campo de visión es de aproximadamente 90°: en los juegos 3D de visión libre, el campo de visión es un cono, mientras que en los juegos de arriba hacia abajo, el campo de visión es un área en forma de abanico. Ni los conos ni las áreas de sectores pueden simularse directamente mediante formas simples como rayos, rayos esféricos o rayos de caja, y se debe encontrar una solución flexible.

Con respecto al problema de usar rayos para simular un área de sector, aquí hay un método fácil de entender: use múltiples rayos para simular el área.

El guión y las representaciones son los siguientes:

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

public class AIEnemy : MonoBehaviour
{
    public int viewRadius = 4;  //视野距离
    public int viewLines = 30; //射线数量
    void Start(){ }

   
    void Update()
    {
        FieldOfView();
         
    }
    void FieldOfView()
    {
        //获得最左边那条射线的向量,相对正前方,角度是-45°
        Vector3 forward_left = Quaternion.Euler(0, -45, 0) * transform.forward * viewRadius;
        //依次处理每条射线
        for(int i = 0; i <= viewLines; i++)
        {
            Vector3 v = Quaternion.Euler(0, (90.0f / viewLines) * i, 0) * forward_left;
            //角色位置+v,就是射线终点pos
            Vector3 pos = transform.position + v;
            //从玩家为之到pos画线段,只会在编辑器里看到
            Debug.DrawLine(transform.position, pos, Color.red);
        }
    }
}

 6580e9a0e296452c9a8e469c04c4a939.png

Pero este es sólo el primer paso, y los rayos realmente necesitan ser emitidos. Sólo cuando se emite el rayo se puede determinar si ha encontrado un obstáculo. Si encuentra un obstáculo, el punto final de la línea de visión caerá en el punto de colisión. Modifique el código FieldOfView de la siguiente manera:

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

public class AIEnemy : MonoBehaviour
{
    public int viewRadius = 4;  //视野距离
    public int viewLines = 30; //射线数量
    void Start(){ }

   
    void Update()
    {
        FieldOfView();
    }
    void FieldOfView()
    {
        //获得最左边那条射线的向量,相对正前方,角度是-45°
        Vector3 forward_left = Quaternion.Euler(0, -45, 0) * transform.forward * viewRadius;
        //依次处理每条射线
        for(int i = 0; i <= viewLines; i++)
        {
            Vector3 v = Quaternion.Euler(0, (90.0f / viewLines) * i, 0) * forward_left;
            //角色位置+v,就是射线终点pos
            Vector3 pos = transform.position + v;

            //实际发射射线。注意RayCast的参数,重载很多容易搞错
            RaycastHit hitInfo;
            if(Physics.Raycast(transform.position,v,out hitInfo, viewRadius))
            {
                //碰到物体,终点改为碰到的点
                pos = hitInfo.point;
            }

            //从玩家位置到pos画线段,只会在编辑器里看到
            Debug.DrawLine(transform.position, pos, Color.red);
        }
    }
}

Representación:

81d26d83217049f3a3c636cc1f97dce6.png

Después de la modificación, si utiliza algún objeto para bloquear el rayo, encontrará que el rayo efectivamente está bloqueado.

En el juego real, cuando el rayo se enfoca en el jugador, significa que el enemigo ha descubierto al jugador y se requiere un procesamiento adicional. En pocas palabras, el código lógico se inserta en la sección de código if del código anterior.

Aunque el método anterior realiza la función lógica, no muestra claramente el alcance de la visión. En los juegos de sigilo clásicos, para recordar a los jugadores el campo de visión específico del enemigo, se agregarán representaciones visuales obvias. Por ejemplo, en algunos niveles de "Honkai Impact 3", hay un sector rojo muy exagerado frente al robot para mostrar el campo de visión del enemigo.

A continuación, utilice el método de modelado de procedimientos para mostrar el campo de visión. El trabajo de preparación es el siguiente:

  1. Crea un nuevo objeto vacío para el enemigo como subobjeto, llámalo vista y establece la posición en 0.
  2. Agregar el componente Mesh Renderer y el componente Mesh Filter
  3. Cree un nuevo material, cambie su modo de renderizado a Transparente, cambie su color a verde y cambie su transparencia a aproximadamente 150. Para probar la configuración del material, puede arrastrarlo al cubo para probar y ver si la transparencia es apropiada.
  4. Arrastra la bola de material al nuevo objeto de vista. Dado que el objeto de vista actualmente no tiene modelo, no se puede mostrar.

Dado que el campo de visión cambia dinámicamente, es necesario usar código para construir un modelo plano en forma de sector y asignarlo al objeto de vista para expresar el campo de visión. Debido a muchos cambios, el script modificado completo se muestra a continuación:

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

public class AIEnemy : MonoBehaviour
{
    public int viewRadius = 4;  //视野距离
    public int viewLines = 30; //射线数量
    public MeshFilter viewMeshFilter;
    List<Vector3> viewVerts;  //定点列表
    List<int> viewIndices;  //定点序号列表
    void Start()
    {
        Transform view = transform.Find("view");
        viewMeshFilter = view.GetComponent<MeshFilter>();
        viewVerts=new List<Vector3>();
        viewIndices = new List<int>();
    }

   
    void Update()
    {
        FieldOfView();
    }
    void FieldOfView()
    {
        viewVerts.Clear();
        viewVerts.Add(Vector3.zero);  //加入起点坐标,局部坐标系


        //获得最左边那条射线的向量,相对正前方,角度是-45°
        Vector3 forward_left = Quaternion.Euler(0, -45, 0) * transform.forward * viewRadius;
        //依次处理每条射线
        for(int i = 0; i <= viewLines; i++)
        {
            Vector3 v = Quaternion.Euler(0, (90.0f / viewLines) * i, 0) * forward_left;
            //角色位置+v,就是射线终点pos
            Vector3 pos = transform.position + v;

            //实际发射射线。注意RayCast的参数,重载很多容易搞错
            RaycastHit hitInfo;
            if(Physics.Raycast(transform.position,v,out hitInfo, viewRadius))
            {
                //碰到物体,终点改为碰到的点
                pos = hitInfo.point;
            }
            //将每个点的位置加入列表,注意转为局部坐标系
            Vector3 p = transform.InverseTransformPoint(pos);
            viewVerts.Add(p);
         
        }
        //根据顶点绘制模型
        RefreshView();
    }
    void RefreshView()
    {
        viewIndices.Clear();
        //逐个加入三角面,每个三角面都以起点开始
        for(int i = 1; i < viewVerts.Count-1; i++)
        {
            viewIndices.Add(0);
            viewIndices.Add(i);
            viewIndices.Add(i+1);
        }
        //填写Mesh信息
       Mesh mesh = new Mesh();
        mesh.vertices= viewVerts.ToArray();
        mesh.triangles = viewIndices.ToArray();
        viewMeshFilter.mesh = mesh;
    }
}

En pocas palabras, la información de la malla consta de "vértice" y "número de vértice". Los vértices son posiciones espaciales, por lo que están representados por Vector 3. Todos los vértices se colocan en una matriz grande y cada vértice corresponde a un subíndice de la matriz.

El número de vértice se refiere al subíndice de la matriz. Dado que la cuadrícula se compone de superficies triangulares, tres números de serie son un grupo, y completar los números de serie de los vértices 3 y 3 representa una y una superficie triangular.

Los lados positivos y negativos de un triángulo:

La superficie triangular se compone de 3 números de vértice y hay dos órdenes posibles de los 3 puntos, a saber, abc y acb, es decir, en el sentido de las agujas del reloj y en el sentido contrario a las agujas del reloj. De forma predeterminada, los materiales generales se representan en un lado y cada lado solo se puede ver desde un lado, pero no desde el otro.

Siempre que el orden de los puntos específicos en el sentido de las agujas del reloj sea diferente, no tiene ningún efecto, como abc = bca = cab, y cba es lo contrario.

Si no puede ver el triángulo al escribir el código, verifique si se muestra el reverso. Al intercambiar el orden de dos números de serie cualesquiera entre los tres puntos, se puede invertir la superficie del triángulo.

Después de preparar la lista de vértices y la lista de secuencia de vértices, puede crear un objeto Mesh. Varias propiedades del objeto Mesh son de tipo matriz, por lo que es necesario utilizar el método List.ToArray para convertir la lista en una matriz. Finalmente, asigne el objeto Mesh al componente Mesh FIlter.

Siga los pasos anteriores y ejecútelo, y obtendrá un modelo de sector verde translúcido. Y el rango verde aún puede mostrar el rango correctamente cuando está bloqueado por obstáculos.

Supongo que te gusta

Origin blog.csdn.net/m0_63024355/article/details/132779219
Recomendado
Clasificación