Unity射线有时失灵,检测失效原因及解决办法

先写总结,如果看总结能懂就不用往下看了,不懂就再看看吧:

假设要对一个正在移动中的比较小的(注意!!是比较小的,大的看不出来,大的即使晚更新几帧肉眼也看不出来)物体A进行射线检测。如果出现射线有时判true,有时判false。可能是以下原因。

首先,只有在FixedUpdate执行完后才会更新物理,此处说的更新物理是更新碰撞体的意思。

FixedUpdate是固定帧更新,默认1秒执行50次。Update是有多少帧运行多少次。如果在Update中更新物体A位置后再进行射线检测有可能检测不到,尤其是帧数比较高的时候。为什么?因为Update可能运行3次,FixedUpdate才运行一次,而在这三帧里,物体A在渲染中可能已经移动了一定距离,而物体A的碰撞体并没有更新还是再原来的位置,所以射线在对渲染的物体的位置进行判定,所以会判空。而有时判true又是因为,在运行三帧后FixedUpdate更新后碰撞体更新到了正确的位置,射线判定正确。

原理就是这么个原理。

解决办法:

往下翻查看红色字体。

 

细节:

在制作枪械跟随时遇到了枪械抖动的问题,简单地通过将相机调整为LateUpdate解决了这个问题。但是在之后制作红点的时候又遇到了问题,这个问题困扰了我整整四天之久,解决后才发现自己对unity是自己对unity的生命周期和物理更新系统没有深入理解导致的。

红点的算法是从相机发出一道指向射击点的射线,射线击中红点镜时返回击中信息,将红点面片位置设置到击中位置,红点方向设置为始终面对相机。

当玩家移动后,带动作为相机的子物体移动。相机内部调用摇晃函数是相机模拟视角摇晃并跟新本地位置。更新完成后,枪械开始跟随相机。当玩家按下鼠标右键时进入机瞄状态。枪械跟随完成后开始进行红点的算法,发出射线进行判定。

其中移动是在Update中,相机也是在Update中,枪械跟随也是在Update中,红点在强械跟随同脚本的lateUpdate中,这四个函数按照依次按照顺序执行。

但是在实际运行过程中,在移动时,红点总是有些时候显示在正确位置,然后滞后正确位置一段不确定距离,持续几帧,这几帧里每一帧都滞后更多,然后又显示在正确位置,站在原地时则不会。一般出现滞后情况都会考虑到unity的函数执行顺序。在这个案例中,每一帧里先运行移动脚本,然后是相机,此时相机位置已经完全确定,之后是枪械跟随,当按下鼠标右键时,枪械跟随到相应位置,枪械位置也完全确定。在LateUpdate中更新红点位置,按理说位置应该完全准确,但是无论如何都不行。

 

 

猜测执行顺序有问题,相机位置更新错误,使用Debug.Ray检查,没有问题。

之后我又Debug了射线,判定射线是否击中,大约有2/3的输出是没有击中红点镜的。

 

猜测了一堆,现在弄懂了之感觉自己之前的猜测都没道理。。就不写了。。不过在之前查询这个问题的时候找了个博客,上面写着update后就调用渲染,渲染完成后再调用LateUpdate。我当时就信了!!!!!!!!!!!!!注意,这个是错的。这个博客导致我花了两天时间来进行胡乱思考。。正确的是LateUpdate后才开始渲染,我已经联系那位博主进行了更改。

又猜测应该将枪械跟随代码放到FixedUpdate,红点算法放在Update或者LateUpdate中问题解决。但是剧烈抖动,因为相机和移动脚本使用Update,两者更新频率不同,然后将相机和移动脚本更换为FixedUpdate,问题解决。但是游戏并不流畅,(其实可能对一直用60hz的屏幕的人来说是比较流畅的了,个人用的144Hz的屏幕,看60帧感觉很卡)。这里先不说为什么能解决,因为这是我第三天偶然试出来的,但是不知道为什么。

这几天查阅了很多资料,一直再查unity生命周期什么的。。直到最后一天,我开始在google查询Raycast sometimes miss。。才发现,unity在FixedUpdate后才会更新物理,这个更新物理是什么意思,是才会跟新碰撞体的位置的意思。。也就是说在update中只会跟更新模型位置而不会更新碰撞体位置,因此射线射过来没有击中碰撞体,所以红点在这一帧不会更新位置,在移动后的视野中看起来就像滞后了。这个射线问题在对大型物体进行判定时问题不大,但是在对红点镜镜片这种很小的物体就很容易看出来。

要解决这个问题有两种办法:

第一种,上面已经说了,把要击中的物体的位移写在FixedUpdate中,这样移动后才会连同碰撞体的位置一起更新,射线才能击中。不过这样基本要求相应的脚本比如相机都使用FixedUpdate来更新位置来避免抖动。

第二种方法。

对于60Hz的屏幕来说第一种方法已经没啥问题了其实,FixedUpdate执行50次,和60帧没多大区别,而且可以调整为执行60次。但是我是144Hz的屏幕。在一秒里,屏幕显示了144帧,但是位移只更新了50次,看起来效果和在60Hz屏幕上没有区别。难道就这么浪费144HZ的屏幕?于是又决定不用FixedUpdate。

不用FixedUpdate的话用什么呢,用Physics.SyncTransforms()。这个函数是手动更新物理系统,在调用时对Transform组件发生了变化的物体的物理系统进行更新,比如某个进行了移动的物体的Collider,Rigibody之类。使用这个就能在Update里进行更新物理系统,射线就能射在更新后的collider上了。

关于Physics.SyncTransforms

 

但是呢。。这个如果在144次Update里都执行了,就相当于144次物理更新。。是默认设置的2.88倍。。话虽如此,性能消耗比想象中的要小许多,自己考虑着用吧。然后也可以动态的更改这个刷新次数,比如不开机瞄用80的更新率,开的话用最大之类的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章