You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

158 lines
17 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 37 | 移动开发新大陆:工作三年半,移动开发转型手游开发
> 你好我是张绍文。15年认识庆文的时候他还在微信读书负责Android端的性能优化工作。某一天他跟我说想转岗去尝试一下游戏开发当时我脑海里浮现了两个想法一个是游戏部门传说中60个月的年终奖看起来游戏是一个非常有“钱途”的方向另外一个还是担忧抛弃掉Android开发多年的积累转去完全不熟悉的游戏领域他是否可以胜任。
> 两年过去了,庆文以他的个人经历亲身证明了两件事情:
> 第一游戏开发并没有想象中那么困难。当年他是Android版微信读书的技术核心如今在新的岗位上依然也是技术核心。“一通则百通”技术是相通的最珍贵的是我们的学习能力和钻研精神。
> 第二客户端平台知识依然非常重要。手游虽然有非常独立的开发体系但是它还是运行在Android或者iOS系统之上。App开发需要用到的很多技术游戏开发也需要用到。作为Android开发我们一层一层往底层走的优化能力反而是其他大部分游戏开发所不具备的也是我们的优势所在。
> 在我看来无论移动的未来如何发展不管是大前端的天下还是转向手游、IoT、AI、音视频等其他方向今天我们所熟悉的Android平台知识以及学习这些知识所沉淀下来的能力和方法都是最宝贵的财富。下面我们一起来看看庆文同学的移动开发转型手游开发的那些事。
你好我是李庆文来自腾讯旗下一间手游工作室应绍文的邀请在《Android开发高手课》里和你聊聊手游开发那些事。
我在2017年4月从App客户端开发转岗成为一名“用命创造快乐”的手游客户端开发。在转为手游开发之前虽然玩游戏会被游戏精美的画面吸引但是并不清楚游戏是如何将这些精细的画面呈现出来觉得游戏开发好像一个神秘的组织一样不知道游戏开发团队的同学每天在做什么。如果你也对游戏开发有些好奇不知道是不是也有我之前同样的困惑现在我以一个亲历者的身份跟你讲讲转向手游开发的那事儿。
## 揭开手游开发的面纱
一般来说,一个手游项目组包括制作人、策划组、美术组、运营组、程序组、音频组。其中游戏核心部分完全由项目组自己完成,部分美术、音频可以交给外包来完成。
策划组分为玩法策划、数值策划,是整个游戏的灵魂,一个游戏的核心玩法是否好玩、周边系统能否更好地支撑核心玩法,是整个游戏成败的关键。有些团队也会把运营组放到策划组当中,运营同学主要的工作职责是外部合作、策划游戏内的活动、根据活动数据对游戏玩法或者活动进行相应调整,希望可以尽量延长游戏生命周期并提高游戏收入。
美术组包括原画师、视觉设计、2D/3D视觉特效设计其中负责视觉特效的同学与负责程序的同学沟通较多主要是因为程序需要与动画配合比如某个动画播放到某一个关键帧之后程序要处理某个特定逻辑。
App开发的程序组同学关注的更多是手机系统的架构希望尽量多了解系统能为App提供的接口或者能力。而手游的程序组同学更多是关注游戏引擎的架构和如何在引擎的基础上优化游戏性能。
下面我以Cocos引擎为例带你看看手游的开发流程以及手游是如何运行的。
**1\. 游戏架构**
以我的游戏项目为例,游戏整体架构如下图所示。
![](https://static001.geekbang.org/resource/image/01/91/01389bcd58dcb27a5b99983d69b88991.png)
其中:
* 资源更新模块让游戏能够不断发布Bugfix和Features。
* 配置系统提供给策划和运营同学主要是编辑XLS文件用于配置游戏内的关卡、活动等。策划同学的配置会在游戏编译过程中自动转表根据配置文件生成Lua文件。
* Services管理着游戏内的全部数据。
* 网络模块负责与后台建立Socket并通信。
* 游戏内使用MVC模型控制各个游戏内模块的跳转。
* 在单个功能模块中,逻辑与动画分离,逻辑层产生动画事件,动画层消费动画事件并让游戏内的精灵根据事件执行特定动作。
**2\. 游戏场景设计**
Cocos采用节点树形结构来管理游戏对象一个游戏可以划分为不同的场景Scene一个场景又可以分为不同的层Layer一个层又可以拥有任意个精灵Sprite。游戏里关卡、周边系统的切换也就是一个一个场景的切换就像在电影中变换舞台和场地一样由导演Director负责不同游戏场景之间的切换。一个Cocos游戏的基本结构如下
![](https://static001.geekbang.org/resource/image/9c/0d/9ca7c16d109401bf378443f1a484620d.jpg)
在同一个场景中的多个精灵可以通过执行动作Action来实现缩放、移动、旋转从而达到让游戏“动起来”的目的。
Cocos引擎中坐标系X轴向右、Y轴向上Z轴Z-Order垂直于手机屏幕指向屏幕外由于是2D游戏所以Z轴只用作控制游戏元素的前后顺序。同一个场景中两个重叠或者部分重叠的 SpriteZ-Order大的精灵会遮挡住Z-Order小的精灵相同Z-Order的精灵后添加到场景中的会遮挡住先添加的。
![](https://static001.geekbang.org/resource/image/a9/ba/a901e8f4c070edae3da1097d55bb7bba.png)
有了树形结构和Z-Order就能利用Cocos引擎构建出任意2D游戏场景了。
**3\. 手游的运行流程**
游戏的主Activity在`onCreate`时候创建一个GLSurfaceView它继承自View是引擎用于绘制游戏内容的。同时GLSurfaceView也接受玩家的点击事件用于引擎与玩家的交互。
GLSurfaceView创建之后引擎开始执行`main.lua`脚本,导演调用`runWithScene`接口游戏就进入到第一个“场景”中了。GLSurfaceView中维护了一个`while`循环循环中发生的事件比如SurfaceView创建、宽高发生变化等通过`Renderer`接口传递给外部。`Renderer`其中一个接口就是`onDrawFrame`Cocos会在`onDrawFrame`接口实现中根据游戏设置的帧率,每间隔一段事件通知一次导演执行一帧游戏循环。
![](https://static001.geekbang.org/resource/image/03/db/039b709bbd29787d94ba661f38c0cadb.png)
借用《我所理解的Cocos2d-x》书中的单帧处理流程图片你可以看到在每帧中
* 先响应用户输入,因为用户输入可能影响到当前帧接下来的游戏逻辑。
* 根据每个精灵设置的动画,计算精灵的位置、缩放等属性。
* 执行游戏逻辑,比如更新玩家得分、修改当前游戏状态等。在这里开发者依然可以更改精灵的各种属性。
* 遍历UI树根据之前对精灵属性的设置生成绘制命令交给OpenGL绘制最后把渲染的内容显示到屏幕上。
## 游戏与App开发的异同
**1.关于热更新**
热更新是App开发和游戏开发都避不开的话题但是App的热更新与游戏的热更新有着本质的区别。App开发的热更新涉及整个系统层面的实现细节比如我在老东家工作期间实现的热更新框架需要了解的内容非常多包括Dex文件的加载过程、SO文件查找过程和APK编译过程的详细细节每个不小心都可能导致热更新不生效甚至导致严重的外网Bug。
然而使用脚本的游戏引擎是天生支持热更新的。手游热更新,也叫作资源更新,更新内容包括代码、纹理图片、界面等。
以Cocos引擎为例Cocos引擎的核心是C++实现的对外提供了JS、Lua接口。业务开发过程中绝大部分代码是Lua代码只有涉及系统相关接口比如支付、音频播放、WebView等才需要写一部分系统相关的代码。Lua代码经过编译之后生成的二进制代码是“.luac”文件并通过luaL\_loadbuffer接口来加载一段“.luac”的文件内容。Cocos引擎在初始化过程中会设置“代码查找路径”加载Lua代码的时候会挨个遍历每一个被设置进来的路径直到找到或者找不到对应的“.luac”文件。而热更新只需要下载“.luac”文件放到优先的查找路径中游戏重启之后引擎就会优先加载新的代码啦。
当然还有另外一种更为激进的方式,不需要重启游戏就能达到热更新的目的。
Cocos以LuaEngine为入口执行Lua代码驱动C++部分的图形库、音频库、物理引擎等。LuaEngine缓存了通过`luaL_loadbuffer`接口加载起来的代码只要重启整个LuaEngine清理掉缓存的代码就能在后续需要执行代码的时候去重新加载代码文件。这样就能做到不重启游戏也能达到真正“热”更新的目的。
其实Unity引擎也是类似可以使用xLua等类似的技术让Unity引擎也能开心地写Lua脚本。
对于热更新图片,只需要清理掉引擎缓存的图片缓存即可。这样引擎在下次使用该图片渲染界面是,发现在缓存中查找不到该图片,自然就会去加载新的图片了。
**2\. 游戏也需要优化安装包大小**
在App开发过程中优化安装包的大小是一个不可避免的话题。压缩图片、代码插件动态下载等众多实现方案也都为大家所熟知。手游安装包大小可能并不像App安装包要求那么严格热门游戏比如《绝地求生》《王者荣耀》安装包大小几乎都接近2GB。虽然玩家对游戏安装包大小不是特别在意但有意识的减小安装包大小也是很有必要的毕竟安装包大小会影响游戏转化率进而影响游戏收入。
手游安装包中图片资源占了安装包体积的绝大部分以PNG图片为主。通常直接压缩PNG图片是有损压缩经过游戏引擎渲染游戏界面会出现很多噪点。这里有一种分离Alpha通道的压缩方案可以供你参考。32位透明PNG图片包含了四个通道RGBA其中每个通道占8 Bit。把RGBA四个通道中的Alpha通道数据拆分出来存储为一张PNG8图片剩下的RGB数据存储为一张JPG格式图片。JPG格式的图片在保证高压缩率的同时也能保证图片质量以此达到压缩图片大小的目的。这种方案的压缩率大概在70%左右。
在游戏运行过程中使用纹理之前先把JPG和PNG8文件都读取到内存将他们包含的RGB和Alpha数据重新合并后交给OpenGL用于渲染。这个压缩方案简单易操作并且运行时不需要额外的第三方库支持。
**3\. 游戏的性能优化**
性能是开发者永远离不开的话题。手游和App开发一样也需要关注游戏的各方面性能指标比如内存、帧率等。
**内存**
* 纹理压缩,减少纹理占用的内存。我前面提到的安装优化包大小中压缩图片,也有助于减少内存占用。在美术同学能接受的前提下,降低图片深度也是快速优化内存的一个方法。
* 使用精灵池,尽量复用已经创建的精灵,减少屏幕中精灵创建的个数,及时回收不用的内存。
* 切换场景时尽快释放上一个场景的无用纹理。
**帧率**
* 减少Draw Call。游戏引擎每次准备数据并发送给GPU去渲染的过程称为一次Draw Call。Cocos把使用相同贴图、blendFunc、Shader的精灵合并到同一个渲染命令中提交给GPU。我们常说的合图就是把很多小图片合并到一张大图片中这样当屏幕中有多个连续渲染的精灵用到同一个大图CPU就会有可能把它们合并之后统一提交给GPU。另外Cocos合并渲染命令是根据精灵在场景中的顺序来做的最终的合并效果不一定是最优的。我们可以实现自己更优化的合并逻辑。
* 减少屏幕内需要渲染的精灵个数。如果屏幕中有“成组”的元素需要绘制比如排行榜中每个玩家的Item在Cocos中可以使用CCRenderTexture把每个“成组”的元素绘制到CCRenderTexture绑定的纹理然后用CCRenderTexture替代原有的Item。这与Android开发中自己实现View的onDraw函数类似并且这也是减少Draw Call的一种方式。
* 分帧。真的遇到CPU密集型的任务时例如同屏幕确实需要大量精灵可以把它们拆分在不同帧里尽量保证游戏内每一帧在规定时间内完成否则会出现卡帧现象。
## App开发转手游开发的思考
刚刚过去的2018年是游戏比较惨淡的一年游戏版号从暂停审批到现在终于慢慢开始放出简直是千军万马过独木桥。但从目前来看2018年确实也像某些大咖说的那样可能是游戏行业未来几年最好的一年。国内拿不到版号的游戏只能考虑出海来寻求出路但是出海需要在海外当地运营或者找海外代理。自己运营可能会遇到水土不服画风或者游戏内容也可能不满足当地玩家的需求找海外代理却可能面临连游戏代码都被代理商卖掉的风险。
手游早已经结束了野蛮生长的年代6年前可能随便一款游戏都可能成为爆款类似捕鱼达人、保卫萝卜它们诞生在Android、iOS手机爆发的那几年。而现在的游戏玩家需要的是更新颖的玩法和更加精细的游戏体验。例如MOBA类的《王者荣耀》、SLG类的《乱世王者》甚至细分领域的《恋与制作人》也都在各自的玩法上不断打磨让玩家心甘情愿“氪金”。如果想要转向这个赢者通吃的行业确实需要谨慎提前考察好目标团队是否符合你的职业发展、是否有盈利能力尽量避免踩坑。
**1\. App开发转手游开发可行吗**
2018年和2019年市场对App开发的岗位需求量已经不像几年前那么大而且也有一些同学也咨询过我App开发转手游开发是否合适。关于这个问题我的回答是可以转到手游开发岗位而且难度并没有想象中那么大。
手游开发跟App开发相比只不过是换了工作内容关注的领域不同而已基本原理是相通的。App开发需要用到的很多技术游戏开发依然也需要用到比如卡顿分析、网络、I/O优化。而且游戏的大部分核心逻辑库是与平台无关的所以不必担心适应不了新的工作内容。下面我来总结一下转到手游开发的优势和劣势。
* **优势**
对手机系统有深入了解能迅速切入一部分游戏团队内的工作内容。比如我刚刚转到手游团队时解决的第一个问题游戏在Android系统上JNI找不到函数的Crash稳稳占据了每个版本的前三名版本Crash率由于这个问题而一直保持在1%左右。当时由于团队对系统没有深入了解所以并没有很好的解决办法。这个问题其实读一遍Android系统查找Native函数的代码基本就能找到解决方案了。
* **劣势**
毕竟是两个不同的开发方向转方向的同学也需要迎接压力。与毕业后直接进入游戏行业的同学相比“半路出家”的同学相对缺乏原始技术积累需要从基础的概念开始学起比如上面提到的Draw Call、合图、OpenGL、Shader需要消耗大量精力去学习。所以需要转方向的同学足够自律主动补齐这些技术短板。
**2\. 转方向后的技术路线**
有些同学说App开发者的技术路线宽担心手游开发者将来技术路线会越来越窄将来不好找其他工作。有这个担心是正常的手游开发岗位的市场需求量比移动开发需求量小太多但这不应该成为害怕转手游开发的理由。个人觉得需要从一个更宏观的角度看待这两个不同的开发岗位不要钻在技术的牛角尖里无法自拔。技术通道无风险而更应该担心的是游戏的政策风险。
小游戏在2017年和2018年真是火了一把鉴于小游戏入门简单、上手快想要转方向的同学可以先用小游戏来练手。“麻雀虽小五脏俱全”看明白一个小游戏的执行流程之后相信你对游戏开发也就会有一个感性的认识了。
最后祝各位开发者游戏愉快!
欢迎你点击“请朋友读”,把今天的内容分享给好友,邀请他一起学习。我也为认真思考、积极分享的同学准备了丰厚的“学习加油礼包”,期待与你一起切磋进步哦。