为了开发类似潜行游戏的角色视野能力,在Unity中找到了一个叫做RaycastNonAlloc的射线,但是网上资料比较少,为了避免自己日后忘记,在这里写出来。下面是整个工具组建,其中的MyLinkList来自一个博主提供的工具类,链接如下:http://www.manew.com/blog-11763-7490.html。
public class InputVision : MonoBehaviour
{
MyLinkList<Collider2D> target;
RaycastHit2D[] jieguo;
[SerializeField, TooltipAttribute("射线最远距离")]
float vision = 1.6f;
[SerializeField, TooltipAttribute("射线偏移距离")]
float offset = 1.6f;
[SerializeField, TooltipAttribute("射线每步的偏移量")]
float interval = 0.5f;
[SerializeField, TooltipAttribute("射线数")]
int rayNumber = 3;
//控制变量
bool[] tags;
int shu;
bool isnewup = true;
//循环查找所需的参数
float offsetrange;
float currentoffset;
private void Start()
{
target = new MyLinkList<Collider2D>();
jieguo = new RaycastHit2D[5];
isnewup = true;
offsetrange = offset * 2 / rayNumber;
Debug.Log("offsetrange:" + offsetrange);
currentoffset = 0;
}
private void Update()
{
updateVision();
}
//获取列表中是否有该角色
int getCollider2DByname(Collider2D col)
{
for (int _i = 0; _i < target.GetLength(); _i++)
{
if (target.GetLength() < 1) return -1;
if (target.GetElem(_i + 1).name.Equals(col.name))
{
return _i;
}
if (_i == target.GetLength()) return -1;
}
return -1;
}
//进行一次线性查看
int lineLook(Vector3 direction) {
float proportion = (float)(System.Math.Sqrt(vision * vision) / System.Math.Sqrt(direction.x * direction.x + direction.y * direction.y)); ;
Vector3 directions = transform.TransformPoint(transform.localPosition + direction*proportion);
Debug.DrawLine(transform.position,
directions, Color.green);
return Physics2D.RaycastNonAlloc(transform.position,
directions-transform.position, jieguo, vision);
}
//进行一次视线更新
void updateVision() {
if (isnewup) {
shu = target.GetLength();
tags = new bool[20];
isnewup = false;
}
for (int _i=0;_i< rayNumber;_i++) {
int number = lineLook(new Vector3((-offset + _i * offsetrange+currentoffset) / vision, 1));
if (number > 0)
{
for (int _j = 0; _j < number; _j++)
{
int key = getCollider2DByname(jieguo[_j].collider);
if (key > -1) tags[key] = true;
else
{
if (!transform.parent.gameObject.name.Equals(jieguo[_j].collider.name)&&
!(jieguo[_j].collider.name.Equals("model")&& transform.parent.gameObject.name.Equals(jieguo[_j].collider.transform.parent.name)))
target.Append(jieguo[_j].collider);
}
}
}
}
currentoffset += interval;
if (currentoffset> offsetrange) {
currentoffset = 0;
isnewup = true;
for (int _i = 0; _i < shu; _i++)
{
if (!tags[_i]) target.Delete(_i + 1);
}
string _s = "目前扫描到: ";
for (int _i = 0; _i < target.GetLength(); _i++)
{
_s += target.GetElem(_i + 1).name + "; ";
}
Debug.Log(_s);
}
}
}
代码的核心是Physics2D.RaycastNonAlloc这个方法,其他功能选项都是为其服务。
首先为了动态的控制看到的物体,这里用链表而不是数组来存看见的东西,至于性能方面有没有区别这里先不管。然后为了泛用性,我将扇形扫描的相关数值接口都暴露了出来,下面是我在unity中设置的值以及节点树。
Vision上挂脚本,model上放模型,NPC是个空壳,此外所有互动模型都要加上碰撞体才能被发现。
首先在Start里面初始化链表以及RaycastNonAlloc要用的数组和其他相关变量。
然后在Update里面开始调用视线更新。
视线更新根据参数判断这是不是新的一次侦察,如果是就初始化。然后根据射线数分配每条射线的活动范围,即每帧只有指定的射线数在侦察,每帧位移一段距离,在活动完自己的活动范围之后将侦察到的结果综合起来,判断旧的看见物体有哪些还看得见哪些看不见,以此为根据进行链表的删除和增加。
这个侦察部分我借鉴了另外一个博主的思路,博主帖子链接如下:
https://blog.youkuaiyun.com/l773575310/article/details/73251093
然后是最关键的线性查看,即给出一个方向,将查看到的结果返回回来。这部分因为Physics2D.RaycastNonAlloc没有画线功能,所以为此还加了Debug.DrawLine用于画线,方便直观查看射线范围。这部分要注意的就是坐标轴的换算,本地坐标和世界坐标是不一样的,此外RaycastNonAlloc是用方向作为射线方向的参照,这个方向是以零点为参照系,如果不注意很容易出问题。
以上就是目前折腾出来的视线范围组件,目前已经调试没有问题,如果有新的bug我会进一步完善。