使用白鹭引擎开发微信小游戏的一些流程改进

微信小游戏是运行在微信环境下的类 HTML5 游戏,可以使用诸如 Laya, cocos2d, egret 等 HTML5 游戏引擎来进行开发。笔者最近使用 egret engine(白鹭引擎)开发了两款微信小游戏,一款单机,另一款支持联机对战,下文是在这两个项目中使用 egret 总结出的个人最佳实践。

原理

将 egret 发布为微信小游戏后会发现导出的目录 xxx-wxgame 下有以下文件和目录:

图中标注的部分就是 egret 为生成微信小游戏做的工作。其中 egret.wxgame.js, weapp-adapter.jslibrary 是 DOM/BOM API 及白鹭 API 向微信小游戏适配的代码,使得我们面向 egret 编写的特异代码能无缝迁移到微信小游戏环境下运行,openDataContext 是开放数据域代码目录。

问题1:Platform.ts 与 platform.js

按照 egret 给的工作流,源码里的 Platform.ts 只是用于 egret 自带的调试工具调试时使用,使用假数据模拟微信平台接口的返回值,编译之后生成的是对应的 platform.js

这里存在两个问题:

  1. Platform.ts 不会(也不应该)直接同步编译到 platform.js 中,所以这部分代码得编写/修改两次
  2. platform.js 无法从 Platform.ts 直接编译得到,所以项目源码与导出的微信小游戏代码都得加入代码库进行管理

我的解决方案:基本抛弃白鹭工作流里的 Platform.ts,实现自己 Platfrom 处理模式。

src 源码里实现 Platform 机制,最终平台代码直接打包到我们的输出的 main.min.js 中,对于 wx 对象不存在无法通过编译的问题使用 src/global.d.ts 编写类型声明来解决。

这样平台代码只需要在一处进行编写,而不需要向之前一样:在 wing(egret 提供的基于 VS Code 改造的编辑器)编写/修改完平台代码后,还需要在微信开发者工具内编写/改写 platform.js

其实包括下面的问题 2 都是围绕着一个核心思路来改造工作流:一处编辑,无需切换到微信开发者工具去修改代码。

问题2:开放数据域

egret 在导出微信小游戏时,会生成一个比较简单的 openDataContext 目录到输出目录,最终开放数据域代码的编写需要切换到微信开发者工具去编写。

所以对于开放数据域,也会存在与 Platform 相类似的问题1,解决思路是利用 egret 编译任务定制功能。

首先我们直接在项目下自己新增 openDataContext 目录,里面放置的全部是开放数据域相关的代码。

然后我们修改 scripts/wxgame/wxgame.ts 中的任务代码,实现我们的需求:最终直接使用源码目录的 openDataContext,而不使用白鹭帮忙生成的 openDataContext

思路是:删除 egret 生成的 openDataContext(可选),递归复制源码目录的 openDataContext 到微信小游戏导出目录下,(可选)也可以先进行代码打包、压缩再复制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import * as fs from 'fs';
import * as path from 'path';
import * as cp from 'child_process';

function copyFileSync(source, target) {
let targetFile = target
if (fs.existsSync(target)) {
if (fs.lstatSync(target).isDirectory()) {
targetFile = path.join(target, path.basename(source))
}
}
fs.writeFileSync(targetFile, fs.readFileSync(source))
}

function copyFolderRecursiveSync(source, target) {
let files = []

let targetFolder = path.join(target, path.basename(source));
if (!fs.existsSync(targetFolder)) {
fs.mkdirSync(targetFolder)
}

if (fs.lstatSync(source).isDirectory()) {
files = fs.readdirSync(source)
files.forEach(function (file) {
let curSource = path.join(source, file)
if (fs.lstatSync(curSource).isDirectory()) {
copyFolderRecursiveSync(curSource, targetFolder)
} else {
copyFileSync(curSource, targetFolder)
}
})
}
}

function deleteFolderRecursive(folder: string) {
let files = [];
if (fs.existsSync(folder)) {
files = fs.readdirSync(folder)
files.forEach(function (file, index) {
let curPath = folder + "/" + file
if (fs.lstatSync(curPath).isDirectory()) {
deleteFolderRecursive(curPath);
} else {
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(folder);
}
}

export class WxgamePlugin implements plugins.Command {

constructor() {
}
async onFile(file: plugins.File) {
// ...
}
async onFinish(pluginContext: plugins.CommandContext) {
// ...

// 其他定制任务
copyFileSync(`${pluginContext.projectRoot}/platform.js`, `${pluginContext.outputDir}/platform.js`)

// 开放数据域
const openDataContextTargetPath = path.join(pluginContext.outputDir, 'openDataContext')
const openDataContextSrcPath = path.join(pluginContext.projectRoot, 'openDataContext')

// 删除输出目录的 `openDataContext`,再复制 `openDataContext` 目录到输出目录
// deleteFolderRecursive(openDataContextTargetPath)
// copyFolderRecursiveSync(openDataContextSrcPath, pluginContext.outputDir)

// 打包压缩输出
cp.exec(`webpack --entry ${openDataContextSrcPath}/index.js -o ${openDataContextTargetPath}/index.js`)

// 可以自己再进行特异化订制
}
}

这样我们每次点击发布或执行 egret publish 时便会同步源码目录的 openDataContext 到输出目录,也达到了我们只需要在 wing 这一处编写所有代码的需求。

问题3:代码的压缩与二次混淆

在未对白鹭工作流作任何修改的情况下,白鹭输出时会进行编译、打包和简单的压缩混淆,但是混淆的还是不够,且开放数据域的代码无法被打包,尤其是当两款游戏用到了一些公用组件时,直接用白鹭输出的代码包去微信那边提审有可能会因为代码包查重通不过而被拒。

对于开放数据域来说,代码的压缩已经在问题2中顺便给解决了。

而游戏逻辑代码输出的文件是 main.min.js,这样混淆的方案就随你选择了,你可以使用 uglifyjs 来对输出的 main.min.js 来进行压缩

1
2
# 替换 ${...} 为你对应源码目录和发布目录
uglifyjs -o ${pluginContext.outputDir}/js/main.min.js ${pluginContext.outputDir}/js/main.min.js --toplevel -c

其他小问题

微信开发者工具经常黑屏

解决方案:关掉微信开发者工具的文件变动时自动编译可以缓解

第三方模块

笔者是在源码目录新建一个 3rd 的目录放置第三方模块



下一篇笔者将继续分享游戏代码层面遇到的问题和解决方案。
FuChee wechat
扫一扫,关注我