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

Unity光源检测器

扇形区域检测效果:

在这里插入图片描述

圆形区域检测效果:

在这里插入图片描述

圆锥区域检测效果:

在这里插入图片描述

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

面板说明:

在这里插入图片描述

Mode:根据需求不同,使用不同的检测模式
在这里插入图片描述

SpotLight:自动拾取自身的聚光灯组件,调整自身参数也会相应的调整聚光灯的参数
在这里插入图片描述
resolution:越密集检测的越精细,发射的射线就越多,消耗性能就越大
在这里插入图片描述

FOV_Angle与FOV_Range分别对应聚光灯的SpotAngle与Range,也会对应调节射线的角度范围
在这里插入图片描述
DetectionTags: 只有这个标签的物体可以被检测,而且目标在范围内才会触发射线检测
IsTargetInFOV:目标是否在一定的范围内,也用来判断是否开始射线检测

三种模式检测方法,返回的List就是需要射线发射的点位置
public List<Vector3> GetSectorial()
{
    itemList.Clear();
    for (int j = 0; j <= resolution; j++)
    {
        float Angle = -( fOV_Angle/2-(fOV_Angle  / resolution ) * j );
        if (Angle < 0) Angle = 360 + Angle;
        Vector3 Rotation = Quaternion.AngleAxis(Angle , transform.up) * transform.forward * fOV_Range;
        itemList.Add(Rotation);
    }
    return itemList;
}

public List<Vector3> GetCircle()
{
    itemList.Clear();
    float radius = Mathf.Tan(Mathf.Deg2Rad * ( fOV_Angle/2 )) * fOV_Range;

    for (int i = 0; i <= resolution; i++)
    {
        float angle = ( 360f / resolution ) * i;
        float x = Mathf.Cos(Mathf.Deg2Rad * angle) * radius;
        float y = Mathf.Sin(Mathf.Deg2Rad * angle) * radius;

        // 根据聚光灯的角度和范围调整 Z 轴坐标,使其形成球面曲率---有问题暂时使用固定距离
        float z = fOV_Range;// Mathf.Sqrt(fOV_Range * fOV_Range - x * x - y * y);

        Vector3 localPoint = new Vector3(x , y , z);
        Vector3 worldPoint = transform.rotation * localPoint;

        itemList.Add(worldPoint);
    }
    return itemList;
}

圆锥区域需要分散点位,不能越到中心点越密集,需要均匀分布
使用黄金比例,用于在圆锥表面上均匀分布点的计算。
请添加图片描述

public List<Vector3> GetCircularCone()
{
    itemList.Clear();
    float halfAngle = fOV_Angle / 2;
    float maxDistance = fOV_Range * Mathf.Tan(Mathf.Deg2Rad * halfAngle);
    float goldenRatio = ( 1 + Mathf.Sqrt(5) ) / 2;

    for (int i = 0; i <= resolution; i++)
    {
        float theta = 2 * Mathf.PI * i / goldenRatio;
        float r = maxDistance * Mathf.Sqrt((float)i / resolution);

        float x = r * Mathf.Cos(theta);
        float y = r * Mathf.Sin(theta);
        // 计算Z轴坐标,使其形成圆锥曲面,---有问题暂时使用固定距离
        float z = fOV_Range;// Mathf.Sqrt(Mathf.Max(0 , fOV_Range * fOV_Range - r * r));

        // 计算局部坐标
        Vector3 localPoint = new Vector3(x , y , z);

        // 将局部坐标转换为世界坐标,考虑聚光灯的位置和旋转
        Vector3 worldPoint = transform.rotation * localPoint;

        itemList.Add(worldPoint);
    }

    return itemList;
}
完整代码:
using System.Collections.Generic;
using UnityEngine;

public class Detetor : MonoBehaviour
{
    public MeshMode mode = MeshMode.扇形;
    public Light spotLight;
    [Range(5f , 1000f)]
    public float resolution = 50;
    [Range(1f , 179f)]
    public float fOV_Angle = 60f;
    public float fOV_Range = 6f;
    public string[] detectionTags;

    public bool IsTargetInFOV = true;
    List<Vector3> itemList = new List<Vector3>();
    void Start()
    {
        DetetorInit();
    }

    private void OnValidate()
    {
        DetetorInit();
    }

    void DetetorInit()
    {
        if (spotLight == null)
        {
            spotLight = GetComponent<Light>();
        }
        else
        {
            if (spotLight.type == LightType.Spot)
            {
                spotLight.spotAngle = fOV_Angle;
                spotLight.range = fOV_Range;
            }
            spotLight.spotAngle = fOV_Angle;
            spotLight.range = fOV_Range;
            UpdateDetectionArea();
        }
    }

    public void UpdateDetectionArea()
    {
        DetectionTarget();
        if (!IsTargetInFOV)
            return;
        switch (mode)
        {
            case MeshMode.扇形:
                GetSectorial();
                break;
            case MeshMode.圆形:
                GetCircle();
                break;
            case MeshMode.圆锥:
                GetCircularCone();
                break;
        }
        for (int i = 0; i < itemList.Count; i++)
        {
            RaycastHit hit;
            Ray ray = new Ray(transform.position , itemList[i]);
            if (Physics.Raycast(ray , out hit , fOV_Range))
            {
                itemList[i] = hit.point - transform.position;
                for (int j = 0; j < detectionTags.Length; j++)
                {
                    if (hit.collider.gameObject.tag == detectionTags[j])
                    {
                        CallBack(hit.collider.gameObject);
                    }
                }
            }
        }
    }


    public List<Vector3> GetSectorial()
    {
        itemList.Clear();
        for (int j = 0; j <= resolution; j++)
        {
            float Angle = -( fOV_Angle/2-(fOV_Angle  / resolution ) * j );
            if (Angle < 0) Angle = 360 + Angle;
            Vector3 Rotation = Quaternion.AngleAxis(Angle , transform.up) * transform.forward * fOV_Range;
            itemList.Add(Rotation);
        }
        return itemList;
    }

    public List<Vector3> GetCircle()
    {
        itemList.Clear();
        float radius = Mathf.Tan(Mathf.Deg2Rad * ( fOV_Angle/2 )) * fOV_Range;

        for (int i = 0; i <= resolution; i++)
        {
            float angle = ( 360f / resolution ) * i;
            float x = Mathf.Cos(Mathf.Deg2Rad * angle) * radius;
            float y = Mathf.Sin(Mathf.Deg2Rad * angle) * radius;

            // 根据聚光灯的角度和范围调整 Z 轴坐标,使其形成球面曲率
            float z = fOV_Range;// Mathf.Sqrt(fOV_Range * fOV_Range - x * x - y * y);

            Vector3 localPoint = new Vector3(x , y , z);
            Vector3 worldPoint = transform.rotation * localPoint;

            itemList.Add(worldPoint);
        }
        return itemList;
    }


    public List<Vector3> GetCircularCone()
    {
        itemList.Clear();
        float halfAngle = fOV_Angle / 2;
        float maxDistance = fOV_Range * Mathf.Tan(Mathf.Deg2Rad * halfAngle);
        float goldenRatio = ( 1 + Mathf.Sqrt(5) ) / 2;

        for (int i = 0; i <= resolution; i++)
        {
            float theta = 2 * Mathf.PI * i / goldenRatio;
            float r = maxDistance * Mathf.Sqrt((float)i / resolution);

            float x = r * Mathf.Cos(theta);
            float y = r * Mathf.Sin(theta);
            // 计算Z轴坐标,使其形成圆锥曲面
            float z = fOV_Range;// Mathf.Sqrt(Mathf.Max(0 , fOV_Range * fOV_Range - r * r));

            // 计算局部坐标
            Vector3 localPoint = new Vector3(x , y , z);

            // 将局部坐标转换为世界坐标,考虑聚光灯的位置和旋转
            Vector3 worldPoint = transform.rotation * localPoint;

            itemList.Add(worldPoint);
        }

        return itemList;
    }


    void CallBack(GameObject obj)
    {
        Debug.Log("发现目标:" + obj.name);
    }
    void DetectionTarget()
    {
        IsTargetInFOV = false;

        // 使用 Physics.OverlapSphere 检测球形范围内的所有碰撞体
        Collider[] colliders = Physics.OverlapSphere(transform.position , fOV_Range);

        // 遍历所有检测到的碰撞体
        foreach (var collider in colliders)
        {
            // 检查碰撞体的标签是否在检测标签列表中
            foreach (var tag in detectionTags)
            {
                if (collider.CompareTag(tag))
                {
                    IsTargetInFOV = true;
                    return;
                }
            }
        }
    }
    void Update()
    {
        UpdateDetectionArea();
    }

    private void OnDrawGizmosSelected()
    {
        UpdateDetectionArea();
        if (itemList.Count > 0)
        {
            foreach (Vector3 point in itemList)
            {
                Vector3 currentPoint = transform.position + point;

                Color color = spotLight.color;
                color.a = 0.1f;
                Gizmos.color = color;
                Gizmos.DrawLine(transform.position , currentPoint);
                Gizmos.color = spotLight.color;
                Gizmos.DrawSphere(currentPoint , 0.03f);
            }
        }
    }

    public enum MeshMode
    {
        扇形,
        圆形,
        圆锥
    }
}

Demo下载链接: Demo

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐沢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值