嘉宾简介

陈伊力

陈伊力 自研引擎力作《乱斗西游》开发经验

2012年硕士毕业于华南理工大学,同年加入网易游戏。现任天下事业部技术经理,《乱斗西游》项目主程。

分享内容

摘要

  《乱斗西游》是一款完美融合MOBA和ARPG玩法的革命性3D动作手游,上市以来表现优异,多次获得苹果官方推荐并入选"App Store 2014年度精选"。
  作为网易第一款3D手游,在自研引擎NeoX上如何做多线程渲染方案?采用了哪些跨平台/渲染/网络同步/存储方案?如何为跨服设计服务器架构?诸多难题,本次由该项目主程陈伊力为您一一详解。

  “在选择游戏引擎的时候一定要从实际需求出发,需要考虑研发时间、画面(2D、3D)、游戏内容、脚本系统以及费用等方面。”

文字实录

自研引擎力作《乱斗西游》开发经验

1/30
  • 自研引擎力作《乱斗西游》开发经验
      6月27日,由网易游戏举办的网易游戏学院第二届公开日《技术进步引发的灵感革命》在广州网易大厦顺利举行。会上,网易天下事业部技术经理、《乱斗西游》项目主程陈伊力就游戏立项时选择游戏引擎的思路及方法、同步交互手游中同步机制的选择等问题发表了演讲。

      他表示:在选择游戏引擎的时候一定要从实际需求出发,需要考虑研发时间、画面(2D、3D)、游戏内容、脚本系统以及费用等各方面。同时他认为手游未来应该更好的利用手机的性能以及将会出现更多同步交互的游戏。

  • 自研引擎力作《乱斗西游》开发经验
       大家下午好。今天我给大家简单做一下乱斗西游的经验分享。

       我大概讲几块东西:一块是我们的技术选型。就是立项之初考虑我们采取什么技术,走什么技术路线。二是技术解析,就是游戏这边用到比较有价值的技术。

      
  • 自研引擎力作《乱斗西游》开发经验
       相信大家在做一个新游戏的时候,都会非常困惑。你接到一个指令要做一个游戏,但是不知道该怎么做,要选哪种游戏引擎,要走技术路线都不是很清楚。

      
  • 自研引擎力作《乱斗西游》开发经验
       当时我们得到的指示说做一个3D的MOBA手游,时间是6个月。一开始定引擎不知道怎么定,就想既然只有6个月,肯定首选比较成熟的引擎。我们就想着一切得从需求出发,既然我们接到指令做3D的MOBA手游,它可能有一些特殊的东西,比如迷雾渲染、动态碰撞寻路,如果没有引擎源码,就没办法做改动,就很难做得很好。还有,完善的脚本系统也是很重要的。我们还需要完善的编辑器,很多开源的引擎,像OGRE,它们跟教课书一样,但是连一个像样的编辑器都没有。还有就是完整的GUI解决方案。此外还要考虑费用问题。

      
  • 自研引擎力作《乱斗西游》开发经验
       最后我们把所有的商业引擎都否决了,决定采用自研引擎,名字叫NeoX(我们习惯称之为"牛叉"),它支持3D/2D,支持Python脚本,跨平台,支持多种图形API(包括D3D/GL/GLE/Metal),还有完善的编辑器和GUI解决方案。

      
  • 自研引擎力作《乱斗西游》开发经验
       服务端这块,我们是没有大世界的手游,会相对简单一些。全新编写的Mobile Server引擎,多服架构支持水平扩展,单组服务器4台物理机便可以支持10万人以上同时在线。C++底层和Python脚本,我们连服务器都是跨平台的,不同方案的同学可以随时随地打开任何电脑就可进行开发,在家里也可以。还有支持热更新。采用MongoDB作为存储方案,它还有一个很好的特性,就是横向扩展,可以跟我们的服务器一样,往里面加机器,就可以支撑更多的用户。

      
  • 自研引擎力作《乱斗西游》开发经验
       技术解析――多线程渲染

      
  • 自研引擎力作《乱斗西游》开发经验
       项目初期,性能不达标,领导说你的游戏好卡,没法玩,手感很差。首先,我们发现主线程非常忙,逻辑占了30%,渲染占了60%,这些都挤在主线程里,当时我们想有没有可能拆分出来,因为我们端游那边也有一些实践,比如将渲染的运算从主线程拆出来。

      
  • 自研引擎力作《乱斗西游》开发经验
       多线程渲染的概念是什么?就是逻辑和渲染分拆分成两个独立线程。优点是更好利用好多核,并发运算。缺点是BUG的数量和线程数成正比(死锁/饥饿/Crash)。

      
  • 自研引擎力作《乱斗西游》开发经验
       想到这个方案后,第一件事做可行性评估。首先是能否在移动平台上实现?实现代价多高?实现后是否有收益?首先面临的问题来自驱动层,OpenGL是不保证线程安全的。第二个问题是怎么拆分渲染任务?要把哪些东西拆到渲染线程?如何在逻辑线程与渲染线程之间传递数据?这是很关键的点。

      
  • 自研引擎力作《乱斗西游》开发经验
       我们简单看一下NeoX引擎的结构,蓝色的跟渲染无关,并且跟平台无关,直到下面NeoX Device那一层是抽象的渲染设备,这是做平台相关的分离。

      
  • 自研引擎力作《乱斗西游》开发经验
       解决GLES的多线程调用,我们的方案是采用Multiple GL Context+Share Group。GL Context,对应一个Surface并保存其所有渲染状态,GL API调用一般只影响当前线程的GL Context。多个GL context可以共享相同的Share Group。

      
  • 自研引擎力作《乱斗西游》开发经验
       每个需要调用GL API的线程,都必须有一个独立的GL Context,建立一个Context Map,用Thread id当Key。

      
  • 自研引擎力作《乱斗西游》开发经验
       传递数据的问题就标配,就是拿一个Queue,主线程作为生产者,渲染线程作为消费者,单向传递数据。

      
  • 自研引擎力作《乱斗西游》开发经验
       资源共享,Double-buffering是以一段数字的形式存在内存里,怎么共享?资源可以一边修改,一边使用。还有就是Copy Everything,这种看起来简单粗暴一点,其实不是,取决你在哪个层次做多线程渲染,可能复制起来开销很大,越到底层就越容易地挑选一种方案。

      
  • 自研引擎力作《乱斗西游》开发经验
       要拆分哪些计算到渲染线程?场景更新、骨骼更新、蒙皮、粒子更新。Render context的组装与提交,这些都不行。最后考虑来,前面的逻辑性东西,它的数据类型和上下文很复杂,数据类型都不是底层那些基本数据引擎,越靠近底层,这些东西如果把它做分离,会更简单一些,所以最终决定偏底层的地方做多线程拆分。

      
  • 自研引擎力作《乱斗西游》开发经验
       最后是改造,从结构上来看,比较清晰,就是把NeoX Device换掉了。

      
  • 自研引擎力作《乱斗西游》开发经验
      改造过程中,也会遇到很多问题,你会有一个帧间同步的问题,我的主线程可能跑的比较快,或者比较慢,那要不要等它?我们的做法是,每一帧选择一个同步点,因为主线程跑得比其它线程快,是没有意义的。并且有一个问题,你的内存会吃得越来越多。此外,还有一个问题,我们严禁从设备做回读。当我们看一个状态是不是我想要的状态时,这是比较没有意义的做法,我们最后的改动,我没办法确认状态时,全部都把它重置就好了。

      
  • 自研引擎力作《乱斗西游》开发经验
       还有GPU资源的创建与销毁,任何线程都能够创建GPU资源,但必须调用GLFlush,因为每个GL Context都有Command Queue。GPU资源的销毁必须在渲染线程执行,把每个销毁指令都做成Render Command。

      
  • 自研引擎力作《乱斗西游》开发经验
       经过之后的共同努力,游戏得到了极大的优化。在iPad mini上提升了10fps。

      
  • 自研引擎力作《乱斗西游》开发经验
       技术解析――同步技术的迭代。

      
  • 自研引擎力作《乱斗西游》开发经验
       我们初期规划时只考虑了异步对战。异步对战,就是把对方数据取到你设备上来,你跟AI打,这是策划深思熟虑后决定的,当时我们立项时觉得中国的网络环境很不好,就不像韩国那边,4G已经很普及了。网络延时对我们网络冲击很大,而且我们的游戏对网络也很敏感。我们设计大量的强制位移技能,我打你并不仅仅是我对你造成伤害这么简单,还可以把你拉过来,如果同步做这个事情,所有的移动操作要等服务器响应后才能动。我们技术释放也有严格的仲裁,这些设计会导致我们的游戏对延迟非常的敏感。

      
  • 自研引擎力作《乱斗西游》开发经验
       所以我们立项之初,做了一个异步对战的模式,到研发中期我们需要为以后做准备,就是WIFI对战,局域网内两个人可以互连。运营初期,我们发现中国的网络开始好转了,开始尝试做一点实时对战的东西。它是一个比较临时的解决方案,现在要推出真?实时PVP,后面会讲到真?实时PVP的一些技术。

      
  • 自研引擎力作《乱斗西游》开发经验
       异步对战只同步角色数据,会把验证数据放在服务器,战斗结果放在服务器,这样就完成了一次对战,流量消耗非常少。如果有一些公司对我们的手游做过竞品分析,我们的流量是遥遥领先的。并且它可以利用碎片时间,不要求对方在线,很符合国情,但这并不是未来发展趋势。

      
  • 自研引擎力作《乱斗西游》开发经验
       我们只是初期这样设定一个游戏,慢慢我们要往实时对战方向靠。WIFI对战我们也采用经典的模式,主客机模式。这种方式比较容易实现,我们当时半个月的时间就可以搞定WIFI对战,但是跨不出家门,一旦没有局域网,两个人就打不起来了。

      
  • 自研引擎力作《乱斗西游》开发经验
       我们运营初期发现,很多竞品也做实时对战的东西,我们觉得自己应该有这样的东西。因为时间紧迫,我们做了一个比较临时的方案,还是基于主客机模式,但是为了实现跨网,我们在中间加了一个Router,所有的指令先发到Router那里,再发到主机,然后再读过来,主机很流畅,但是客机实际上各种卡。为了解决"卡"的问题,我们在客机加上客机先行的Trick。这种方式虽然可以打起来,但是客机体验不太好,因为受限于主机技能以及网络状况,所以体验不是特别好。

      
  • 自研引擎力作《乱斗西游》开发经验
       后来我们开始真的做这件事情,我们把所有的运算搬到服务器,大概花了接近一个月的时间,并且我们在底层做了一些迭代,加入UDP连接,可以允许更高的丢包,更好的适应移动网络。

      
  • 自研引擎力作《乱斗西游》开发经验
       这里值得一提的是同步机制,同步模型有很多种,比较经典的一个是帧同步,就是War3/星际等采用,基于指令驱动各个客户端各自计算逻辑。服务端只管分发指令,每个客户端根据完整的规则运算整个战场。这种同步方式的优点,逻辑好不好写先不说,因为流量消耗非常小,只需要同步指令,但具有比较明显的缺点,如果断线了需要补帧,就是一旦你的战场状态没有了,必须从头开始,从第一个指令运算到连回去的战场状态,虽然现在有一些办法,做到整个战场布局全部呈现,但是非常困难,我们也遇到过这种事情。编码者需要非常小心,因为每个客户端自己算自己战场的状态,所以随机数的要求是零随机,编码者必须非常小心,不能使用任何未初始化的变量。如果组里面来了新人,可能遇到一个问题,不小心使用一个未初始化的变量,整个游戏就乱了。

      
  • 自研引擎力作《乱斗西游》开发经验
       我们最终采纳状态同步,就是LOL/ Dota2都是采用这种方式。客户端只有表现,没有逻辑,逻辑全部在服务器端。它的数据传输量一般来讲比前面的方式大,因为它传输的东西不仅仅只有指令,还有各种状态,但是相对来说实现会容易一些,服务端会承载所有的计算。我们最终采用这种方式的原因,首先我们已经把它写成这样了,前面的方式连数据结构的遍历顺序都有要求,如果要让它顺序确定,需要再做一点修改,变成一个有序,并且这个修改会让内存占用变大。

      
  • 自研引擎力作《乱斗西游》开发经验
       我们在客户端做一些Tricks,一个是客户端移动先行,大部分网游都这么做,不管服务器在什么位置,客户端遥感一动,就先走了。但是跟你服务器真实的位置不一致,那边是你有权利做仲裁的东西,所以必须做位置修正。当角色强制位移时,我们做位置修正,当人把你击退了,我们修正到一个正确的位置。修正的时候我们要根据误差调整,网络数据中断我们采用历史数据做插值。

      
  • 自研引擎力作《乱斗西游》开发经验
       关于网络协议,我们后来采用UDP。因为TCP有一些不好的特性,它不适合我们游戏,比如说它有一个拥塞控制,是完全可靠的协议。使用UDP网络,我们可以更高的丢包容忍度,自己可以做丢包的重发,就是拿网络公平性换我们游戏局部的效率。

      
  • 自研引擎力作《乱斗西游》开发经验
       最后测试结果,我们做了100次RPC环回测试。

      
  • 自研引擎力作《乱斗西游》开发经验
       最后还有我们的一些优化实践。

      
  • 自研引擎力作《乱斗西游》开发经验
       首先就是Neon指令,须16 Bytes对齐,19条指令完成4×4矩阵乘法,效率要比C函数高。它可以帮助我们实现诸如:矩阵乘法、矩阵与向量乘法的功能,事实上,任何数值向量操作都可以考虑用Neon优化。

      
  • 自研引擎力作《乱斗西游》开发经验
       还有就是在Alpha Test中遇到的问题,比如GLES 2中,只要Shader里写了discard就会禁用隐面剔除。这个是有感于前阵子刀塔传奇被人全盘照抄,被别人破解了,做了一个一模一样的游戏。我们分享一下脚本加密的问题,可以打乱OpCode,为了不影响性能,需要进行排序。不同的OpCode处在不同的位置,要进行合理排序,保证效率。打乱后,有经验的还是可以破出来,可以加入新的OpCode,这是没有用处的,但是可以让你的脚本更难破解,但是数量要控制,避免影响性能。性能优化方面,可以引入Computed Gotos,还可以引入PGO。

      
  • 自研引擎力作《乱斗西游》开发经验
       奇技淫巧,Python也能有常量,const模块里放置了各种各样的"常量"。对于Python来说,都是查dict。我们的做法为了优化这种东西,我们对它进行了正则表达式替换,替换成一个真正的常量运行,非常惊讶的是,能让脚本快7%,尤其是一些战斗逻辑里做这些事情。

      
  • 自研引擎力作《乱斗西游》开发经验
       这是我今天所有的分享,谢谢大家。

      
关闭