关于Untiy编写敌人可视化视野
最近遇到了一个编写可视化视野范围的问题,写出来了以后记录一下可视化视野的方法,因为用到的方法较多,我又想把原理讲解明白,所以本文的篇幅较长,如果你是没有耐心的话,建议退出看其他文章。
首先说一下视野的思路:我们可视化的视野全部都是以扇形显示的,同时为了后期的方便调整我们的视野和距离都必须是动态的。那么我们是不是可以使用度数来控制视野范围,那么我们就需要画出一个扇形。那么我们可以先画出来一个圆 然后在这个圆上面来截取扇形,在画圆之前我们需要定义圆的半径(因为我们的物体在圆的中心,所以圆的半径就代表了视野的纵向长度,代表了物体可以看多远),我们还需要定义一个视野的角度(视野 的角度代表了扇形的大小,表示视野的横向大小)。

设置半径、视野角度的变量,并利用Unity特性把视野利用滑动条显示出来,把角度限制在0~360°之后就是画圆,然后在圆上面截取扇形(写在Editor文件夹中)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
//自定义编辑器
[CustomEditor(typeof(FieldOfView))]
public class FieldOfViewEditor : Editor
{
private void OnSceneGUI()
{
FieldOfView fow = (FieldOfView)target;
//画的颜色为白色
Handles.color = Color.white;
//画一个线弧(圆的中心,圆的法线,开始的中心角度开始的地方,弧度、旋转的度数,圆的半径)
Handles.DrawWireArc(fow.transform.position, Vector3.up, Vector3.forward, 360, fow.viewRadius);
//把视野角度的一般转为Vector3向量
Vector3 viewAngleA = fow.DirFromAngle(-fow.viewAngle / 2, false);
//把视野角度的一般转为Vector3向量并取反
Vector3 viewAngleB = fow.DirFromAngle(fow.viewAngle / 2, false);
//从玩家的位置到夹角的一条边画一条线(长度为视野的半径)
Handles.DrawLine(fow.transform.position, fow.transform.position + viewAngleA * fow.viewRadius);
//从玩家的位置到夹角的另一条边画一条线(长度为视野的半径)
Handles.DrawLine(fow.transform.position, fow.transform.position + viewAngleB * fow.viewRadius);
Handles.color = Color.red;
//遍历所有打到的敌人的位置
foreach (Transform visibleTarget in fow.visibleTargets)
{
//画一条线从玩家的位置到敌人的位置
Handles.DrawLine(fow.transform.position, visibleTarget.position);
}
}
}
/// <summary>
/// 把传进来的度数转为Vector3(给出角的方向)
/// </summary>
/// <param name="angleInDegrees">传进来的角度</param>
/// <param name="angleIsGlobal">bool值判断角度是否变化</param>
/// <returns></returns>
public Vector3 DirFromAngle(float angleInDegrees, bool angleIsGlobal)
{
//如果使false
if (!angleIsGlobal)
{
//角度加上自身的欧拉角的y(旋转角度的Y轴)
angleInDegrees += transform.eulerAngles.y;
}
//返回(把传进来的度数转为弧度再转为正弦,0,把传进来的角度转为弧度再转为余弦)
return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0, Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
}
DireFromAngle函数可以把一个角度传进来,然后把返回一个Vector的向量。参数为一个角度和一个布尔值。(因为我们的物体是敌人也好是玩家也好是会一直移动的,为了让可视化的视野跟随玩家移动,所以需要判断我们取的度数是否跟随了玩家移动,但是我们取视野范围的时候并没有让其跟随物体的变化而变化),所以当我们把角度转成向量的时候需要添加一个布尔值的参数,如果这个角度不跟随移动的时候那么我们就需要让这个角度加上物体的旋转角度,才是视野正确的角度。
利用Handles画一个圆,然后获取到圆的角度,把角度除以2并且其中一个取反,利用DirFromAngle方法把这两个角度转换成Vector3向量,之后再利用Handles的划线方法画出两条线。

这里两条线的夹角就代表了视野的纵向范围,当敌人在这个范围内,代表已经发现了敌人。到现在为止我们已经把扇形准备好了,但是我们会发发现这个线只会在Scene视图显示,我们要怎么把它显示在Game视图呢? 同时我们怎么检测敌人是否在视野范围内呢?
我们先说检测敌人的方法:因为在一个场景中我们不止会有敌人,还会有障碍物、队友、以及其他的物体。我们的Physics中有一个方法可以检测到所有与上图的圆发生触发的物体。那么我们就可以利用layer去检测与圆发生碰撞的物体,然后计算检测到的敌人与玩家的度数是否小于视野角度的一半,如果小于一半的话我们就确定敌人在玩家的视野内,如果大于一半就判断敌人没有在玩家的视野内。然后向敌人的位置发射射线,射线的长度为视野的半径,因为敌人跟玩家之间可能存在障碍物,所以利用layer判断射线是否打到了敌人,如果射线打到敌人则把敌人保存在数组中。下面上代码:
/// <summary>
/// 把打到的玩家物体保存在数组中
/// </summary>
void FindVisibleTarget()
{
//列表清空
visibleTargets.Clear();
//创建一个数组保存所有与重叠球接触的碰撞器 。 方法的参数数据为(球的中心,球的半径,选择投射的层级)
Collider[] targetsInViewRadius = Physics.OverlapSphere(transform.position, viewRadius, targetMask);
//遍历数组中所有的碰撞器
for (int i = 0; i < targetsInViewRadius.Length; i++)
{
//遍历接收所有碰撞器的位置
Transform target = targetsInViewRadius[i].transform;
//获取接收到的碰撞器与物体之间的vector3向量(方向)
Vector3 dirToTarget = (target.position - transform.position).normalized;
//如果物体与碰撞器的角度小于视野角度的二分之一
if (Vector3.Angle(transform.forward, dirToTarget) < viewAngle / 2)
{
//计算物体与碰撞器的距离
float dstToTarget = Vector3.Distance(transform.position, target.position);
//如果射线没有打到障碍物就把打到的物体保存在数组中{如果视野范围内有玩家,判断他们之间是否有障碍物}(起点,方向,射线的最大长度,射线可以照到的层级)
if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, obstacleMask))
{
//把打到的层为targetMask的物体保存在数组中
visibleTargets.

本文详细介绍了如何在Unity中实现敌人的可视化视野范围以及障碍物遮挡检测。通过创建一个自定义编辑器,利用Handles API绘制扇形视野,并结合射线投射检测敌人和障碍物。视野范围动态调整,利用角度和半径参数,并通过射线判断敌人的位置是否在视野内。此外,还展示了如何使用Mesh绘制出视线路径,模拟出障碍物阻挡效果,提供了一种在Scene和Game视图中可视化角色视野的有效方法。
最低0.47元/天 解锁文章
942





