网络游戏UI中的现实时间
其实标题也可以换一种说法,网络游戏中涉及到现实时间的UI展示,不过这样太长了不太适合做标题。
我们拿手游来举例子,手游中会有很多与现实时间相关的元素,例如礼包,卡池,邮件等等,它们通常会有一个基于现实时间的倒计时或者是详细的时间展示。
要注意,我接下来所写的并非绝对适用于所有需求,一定要根据需求来分析,不能生搬硬套。
原则上不使用本地时间
这点其实不难理解,主要出于两方面的考虑:
一是因为你不能确保本地时间是安全的,玩家拥有修改设备本地时间的权利,以前的老游戏经常会出现这种利用修改时间来卡bug的现象,对于单机来说无伤大雅,但对于一个要联网的游戏来说那可就要了老命了。例如玩家的体力是随时间恢复的,你使用了本地时间,然后玩家改了本地时间,然后噩梦就开始了,校验过不过,过了你返回给后端了,那就是利用漏洞作弊成功了,校验不过,那你又要怎么办?最好的解决办法就是不要出现这种问题。
二是玩家的时区问题,一个utc+0的玩家的本地时间和一个utc+8玩家的本地时间那可是不一样的,例如说北京时间八点,东九区的日本或者韩国玩家登陆了游戏,发现卡池提前一个小时关了,玩家心态可就炸了,所以我们可以看到很多游戏的国际服都会在时间上标注utc时间,这样是为了确保卡池关闭的时间是统一的。
所以可以这么说:网络游戏中涉及到现实的时间,一定都是服务器给的时间戳。
此外对于一些强依赖的系统,例如天气系统,早晚的变化可能是基于服务器时间来,就会有对不上的问题,解决方案自然是有的,例如奶排很粗暴,直接在设置项里面加了一项调整时区的功能,好处就是不会需要额外的权限。智能一点可以寻求玩家下放定位权限,然后通过定位来调整,毕竟在这个设备上只要做一次就行了。
取巧:显示的精度越低越好
通过上面的讨论,我们基本可以明确倒计时基本都是时间戳与约定的期限来做差值,一般来说会有专门的表,或者表中会有专门一列留给运营去定期限时间。至于这个期限是客户端读还是服务端给,一般来说还是客户端读,因为服务器端会做校验,即便玩家有这个能力修改了,到了服务器校验这关他也过不去。
对于Unity来说,做差值并且显示的逻辑是跑在UI的FixedUpdate里的(实际可以不用那么精确,放在自定义的定时器类里也是可以的),这能最大程度确保设备每秒计算的次数是一致的,但即便这样我们也不能打包票时间就一定是跟服务器端完全对齐的,所以你会发现很多时候显示会避免展示秒,即便发送给服务端的请求会做校验确保超时的请求会return错误码,但你最好也别让玩家感知到差距。最好最方便的方法就是减少显示的精度。
避免长连接 减少服务器主动推送
要实现服务器主动推送的功能,需要客户端在启动的时候建立一个socket连接到服务器,所有数据都通过这个连接来传输,但维持大量长连接对于服务器来说是比较耗性能的。因为长连接即便在没有发起数据传输的时候也会有链路检测包的存在,所以要不要用长连接就要看项目的取舍了。你想要让服务端建立一个长连接能一直拿时间戳,这可不可以呢?其实不考虑性能那是完全没有问题的。假如项目确实非用长连接不可,你就要和后端好好讨论讨论了,是不是可以通过一些调度的方式去规避,例如挂机的,长时间没有活动状态的,我们是不是可以让客户端主动断开长连接来减少服务器的负担。
有一个例子,就是天数变更导致的数据变化。假如你是夜猫子经常凌晨打手游,你就会发现到点了游戏会自动重启走一遍登录流程,这是因为天数变更,每日任务/每周任务/每月任务等等都会发生重置或者移除之类的变化,此时后端的修改需要同步到客户端来,如果用长连接当然可以主动推,但大部分游戏还是会选择让玩家重走一遍登录的方式来重新拉取数据,毕竟这样更保险也不费事。
总结
以前的我甚至想过在场景里放一个脚本专门做轮询短连接来向后端拉取数据,其实也不是不行不是吗?今晚整理思路的时候重新复盘了一下,请教了几位朋友,专门记录一下这些问题,希望能对看到的人有所帮助。