编程文汇

研发网游所需要的编程技能(1)

这些都是过去一年多学习和运用的知识点,既是总结,也是备忘。

  1. 选择合理的服务器开发语言。
    网游要求低延迟,如果语言有gc,必须考虑gc暂停带来的影响。
    • 可以选择无gc语言:c之类的。
    • 低延迟gc:go(不是十分有保证,几毫秒-几十毫秒),Java(zgc 10ms以下)。
      暂停较低的gc也是复杂的gc,看azul的价格、zgc千呼万唤始出来的历史,就明白它有多麻烦。大部分语言实现的gc都远远落后于这二者。
    • 控制内存大小:比如c#,如果只分配几G内存,延迟大概也就是几到十几毫秒。再比如lua,如果把执行任务分布到几百几千个lua context里,和放在同一个lua context中是不同的,分开的话,gc也是分开的,不会出现集中的暂停,而且每个context的内存不大,gc也会比较快,不过要事先规划好。
    • 或者使用对象池(这也是个大坑),容易出错,也可能避不开暂停。
    • 只要有gc,不要幻想这个开发语言的供应商有多牛逼、无暂停,只是你之前遇到的应用体量比较小、调用频率比较低,没有触发临界点,可以自己google一下英文,都会有相关文章。如果官方或者网上没有任何对此语言gc暂停时间的描述,那么要小心了,一旦触发full gc暂停时间往往都无法接受,可以能秒级、分钟级。
  2. 选择合适的客户端开发工具。
    流行的、用户量大的都可以,就工具链来看,unity最完善,如果项目想更早的出成品,最好选它。
  3. socket通信。
    在现行情况下,有大量的库可以选择,socket、websocket,建议选择websocket,扩展性强,可以和现有的很多程序一起使用。不要自己造轮子,浪费时间。
    • 同时要注意封包合并!
    • 降低序列化次数!
    • 降低copy次数
  4. 加密 压缩
  5. 序列化。就是如何发送封包
    有很多选择,主流是thrift和protocol buf。
  6. 熟练运用多线程,并清楚如何把各种任务合理分配到不同线程。
    也可以用多进程架构,对于c程序来说,多进程更稳定,崩溃就崩一个,重启就行。而对于java c#之类的程序,崩溃只崩溃一个线程,就没必要用多进程的架构,多线程反而更简单。
  7. ECS。
    当下最主流的游戏事件处理架构。确实有独到之处,在此之前,我一直不明白怎么添加灵活的脚本系统,看过ecs之后,豁然开朗。有些事不是难于处理,而是我们没找到合适的工具或者概念。
    • 循环迭代,可以有两种方式:
      • 遍历entity,对每个entity执行system链:这种方式,每个entity自身的生命周期很明显,但是,entity整体却体现不出来阶段性。
      • 遍历entity,执行一个system,再次遍历entity,执行下一个system......。这种方式entity整体的阶段性很明显,整个执行过程有个明确的开始、结束。
      • 以上两种遍历方式,可以根据自己的需求选择。如果自己的逻辑需要有明显的整体阶段性就选第二种,否则就可以任选一种。个人倾向于用第二种,执行流程很清楚。
  8. AI。
    • 现在大家基本都用行为树,不要想着造轮子,就用行为树,有现成的库就用。
    • 不要妄想一部到位。如果没有积累,就想一步到位,会花费你大量时间,影响进度。就先弄明白行为树的原理,照着这个原理硬编码,这样的代码也比较容易维护,AI一般不会太多,可以到后期再整理提炼。
  9. 视野处理。
    每个地图上都会有大量玩家,如果没有视野机制,消息广播就是一场灾难。游戏里的感知范围就是由它来处理的;查找附近的人,也是由它处理;再如碰撞,也可以先通过它做一次筛选。
  10. 瓦片地图。
    这个不是必须,自己看情况。
    • 瓦片地图的随机生成,这个算法基本也是现成的,自己编码调试、优化图形要花很多时间。这里有个技巧:巧用windows的点阵字体,采用正方形点阵,用print打印出形状,降低调试的迭代周期。
    • 客户端瓦片地图自动拼接。这个和具体用的引擎,瓦片地图的api有关。
  11. 高效的定时器机制。
    除了异步事件以外,所有事件都是由定时器驱动的,重要性不言而喻。
    • 简洁。不要往复杂了做,简单、易用就足够。
    • 统一的定时器引擎(方便移植到不同平台),同时也可以减少使用不同定时机制引入的偏差。
    • 最好有统一的帧的概念,比如帧开始,帧结束等事件,因为有很多事情要放到这些时间点去做。
    • 时间的格式也要统一,比如用从1970开始的毫秒数(整数),或者用秒数(浮点数)。
    • 定时器最好提供一套固定fps的机制,比如60fps 30fps,6fps等等,这样不同频率的逻辑直接选择相应频率的定时器即可,可以统一处理,尽量在不明显影响游戏的情况下选用低频率定时器,这样带来的fps整体提升是很明显的。
  12. 矢量计算:
    • 主要是vector的计算,移动、方向、距离、相交什么的都是用这东西计算的,如果开发语言可以支持操作符重载,那是最好不过,不然优先使用流式api,但是看起来就没有那么直观,很啰嗦。c# c++是支持的,java的话可以考虑引入kotlin来处理这部分逻辑。
    • 还有一些数学常量,比如epsilon什么的都要统一定义,避免各种语言不一致。浮点数的等于要自己使用函数判断,不要直接用“==”
    • 这东西尽量自己写,可以避免因各个平台使用了不同的库,而运算结果有所差异。
  13. 尽量减少消息(封包):
    • 优化处理逻辑
    • 尽量减少用户输入产生的事件:比如移动摇杆,如果这种消息不做任何优化,每秒就会有几十个。优化方式有很多:和上次相同就不发,方向接近也不发,或者做成8方向、12向、16向,等等,尽量在不十分影响用户体验的前提下,减少消息个数。
  14. 渲染优化:
    • 平台级优化:比如unity平台下的通用优化手段
    • 处理逻辑的优化:比如降低不必要的高频更新,这种优化是很明显的,可以保持fps的稳定。
  15. 数据存储:
    玩家数据的保存,排行榜的实现,充值消费实时保存,等等,这里可以很简单,也可以分很多层。如果游戏体量不大,比如活跃玩家几十万,就尽量简单。

零零星星的还有很多,一时也没写完,