Unity 模拟手电筒光源探测器,AI攻击范围检测区域,视锥内检测物体,扇形区域检测,圆形区域检测,圆锥区域检测

Unity光源检测器

扇形区域检测效果:

请添加图片描述

圆形区域检测效果:

请添加图片描述

圆锥区域检测效果:

请添加图片描述
参考文章: Unity AI探测器
文章写的非常的详细,不过根据需求不同,只能检测扇形区域肯定是不够的
我增加了光源边缘检测与光源实体区域检测

面板说明:

显示视野:渲染检测区域
视角网格:区域网格(sphere),可以调整材质
检测模式:根据需求不同,使用不同的检测模式
网格密集度:网格越密集检测的越精细,发射的射线就越多,消耗性能就越多
请添加图片描述
视野角度与视野半径分别控制着扇形模式与圆形,圆锥模式的视野角度
请添加图片描述
视野距离:调整检测距离
目标Tag: 只有这个标签的物体可以被检测

工具面板:

在这里插入图片描述

三种模式检测方法,返回的List就是需要射线发射的点位置
 	/// <summary>
    /// 得到圆形
    /// </summary>
    /// <returns></returns>
    public List<Vector3> GetCircle()
    {
    
    
        itemList.Clear();
        float angle = 0;
        for (int i = 0; i <= Points; i++)
        {
    
    
            Vector3 Rotation = Quaternion.AngleAxis(angle, transform.forward) * transform.up * FOVRadius;
            Rotation += transform.forward * SightRange;
            itemList.Add(Rotation);
            angle += 360 / Points;
        }
        return itemList;
    }
    /// <summary>
    /// 得到扇形
    /// </summary>
    /// <returns></returns>
    public List<Vector3> GetSectorial()
    {
    
    
        itemList.Clear();
        for (int j = 0; j <= Points; j++)
        {
    
    
            float Angle = -FOVAngle + (((FOVAngle * 2) / Points) * j);
            if (Angle < 0) Angle = 360 + Angle;
            Vector3 Rotation = Quaternion.AngleAxis(Angle, transform.up) * transform.forward;
            itemList.Add(Rotation);
        }
        return itemList;
    }

圆锥区域需要分散点位,不能越到中心点越密集,需要均匀分布
请添加图片描述

    /// <summary>
    /// 得到圆锥
    /// </summary>
    /// <returns></returns>
    public List<Vector3> GetCircularCone()
    {
    
    
        itemList.Clear();
        float goldenRatio = (1 + Mathf.Sqrt(5)) / 2;
        float angleIncrement = Mathf.PI * 2 * goldenRatio;

        for (int i = 0; i < Points; i++)
        {
    
    
            float t = (float)i / Points;
            float inclination = Mathf.Acos(1 - 2 * t);
            float azimuth = angleIncrement * i;

            float x = Mathf.Sin(inclination) * Mathf.Cos(azimuth);
            float y = Mathf.Sin(inclination) * Mathf.Sin(azimuth);
            float z = Mathf.Cos(inclination);
            Vector3 Rotation = new Vector3(x, y, z) * FOVRadius;
            Rotation += transform.forward * SightRange;
            itemList.Add(Rotation);
        }
        return itemList;
    }
完整代码:
using System.Collections.Generic;
using UnityEngine;


public class Detetor : MonoBehaviour
{
    
    
    [Header("显示视野")]
    public bool ShowLOS = true;
    [Header("视角网格")]
    public MeshFilter FOVMesh;
    [Header("检测模式")]
    public MeshMode meshMode = MeshMode.扇形;
    [Header("网格密集度,精确度")]
    [Range(5f, 1000f)]
    public float Points = 50;
    [Header("视野角度")]
    [Range(1f, 180f)]
    public float FOVAngle = 60f;
    [Header("视野半径")]
    [Range(1f, 50f)]
    public float FOVRadius = 5f;
    [Header("视野距离")]
    public float SightRange = 6f;
    [Header("目标Tag")]
    public string[] DetectionTags;
    bool IsTargetInFOV = true;

    void Start()
    {
    
    
        Init();
        this.GetComponent<SphereCollider>().radius = SightRange;
    }
    void Init()
    {
    
    
        Light light = GetComponent<Light>();
        if (light)
        {
    
    
            if (light.type == LightType.Spot)
            {
    
    
                FOVAngle = light.spotAngle / 2;
                FOVRadius = light.spotAngle / 8;
                SightRange = light.range;
            }
        }
    }

    /// <summary>
    /// 生成视线网格线
    /// </summary>
    void GenLOSMesh()
    {
    
    
        if (!IsTargetInFOV)
            return;
        this.GetComponent<SphereCollider>().radius = SightRange;
        List<Vector3> newVertices = new List<Vector3>();
        newVertices.Add(Vector3.zero);
        //if (itemList.Count == 0)
        {
    
    
            switch (meshMode)
            {
    
    
                case MeshMode.扇形:
                    GetSectorial();
                    break;
                case MeshMode.圆形:
                    GetCircle();
                    break;
                case MeshMode.圆锥:
                    GetCircularCone();
                    break;
            }
        }
        foreach (Vector3 item in itemList)
        {
    
    
            RaycastHit hit;
            Ray ray = new Ray(transform.position, item);
            if (Physics.Raycast(ray, out hit, SightRange))
            {
    
    
                for (int i = 0; i < DetectionTags.Length; i++)
                {
    
    
                    if (hit.collider.gameObject.tag == DetectionTags[i])
                    {
    
    
                        CallBack(hit.collider.gameObject);
                    }
                }
                newVertices.Add(hit.point - transform.position);
            }
            else
            {
    
    
                newVertices.Add(ray.GetPoint(SightRange) - transform.position);
            }
        }

        if (ShowLOS)
        {
    
    
            DrawLos(newVertices);
        }
        else
        {
    
    
            FOVMesh.mesh.Clear();
        }
    }
    List<Vector3> itemList = new List<Vector3>();
    /// <summary>
    /// 得到圆形
    /// </summary>
    /// <returns></returns>
    public List<Vector3> GetCircle()
    {
    
    
        itemList.Clear();
        float angle = 0;
        for (int i = 0; i <= Points; i++)
        {
    
    
            Vector3 Rotation = Quaternion.AngleAxis(angle, transform.forward) * transform.up * FOVRadius;
            Rotation += transform.forward * SightRange;
            itemList.Add(Rotation);
            angle += 360 / Points;
        }
        return itemList;
    }
    /// <summary>
    /// 得到扇形
    /// </summary>
    /// <returns></returns>
    public List<Vector3> GetSectorial()
    {
    
    
        itemList.Clear();
        for (int j = 0; j <= Points; j++)
        {
    
    
            float Angle = -FOVAngle + (((FOVAngle * 2) / Points) * j);
            if (Angle < 0) Angle = 360 + Angle;
            Vector3 Rotation = Quaternion.AngleAxis(Angle, transform.up) * transform.forward;
            itemList.Add(Rotation);
        }
        return itemList;
    }
    /// <summary>
    /// 得到圆锥
    /// </summary>
    /// <returns></returns>
    public List<Vector3> GetCircularCone()
    {
    
    
        itemList.Clear();
        float goldenRatio = (1 + Mathf.Sqrt(5)) / 2;
        float angleIncrement = Mathf.PI * 2 * goldenRatio;

        for (int i = 0; i < Points; i++)
        {
    
    
            float t = (float)i / Points;
            float inclination = Mathf.Acos(1 - 2 * t);
            float azimuth = angleIncrement * i;

            float x = Mathf.Sin(inclination) * Mathf.Cos(azimuth);
            float y = Mathf.Sin(inclination) * Mathf.Sin(azimuth);
            float z = Mathf.Cos(inclination);
            Vector3 Rotation = new Vector3(x, y, z) * FOVRadius;
            Rotation += transform.forward * SightRange;
            itemList.Add(Rotation);
        }
        return itemList;
    }
    /// <summary>
    /// 回调
    /// </summary>
    /// <param name="obj"></param>
    void CallBack(GameObject obj)
    {
    
    
        Debug.Log("发现目标:" + obj.name);
    }
    /// <summary>
    /// 绘制网格
    /// </summary>
    /// <param name="newVertices"></param>
    void DrawLos(List<Vector3> newVertices)
    {
    
    
        FOVMesh.mesh.Clear();
        List<Vector2> newUV = new List<Vector2>();
        List<int> newTriangles = new List<int>();
        for (int i = 1; i < newVertices.Count - 1; i++)
        {
    
    
            newTriangles.Add(0);
            newTriangles.Add(i);
            newTriangles.Add(i + 1);
        }
        for (int i = 0; i < newVertices.Count; i++)
        {
    
    
            newUV.Add(new Vector2(newVertices[i].x, newVertices[i].z));
        }
        FOVMesh.mesh.vertices = newVertices.ToArray();
        FOVMesh.mesh.triangles = newTriangles.ToArray();
        FOVMesh.mesh.uv = newUV.ToArray();
        FOVMesh.transform.rotation = Quaternion.identity;
        FOVMesh.mesh.RecalculateNormals();
        //switch (meshMode)//法线设置
        //{
    
    
        //    case MeshMode.平面:
        //        FOVMesh.mesh.RecalculateNormals();
        //        break;
        //    case MeshMode.圆形:
        //        InversionNormal(FOVMesh);
        //        break;
        //    case MeshMode.圆锥:
        //        FOVMesh.mesh.RecalculateNormals();
        //        break;
        //}
        //FOVMesh.gameObject.GetComponent<MeshCollider>().sharedMesh = FOVMesh.mesh;
    }
    /// <summary>
    /// 法线反转
    /// </summary>
    /// <param name="meshFilter"></param>
    void InversionNormal(MeshFilter meshFilter)
    {
    
    
        Vector3[] normals = meshFilter.mesh.normals;
        for (int i = 0; i < normals.Length; i++)
        {
    
    
            normals[i] = -normals[i];
        }
        meshFilter.mesh.normals = normals;

        int[] triangles = meshFilter.mesh.triangles;
        for (int i = 0; i < triangles.Length; i += 3)
        {
    
    
            int t = triangles[i];
            triangles[i] = triangles[i + 2];
            triangles[i + 2] = t;
        }
        meshFilter.mesh.triangles = triangles;
    }

    void OnTriggerStay(Collider col)
    {
    
    
        IsTargetInFOV = false;
        for (int i = 0; i < DetectionTags.Length; i++)
        {
    
    
            if (col.gameObject.tag == DetectionTags[i])
            {
    
    
                IsTargetInFOV = true;
                break;
            }
        }
    }
    public enum MeshMode
    {
    
    
        扇形,
        圆形,
        圆锥
    }
    void Update()
    {
    
    
        GenLOSMesh();
    }
}

绘制圆形时法线需要处理,后来换成双面材质问题解决,所以代码注掉了.
应用时不要忘记关闭绘制网格,
Demo下载链接: Demo

猜你喜欢

转载自blog.csdn.net/CTangZe/article/details/125389520