8

苏打世界的流体灌装是如何实现的 – Liquidfun在Cocos2d-x-Lua中的应用

 3 years ago
source link: https://wuzhiwei.net/liquidfun_in_soda_world/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

新作「苏打世界」已经上架AppStore,并在首页被大屏推荐。上架伊始便颇受玩家喜爱,目前已经收获大量玩家的五星好评,欢迎大家前去下载试玩。

本文将介绍游戏内的灌装系统是如何实现,最后将附上参考资料以供读者们阅读和研究。以下将不会有具体代码,只讲思路和方法。

在「苏打世界」里,一个很重要的日常任务便是生产苏打。由于产品汪提姆绿需要尽可能的去模拟真实的苏打灌装,比如苏打灌满后溢出和未灌满时水面的波澜起伏等,这也导致用动画来模拟实现是不可能的。

所以便只能去寻找一个支持流体功能的2D物理引擎,并将其移植到我们开发采用的Cocos2d-x + Lua的游戏框架中。

LiquidFun

当接到这个需求后,我便联想到了Google的LiquidFun

由于LiquidFun支持JavaScript,这也非常方便我们快速验证原型,即LiquidFun到底是否合适我们的游戏。

对LiquidFun的testbed修修改改后,我快速的建立一个灌汽水的网页版原型。同事们看完觉得还可以,便开始了将LiquidFun移植到游戏的工作。

怎么接入?

LiquidFun依赖于2D物理引擎Box2d,可以认为其是Box2d的加强版,比原先的Box2d增加了流体粒子功能。

我们游戏采用的是Cocos2d-x+Lua的框架,所以问题变成了:如何整合LiquidFun到Cocos2d-x中,并绑定其常用API到Lua中,使游戏逻辑可以操控汽水的灌装,隐藏,销毁和回收。

将这个复杂的任务分解后,大致如下:

  1. 移植LiquidFun库到Cocos2d-x中,覆盖其原先的Box2d库
  2. 在C++层跑通,使LiquidFun的流体粒子能在Cocos2d-x正常工作
  3. 对LiquidFun做tolua绑定,使Lua中使用LiquidFun成为可能
  4. 在Lua书写灌装的逻辑,实现一个灌装的DEMO

如何移植到Cocos2d-x

关于LiquidFun的移植工作,已经有先驱帮我们趟坑了。Cocos2d的作者Retro在其博客中详细阐述了:如何将LiquidFun移植到Cocos2d-x,以及如何利用metaballs技术来渲染粒子使其看起来更像流体

读者不妨先去阅读以上的两篇文章,花点时间,搞懂每一步的目的和原因。

简而言之,Retro在Part1所做的工作是将LiquidFun的每个流体粒子在物理世界的坐标转换到Cocos2d-x的绘制坐标中,然后用GL_POINTS命令将粒子绘制成一个个的小点显示到屏幕上。

在Part1内,我们已经完成了LiquidFun对Cocos2d-x的C++层的移植工作。但是仅仅显示一个个白色的小点是不够的,因为这样看起来不像流体而比较像沙子。

接下来在Part2中,Retro引入了一张中间圆形实心,四周趋于透明的纹理代表一个粒子,利用一个透明度阀,将此透明度(例如0.01)以下的全部显示为全透明,而之上都显示为一个纯色。然后用一个RenderTexture去实现整个Shader的绘制,最后将其显示在屏幕上。当两个粒子很临近时,可以看出一个不规则的融合形状,这个技术就是最基础的metaballs

C++绑定到Lua

由于Cocos2d-x没有对Box2d进行tolua绑定,所有绑定的工作需要从零开始一遍。

原先本想采用Cocos 3.x引入的ini格式的配置进行绑定,后来发现不太现实,因为有太多的结构体需要进行绑定到Lua。

所以只好回归Cocos 2.x时代的pkg绑定。关于如何进行pkg绑定的方法,不是本文关心的内容。因为基本都是把C++头文件按照指定的规则转译为pkg文件的体力活。

我曾经在2.x时代因为极度痛恨这种手工方式而写了一个Lua脚本来进行自动转译,这个脚本现放于Github中,年久失修,可能有坑需要手填,慎用!

这基本是个体力活,为了大家移植方便,不再趟坑,我已经将转译好的pkg文件和tolua后的C++文件上传到Github的仓库中,大家可按照MIT协议进行下载使用。

Lua逻辑实现

  1. 构建好仓库和瓶子的物理形状
  2. 填充好粒子系统到仓库中
  3. 用按钮来控制仓库出口开关的位移
  4. 生产按钮按下时,仓库出口开关移走,粒子以重力掉落到瓶子中
  5. 生产按钮恢复时,仓库出口开关回原位,粒子将被其阻隔而不能下落
  6. 当粒子掉到屏幕外时,销毁该粒子,并在仓库内重新创建一个新粒子

一张图让你明白,其实我们后面是真的在汽水!

  • 尽可能只使用一个RenderTexture来渲染所有粒子,以避免重复绘制的浪费
  • 如果可能,限制用于渲染流体粒子RenderTexture的大小,避免全屏绘制,以降低绘制面积
  • 精简shader,移除没必要的变量和多余的if...else...分支判断
  • shader使用低精度浮点数即可:precision lowp float;
  • 将耗时操作放在C++层执行,以避免每次循环内调用tolua的开销。比如从C++层直接获取一个在屏幕外的粒子列表,而不是在Lua层对所有粒子循环判断其位置。因为每次粒子位置的获取都需要消耗tolua时间,粒子数一多,就容易积少成多拉低性能了。
  • 粒子对玩家不可见时,将粒子系统的更新暂停,直到下次被使用时再恢复,可以极大缓解设备发热和耗电量
  • 降低Box2d世界更新的步进值,以达到性能和效果的平衡
  • 在保证效果的情况下尽可能减少同屏共存的粒子数
  • 自己管理粒子的生命周期,以避免在每次粒子的步进更新中检测是否销毁
  • 使用release版,掩面逃)

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK