最近用UE4遇到了一个非常奇怪的现象,
困扰了好久,于是我决定用力追查下去…
网志版:
http://dorgon.horizon-studio.net/zh/archives/1561
===============全文开始====================
事情是这样的。
本来我是想要测一下network做replication变量相关的功能,因此建了一个空白的level(全黑色那张),并做了一个Character后加了几个变量与写了几个function来进行实验。在按下play之后……嗯,一开始变量成功的从server传过去给client了(开了二个player,一个listen server,一个client)。很好!就在我满足于事情正如想像中进展的时候,突然间,变量却完全送不过去给Client了!
到底发生了什么事?就我的理解上,我用的是COND_None,只要server有任何变动的话,变量应该会送给client阿?难不然变量是unreliable的?不对阿,replication变量肯定都是reliable,顶多就只有因为频宽的问题而延迟送达而已。经过我的反复实验,最后发现了一件惊人的事实:
我listen server上的character居然被砍掉了!
在得知这项资讯后,我决定马上到AMyCharacter::EndPlay这个function中下断点──我想想看看到底是那个混蛋砍了我的character。可惜的是,我依然没有抓到凶手。──AMyCharacter::EndPlay是被呼叫了没错,可是EndPlayReason只写了一行Destroyed。这资讯完全没有帮助,因此我决定循着call stack往上层追,最后看到了这段程式码:
https://gist.github.com/dorgonman/00c3c173f3a70fc040d88d00915e3323
WTF!?网络收到了断线通知之后,就马上把我的角色砍了!?为什么listen server上所拥有的Character会被断线?我并没有加入任何砍Actor的逻辑阿?难道是因为自动触发GC的缘故?也不对阿,Actor的生命周期应该是跟着world才是,要砍Actor的话只能手动呼叫Destroy(K2_DestroyActor)才行。我完全被眼前的事实惊呆了,这跟我过去对于UE4相关的知识完全不相符。
“这样不行,一定要找出原因。”──虽然感受到了挫折,但我才不会因为这点小事就被击败。为了找出被断线的原因,我决定利用手边仅剩唯一的蛛丝马迹“Bunch.bClose”来找出杀死Character的凶手是谁。事出必有因,既然Bunch.bClose这个flag是true,那必然是有那段逻辑使然。在搜遍了整个引擎之后,总算发现下面这段非常可疑的程式码:
温ttps://gist.github.com/dorgonman/5f021b9eac330493d3f012bfda9a8d99
当Channel->Close();被呼叫之后,就会送出一个CloseBunch出来并把我的Character杀死。而为什么会进到这段逻辑的根本原因,在于bIsRecentlyRelevant是false,意即,listen server认为这个Character已经跟自己没有任何瓜葛了,所以就杀了他。
到这里,似乎就已经破案了,原来人是Server自己杀掉的阿……
但李组长眉头一皱,事情似乎并不是这么样的单纯。为什么listen server会无缘无杀的去杀掉自己的亲生儿子?难不成二儿子就那么该死吗(PlayerController2)?大儿子(PlayerController1,listen server)不是还活蹦乱跳的好好的在那边吗?为什么就只有二儿子!?而且是非常固定,每次在世界被建构起来约10秒钟之后,这件凶杀案就会必然的发生。不管是我模拟了几十次还是几百次,都无法逃离二儿子死亡的结局。
这世界上肯定是哪里有Bug了!──我的心底对着Epic大神这么叫嚣著。
这么显而易见的Bug,不可能只有我才会遇到。于是我转而求向古歌大神的协助……然而,却还是一无所获。没办法,只好靠着新发现的事实,继续的往上追踪。目前我们知道,当Listen Server决定要跟二儿子断绝关系的时候,bIsRecentlyRelevant就会被设成false,那么我们只要找到设置bIsRecentlyRelevant这个flag的源头,就必然有办法找出这件凶杀案背后发生的原因。
杀人凶手,是身为父亲的Listen Server。──这点已经不容质疑。
但事实的背后往往有着更令人讶异的真实。只要努力不懈的话,真理永远就只会有一个。是的,最后我终于到达了,那扇真理之门的面前:
https://gist.github.com/dorgonman/baba67405e381f52f35f7112c7b610a7
看到这段程式码之后,我马上理解了整个案情的来龙去脉。为了证明事实真的如同我所想像的,我马上再次开启了最后一次世界的模拟:
“failllllllllllllllllllllllllllllllllllllling──────!”我仿佛听到了惨叫声。
二儿子Z轴的数字正以著不可思议的速度往下递减──在重力加速度的加成下,二儿子快速的离大儿子远去,然后在到达一定的临界值(NetCullDistanceSquared)之后,就这么消失在世界的尽头。
什么嘛,原来是摔死的。
至此,一切的谜题都已经解开了。
想要避免这件惨绝人寰的凶杀案,大致上有以下几个解决方案:
将CharacterMovement中的Default Land Movement Mode 设成Flying:这样你的角色就不会掉下去了。
将Character中的Always Relevant这个选项打勾:但是你的Character还是会无限的往下进行自由落体的动作,只是,这个flag是有其必要性,它在多人连线游戏下可以减少不少网络频宽,在调整这个设置前,最好对这个flag有正确的认识。
下面放个Cube:这样Character就有地方可以站。