在上篇文章,主要讲述
更详细的内容,请跳转:联网战斗同步实现
之前实现了一版联网战斗方案,还比较粗糙,存在许多不足的地方。
秉承着 先实现,再持续交付、快速迭代 的理念。
由于实践的效果不是很好,所以需要做一版优化。
总览:
在捋清各个流程步骤时,发现有些轮询机制,会导致延迟的增高。
轮询的作用是减轻各模块功能的压力,降低成本。
我个人是觉得,在实现功能的时候,对于这种可控性成本需求,先按最优版本实现,做出我们最优效果。
之后,再根据成本等问题,进行打折简化。
我们做好对比数据的提供,交由项目负责人决策。
之前,为了让客户端压力小一些,处于“饥饿”状态,服务器推帧间隔设置为 35ms。
但是,在有些模块进行逻辑处理依旧以 30帧/秒(固定间隔 33.3ms)处理数据。
会导致部分客户端逻辑 与 实际有差异,造成抖动。
因此,将帧推送间隔时间修改为 33ms,减缓抖动。
基础方案,过于依赖网络状况。
显然这样是不现实的,所以需要做一套帧缓存机制,来应对网络的波动。
释放帧的时机:
释放帧策略:
第一版做的是可配置缓存。
缓存帧的数量可配,通过实际体验来决定具体缓存帧的数量。
(最开始设想是,先做成可配;之后再根据实际网络状况,动态修改配置缓存的帧数量,类似于流媒体领域的 JitterBuffer)
对于释放帧的数量方案是:
缓存帧数量为 n
帧池内帧数量为 m
经过实际体验,缓存一帧是比较理想的,大于一帧的延迟,大家接受不了。
之前的释放帧策略是针对缓存n帧的普适规则,既然定下来缓存1帧,就做一个更为详细的策略。
先说结论再说步骤:
帧池内帧数量为 m
分为四个阶段
这个是怎么算出来的呢?
我列了一个表格,
列为:
帧池内帧数量 | 延迟时间 | 释放帧数量 | 释放后帧数量 | 下次释放帧时,帧池内可能帧数量 | 恢复正常所需要快播次数 | 往后每次释放帧数量
行为:
从1-15, 每帧33ms,15帧大约为 495ms
接下来开始填释放帧数量,依据以下原则:
然后就开始填表格。
有一点遗憾的是,没有卡到300ms 和 500ms,都差一帧。
之前方案中,客户端收集完帧,上传时机在于执行逻辑帧前:先上传之前的操作,再去执行帧操作。
扩充为三个方案:
我个人一直特别推崇数据驱动,数据不会说谎,除非数据收集的粒度与广度不够。
建立一套完善的统计机制,通过数据来分析用户状态、行为,进而辅助设计,优化体验,做到有依据有目的有验证标准的方式的去解决问题是非常必要的。
统计数值大概包括:
针对每个数值,都可以包括更详细的项:
注意:
首先对战斗产生日志分级存储:
注意:
1级日志内容会比较少(因为会在战斗中生成,每场战斗要重置),是重点的同步信息。这份日志主要是用来判断各客户端战斗是否同步使用的,同步判断包括两部分:
2级日志,是用来辅助1级日志查找不同步问题,但是内容会相对3级日志更偏重联网战斗一些
3级日志,就是更广范围的日志,包含游戏其他功能模块的处理逻辑日志打印
不同步判定&收集:
战斗结束后,客户端战斗产生的1级日志内容,压缩为MD5,上传给服务器
服务器收集各客户端MD5,进行比较;判定战斗不同步即分版本存储本场战斗录像(所有的帧操作)
任意客户端可在debug模式中,拉取不同步的战斗进行回放现场
通过不同步收集,可以获取到不同步的战斗信息。
剩下的就是重现不同,这里采用的方式很简单,就是不断的跑这场战斗。
比如,跑100次战斗,将MD5不同的日志收集起来,进行比对,进而修改,直至这100场战斗的日志产生的MD5均一致。
其实,只要能重现BUG,就离解决不远了,而且是能无限重现的BUG。
前期测试时期,可以让服务器把同步的战斗操作也存储,然后拉去下来本地跑100场。
100场也只是一个样例,嫌多可以跑30场,200场,都随意。
善于利用闲置电脑,让所有客户端都能替你跑不同步测试
通过最新一期的线上测试;大概近6000场战斗,总的不同步率 及 各战斗模式的不同步率 均已经降低到了 0.1%以下。(包含 1V1 PK,多人组队战斗等)。
可能还是战斗场次不够多,需要更大量的数据来检测,但是,起码目前已经到及格线,下一步就是调整优化延迟了。
联网战斗就是这样:
延迟调完调同步,同步调完调延迟。
现在已经算是一个闭环的方案了。
各个方面都有解决方案了,剩下的就是基于这个基础上再去不断的优化完善。
在不同步上,目前待测试的:
不知不觉已经做了这么多,可惜最后无法见证最终的效果,时也命也。
仅愿:
功成须献捷,未必去经年。
这些是在优化过程中遇到的一些问题,采用的一些方法,一些策略
每次接到不同步的反馈,都是异常痛苦的,就如之前所说。
同步模块做的很多的事情都是大方向框架性的,具体的流畅问题、不同步问题,往往是负责最繁琐复杂的发现问题的角色,
然后带着发现的问题找相应模块负责人,去反馈。(最麻烦的就是发现问题,一旦定位问题,距离解决基本不远了)
但是,
同步方面的问题,往往是上线前无人问津,上线后铢锱必较。
一旦出了问题,虽然能把锅分分钟甩出去,但是最终留下来加班修改的还是自己;所以,解决这个问题才是关键。
上线前为什么会无人问津?
自动化测试,本不应该属于这片内容。
作为联网战斗的实现,处理同步问题,是重中之重。
处理方向有两个:
预防不同步,就是使用自动化测试的方案,针对策划所配置的所有项都提前做好测试。做一个改动后,就相关影响的战斗,都多跑几遍;就是把人力做的事情,通过技术来自动化跑。
解决不同步,可以分为三个部分:
收集,就是当出现不同步问题的时候,我们一定要知道;
重现,就是我们要对现场进行重现,这样就方便查找问题,进而验证是否解决问题。
这些通过上面优化方案的自动化工具都已经初具模型。
以前做东西,习惯闷头开发,因为为了做这方面的内容,查阅资料,实验功能,做了各种各样的努力,对这方面的认知和理解还是有一定的把控的。
但是,“不识庐山真面目,只缘身在此山中”,有时候往往会自己把自己限制住,对一个问题不同高度,不同角度,不同层次的解析,也是值得尝试的。
于是,这次优化,准备了以下内容,然后先在内部范围开了个会,收集大家的意见:
事实证明,通过这次会议,收集到了很多有用的建议与方案,回去做了针对性的调整,再去实施。
联网战斗的效果,最终都是归咎与延迟与平滑的博弈,效果平滑,完全可以通过最粗暴的高延迟实现,但是动作游戏的高延迟还是比较影响玩家体验的,所以就需要不断的调整优化,找到那个平衡点。
下面是一个经典的样例:
注意:
在重现不同步时,手机产生的MD5与PC产生的MD5不一致。
经过一系列实验检测发现,在时间种子传输过程中(Lua -> C++),种子的数据类型发生变化,导致种子数值发生了改变。
为了更近一步手机玩家反馈,同时也不影响玩家正常体验。
在联网战斗后,结算界面添加反馈按钮,收集第一手玩家信息。
然后,设定一个卡顿阈值来自动弹出反馈面板(该面板允许玩家选择永久不自动弹出)。
参考: