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
{
扇形,
圆形,
圆锥
}
}