作者dorgonman (dorgonman)
看板GameDesign
标题[程式] [UE4]网路同步之Character谋杀事件
时间Thu Jun 28 22:09:07 2018
最近用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就有地方可以站。
--
Sent from my Windows
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 218.35.182.84
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/GameDesign/M.1530194954.A.836.html
1F:推 coolrobin: 推推 06/29 00:02
2F:推 dklassic: 这也写得太有趣了吧 Xd 06/29 12:18
3F:推 ConSeR: 摔死XD 07/05 18:28