前端视角支持游戏开发

November 12, 2019

最近实现了一个天天领钱 H5 游戏,一期基础场景如下 image.png

在做果园之前,我们和其他部门专职做游戏的同学充分的进行了沟通,得到很多宝贵的经验: 框架层面:了解到他们使用的是 cocos creator 框架,但是提到 cocos 框架是 c++迁移到 js 的,所以本身就有诸多小问题,所以新的游戏他们都使用的是 laya 框架,也推荐我们来使用。 通讯层面:后端交互使用的是 websocket 实现的,通讯相关的复杂度很高,因为后端是有状态服务,前端要做 request event 和 response event 之间的关联。所以建议我们还是用短连接。

回来后深入学习了下 laya 框架,也玩了一些同类的游戏,发现其实这类游戏与其说是游戏还不如说是一个运营活动。其实技术上游戏元素很少,只是玩法上是类似游戏的交互。所以未必真的需要上 laya 框架另外:

  • 团队技术储备都是偏向小程序/H5 游戏类的都没什么经验,产品侧给的时间比较短风险也大
  • 游戏本身很简单(交互性很低),判断不需要太复杂的游戏框架

架构分析

那么基于这样的一个限制,思考了一个新的做法:

  1. 美术同学做好果园需要的骨骼动画,
  2. 前端通过某种方式当用户点击的时候播放对应动画即可

剩下所有功能都使用前端技术(React)去做。

关于骨骼动画目前其实三部分:

  1. 树的点击,成长,升级;
  2. 水壶的浇水;
  3. 奖励的弹窗
  4. 领水滴动画

所以其实就是四个动画文件,在合适的时机去播放合适的动画就行了。调研了一圈,最后选择使用 pixi.js V5 来做这游戏的画布,封装了 pixi-dragonbones 进行龙骨的播放。

所以我们的整个架构就变成了下面这样三层的结构,底层就是主场景的绘制,主要动画的执行,中间层就是各种业务弹层,上层是动画弹层 image.png

游戏引擎篇

最开始的时候,只是单纯的封装了三层架构到一个 Game 类里面,然后游戏精灵的创建还是使用的类似 JQuery 的方式进行的。所以一旦有数据层面的变动,必须直接找到对应的精灵实例进行操作,比如坐标变化。这样其实有一些倒退。写的过程中也很不爽,后面就思考了下响应式的做法,有两个办法

  1. 基于 RXJS+对象
  2. 类似 react 的方式声明对象,数据变化导致精灵属性的变化由框架维护

因为之前写过一些 RXJS 的项目,了解里面的复杂度,再团队内推很可能导致较大的延期风险,毕竟很多 RXJS 配套的运行时都没有研发。所以潜意识里就第一时间放弃这个做法(也是目前最后悔的地方,应该用的,后面再聊),而为了方便现有开发,将已有的 react 资产集成进来开始考虑如何用 react 的方式进行游戏开发,第一反应就是实现一个 ReactPiXi (对标 ReactDom),所以快速写了一个 ReactPiXi 验证了一下发现果然可行。 继续完善的过程中,查资料偶然发现一个开源的方案 @inlet/react-pixi ,大部分都实现了,但是遇到点小问题,提了 pr 也很快合入了,最后就选定了这个库,至此游戏精灵对象和 dom 对象两层就在逻辑上合并为一层了。 image.png 游戏精灵的 react 写法 这样游戏对象和数据流就可以通过声明的方式进行关联了。至此整体开发就完全变成前端最熟悉的开发模式,和游戏就没有太多关系了。后续这方面的工作就和 RN 一样,要使用 react 组件的方式封装一些原生的精灵对象即可。

数据流篇

数据流选用了 mobx,这是目前复盘看比较失败的地方。mobx 的优势最大的就是响应式编程,当时使用 mobx 是想的当数据变化的时候,直接响应的回调操作游戏精灵,类似下面的代码

autorun(()=>{
  const sprite = new Sprite();
  sprite.y = store.tree.x;
  sprite.x = store.tree.y;
})

这样只有在tree的x或者y属性变化的时候才会执行这个回调,能有效的进行数据和对象的绑定,但是后面换成react架构之后,绑定关系在render函数中就实现了,所以根本不需要这么写。当时考虑到mobx和react也能很好的集成,所以我们就没有进行数据源的更换,还是沿用了mobx。

在正常业务开发的时候mobx没有太多的问题,但是当进行多个动画对象联动的时候mobx就出现了比较大的麻烦。比如浇水动画播放到2s的时候播放进度条动画,等服务端返回的时候播放数升级动画同时更新主场景,然后升级2s的时候出弹层动画。整个流程使用mobx就比较难以描述,还是要在业务层代码写很多离散的逻辑。而这些正好是rxjs比较擅长解决的问题,会把同步的异步的推拉两种架构都汇总到同一个流的模式上完成,其实是最契合目前的场景的。

另外发现一个问题,mobx 的开发者工具没有 redux 的好用,仅仅支持简单的数据流变化,这种变化监听是牺牲了易用性完成的,及必须将每个 state 的操作都通过 action 进行才可以。而这么写在开发角度看就回退到了 redux 的模式。另外 mobx 对数据没有一个很好的管理方案,store 是离散的,所以没办法对 store 做很好的镜像上报,不容易排查问题。

目前实践下来 Mobx 目前比较好的点就是不需要关注更新位置,只有变更的组件才会更新,不需要写 memo 之类的函数进行处理。

资源管理篇

资源管理的诉求

  1. 支持换肤,动态换肤/静态换肤
  2. 支持自动图集(雪碧图)
  3. 支持自动有损压缩
  4. 支持webp
  5. 最好能做到设计自行传图片,应用后线上自动生效,不需要研发介入

针对上面的诉求资源管理最开始希望做一个托管的网站进行管理,解耦设计和研发之间的依赖。但是发现流程和版本控制上比较复杂,几经讨论就暂时搁置了这个方案,需要进一步探索

新的方案暂时不满足第五点,满足前四点就只需要提供一个本地的构建工具即可,我们按照目录进行了约定,将游戏内使用的资源分为三类:

  1. 文案资源
  2. 颜色资源
  3. 静态文件资源(json动画文件,图片文件等)

目录结构: image.png

所有主题默认继承base,如果存在同名的就覆盖base里面的配置,其中比较特殊的是files下有sprites目录,这个目录下面每个文件夹都会被自动打包成一张图片,最后处理完所有资源后会生成d.ts和json两个文件。项目中实现了一个资源替换的模块统一处理了游戏内皮肤替换和H5皮肤替换

至此,一个使用 react 开发前端营销游戏的架子就完整的搭建起来了。基本满足我们的诉求

  1. 团队成员没有游戏开发经验如何快速上手开发游戏
  2. 已有前端资产如何和游戏打通
  3. 能支撑业务长期迭代

文档

https://juejin.im/post/5c563a4ef265da2ddb293ba3#heading-2 https://github.com/Zainking/LearningPixi#takingitfurther https://reactpixi.org/components/container#props