视野迷雾
一、迷雾Demo
(踩坑全过程纪实)
1.1 创建场景
利用引擎创建 :
1.后缀为“_occ”的视野阻挡(此步是为了一会可以从场景中拿到障碍数组)。
2.无后缀的视野阻挡(可以没有)。
3.两个可以移动的人物(其中一个人的节点为role1,另一个人节点为role2,左键控制role1移动,右键控制role2移动)用于测试。
4.加载摄像机文件(实现镜头拉远拉近,按住鼠标中键摄像头左右移动,按住鼠标中键加alt对准人物360度旋转),摄像机的操作也是为了便于后续测试(可以没有)。
1.2 实现人物被阻挡则互不可见
初步方案是利用的是引擎中的方法,即通过判断场景中两点是否直线可达来完成判断目标节点的显示隐藏状态,后续会利用绘制辅助线的方法进行判断
1.3 绘制辅助线
1. 绘制每(单)个障碍物的辅助线(以下的描述是我进行的步骤。可以直接跳到实际方法中去)
初步方案是利用角度:如图1.1所示, 一个凸多边形(暂时不谈凹多边形)的视野范围就是这个多边形的顶点与自己连线,之后判断夹角,夹角最大的即为视野线。
图1.1 利用角度求障碍物视野范围
接下来要做的就是判断夹角的方法:
首先取一个基准线,第一种方案是取x轴做基准线,遍历这个多边形的每个节点将连线与x轴做夹角,通过保存最大的夹角和最小的夹角来找到差最大的夹角作为目标视野,
这里要注意
1.障碍数组是不是可以闭合成多边形,且没有重合的点。
2.有时拿到的线也没发现它比应该要的两个点形成的角大,所以我将夹角的度数打印出来,发现在错误的点形成的夹角大于180度,这里是因为在利用 反三角函数 arctan 时求出的夹角为负的情况,导致计算出的夹角超过了正常值,(例如 179 - (- 179)= 358)因此我在这里加了个判断,当夹角A大于180时,将其补角(180 - A)返回。
至此,凸多边形障碍物的利用夹角求视野范围的过程叙述完毕。
当然,凸多边形不够,场景中肯定有凹多边形,于是就需要解决凹多边形的问题。
图1.2 利用延长线是否与本图形有交点求视野范围
在本项目中我先是找到了一个如何判断凸多边形还是凹多边形的方法,用来将凸多边形还是凹多边形分开处理,网上对于这个问题已经有了很简单的方案,即沿着一条初始边作为初始向量,然后判断下一个顶点在这个向量的哪一边(左边或者右边),这里如何判断是哪一边呢,我们只需要用到叉积即可。
假设当前连续的三个顶点分别是P1,P2,P3。计算向量P1P2,P2P3的叉乘,得到的结果如果为正,则表示P3点在线段P1和P2的左侧,凸多边形的顶点是逆时针序列。然后依次计算下一个前后所组成向量的叉乘,如果在计算时,出现负值,则此多边形时凹多边形,如果所有顶点计算完毕,其结果都是大于0,则多边形为凸多边形。
下面是如何判断线段相交的方法(百度上有):
图1.3 判断两条线段是否相交
为方便表述,下文将线段a1 a2 表示为线段a, 线段b1 b2 表示为线段b。为了利用向量的叉乘关系,将线段的端点看成四个向量,下面用粗体表示向量。
根据向量运算可知:
a2 = a1 + a,
b2 = b1 + b。
将线段表示为参数方程(即将线段上的任意一点由这个方程表示)
a = a1 + ta,
b = b1 + ub。
其中参数t, u 取值[0, 1], 两条线段相交则具有如下关系:
a1 + t a = b1 + u b
将上式两端同时叉乘b ,得到下式:
(a1 + t a)x b = (b1 + u b)x b
由向量的叉乘关系 b x b = 0,可得:
a1 x b + t a x b = b1 x b
解出参数t
t = (b1 - a1)x b / (a x b)
同理,解出参数u
u = a x (a1 - b1)/ (a x b)
当 0 <= t <= 1, 且 0 <= u <= 1 时,两线段有交点。
以下四种情况,为两线段的相交情况
1.如果 a x b = 0 且 (a1 - b1)x a = 0,则两条线共线
2.如果 a x b = 0 且 (a1 - b1)x a ≠ 0,则两条线平行且不相交
3.如果 a x b ≠ 0 且 0 <= t <= 1 且 0 <= u <= 1则两条线段在 a1 + ta = b1 + ub处点处相交
4.其他情况,两条线段不平行且不相交
当我们拥有了线段相交的方法之后,就可以进行视野的判断了
长时间没有编辑的分割线--------------------------------------------------------------------------------------------------------------------------------------------------实际方法
2019.3.7 继续
我尽量用文字写,图片不太好画QWQ
以上说的凹多边形的东西,最后发现会有一种情况出问题,就是身处凹多边形内部的时候,遮挡的视野角度不对(遮反了)本来应该是360 - 90, 然后把90给盖住了。。。
所以,新方案,在构建地图地形时,让所有多边形阻挡都是凸多边形,(凹多边形也可以做成几个凸多边形的组合),这样就可以避免这个问题了,我是真的没想到更好的方案,
不得已只能用这个方法避免了。
正式迷雾遮挡过程:
一
第一步要做的事是将场景中的障碍物都用一个个的外包圆包装起来,
这一步的目的是:通过外包圆来保证障碍的位置数组(多边形障碍的端点位置数组)可以完全完整的放进场景中划分的格子中,不会出现某些漏放的情况出现。
步骤如下:
1.遍历场景所有阻挡(数组),遍历这个阻挡所有端点,将所有端点的x, y坐标取平均值, 作为圆心,然后再取这个圆心到阻碍上最远的点作为半径,因为
阻碍的存储格式是数组,所以我们这个外包圆也以数组的形式存储,circleBox[ i ] = { center = {x, y}, radius = R }, i 对应的是对应阻挡数组的index, 这样就可以将
外包圆与阻挡数组产生联系了。(当然,这里最好是最小外接圆,能更加精准)
二
第二步要做的事是将场景中的阻碍在预加载的过程中放入场景中的格子中,
这一步的目的是:在游戏中玩家时时刻刻都处于移动的过程中,所以要通过格子来进行第一步筛选玩家附近的阻碍,优化效率。
步骤如下:
1.在预加载的过程中,先在每个格子的遍历过程中添加一份对所有阻碍的外包圆的遍历,在遍历时,判断该格子是否任意端点在外包圆中,如果在,就将这个阻挡区域
放进这个格子中,便于将来玩家判断周围格子的阻碍。
三
第三步要做的事就是将视野范围内的阻碍(多边形)切割开,这里必须用图了,我去画。
图2.1.视野切割后的多边形阻碍
visio有点不太会用,大致上就是这个意思,切割后的新多边形组成的点为在视野内的点,以及多边形与视野圆的交点,按顺序逐个连接的多边形。
这一步的目的是,去除会影响视野线判断的多余形状,只拿到真正对自己视野判断有影响的形状,可以避免视野外的阻碍形状产生错误的视野线,
步骤如下:
1.遍历周围的阻碍数组,先拿到玩家附近的格子。然后从格子中拿到障碍数组,然后判断该障碍数组的外包圆圆心到玩家位置是否比玩家视野范围半径与外包圆圆心半径小,如果大于
则不必判断,然后遍历每一条边,如果起始点在我的视野里就塞进这个新的阻碍表中,然后判断这条边是否会与圆相交,线段与圆相交的方法一会再说,这个方法会返回三种情况,
没有点,一个点,两个点,没有点就不管,一个点正常放,两个点按顺序放进数组(线段起点到终点顺序),这个方法会返回一个临时用的数组,这个数组储存所有一会将要判断视野的障碍。
2.如何判断凸多边形的影响视野情形呢?
实际上的凸多边形阻挡视野的判断方法。。。。最后没有用角度的方法,而是从实际视野判断阻挡取得的灵感,具体操作上用到了非常多的线段相交。
我们在视野中能看见一个障碍物所有端点是因为我们的视线到这些端点之间没有阻挡,所以只要连接玩家位置到端点,然后判断是否与多边形有交点(要排除端点邻近的两条线段)
,然后拿到最终的两侧端点方法是,算端点时将所有连接线延长,延长后的距离与玩家的视野范围相同即可,这样拿到的没有交点的两个端点即为要拿到的点。
3.线段与圆交点(方法来源自百度)
参数4个(圆心坐标,半径,线段两个端点的坐标)方法在最后
通过以上步骤,我们可以拿到周围所有障碍物的影响视野的两个关键点了,接下来是视野内覆盖迷雾,因为引擎中没有提供一个多边形中某个边是弧这样的异形多边形,因此,我用了六边形来
盖住目标区域,样子如图,1-6线是垂直于1-2的,,,我画的有点歪,2-3也是垂直于1-2的。
图2.2.多边形视野阻碍
计算1,2点位置的方法有很多,一种思路是连接6-3然后做平行线,偏移距离是3-6中点与圆心相连到圆上的点到3-6中点的距离。
以上,一个障碍物对于人视野中阻挡的迷雾范围就有了,只要先将玩家附近迷雾全部打开,在用以上所述方法全部盖住即可。
线段与圆交点
是百度的,位置找不到了,,,