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