小游戏首屏启动优化
一、优化启动的意义
衡量一个游戏好坏的一个很重要的标准就是留存,而启动时间直接决定了第一波玩家的流失率。当用户打开游戏,满怀期待的等待游戏开始。最好的情况是游戏在1-2秒内给与反馈,或者能让用户进行下一步操作。一般首次打开,由于首包需要从服务器下载,都会有一个等待过程。在这个等待的过程中,用户的忍耐度是慢慢降低的。如果游戏在2-5秒之后才进入可用的状态,首屏留存就会受到影响。最后如果游戏超过5秒甚至更久才显示首屏,这时用户的耐心可能完全消失,有一部分用户可能会退出重新进入,但更多的用户会放弃使用。
根据小游戏整体启动留存率分析,Android玩家的首屏打开留存率约为85%。这是什么意思呢,就是玩家从点击小游戏到能看到首屏的渲染界面大约有15%的玩家流失。对于首次玩某款小游戏的玩家,由于本地没有版本缓存,留存率会明显低很多。据统计,仅代码包加载阶段新玩家流失率就达到20%。(以上数据来源微信小游戏性能优化指南)
二、晒数据
以下数据来自我们游戏优化前后的数据对比
[图片]
三、启动性能优化
小游戏启动加载时序
以下是官方给出的启动时序图和优化建议
[图片]
[图片]
1.首包优化上面,我们可以完全按照官方的建议,尽量减少首包的大小,由于我们对引擎有定制,所以暂未使用引擎插件能力,我们首包中只存放了引擎及基础的启动代码,大小为1.5M。如果使用引擎插件功能,这个大小可以缩减到300K。
2.我们在首包中仅放入了游戏引擎的代码和一些必要的资源。这时候游戏尚不能完整运行,因为游戏的逻辑代码在子包中,需要进一步的加载。但是这时我们要尽快让游戏给出反馈,也就是显示首屏。首屏的内容绘制我们有两种方案:1)依赖游戏引擎绘制 2)不依赖引擎直接绘制。
1)依赖游戏引擎进行绘制
我们利用引擎进行绘制,要做到资源尽量少,能够满足绘制一个启动图和一个进度条就可以了。
a.对于使用CocosCreator制作的游戏:我们可以在游戏启动的时候, 对于第一个场景那里使用动态创建场景的方式,
[图片]
这个动态创建的场景,只使用放在首包里的一些资源。
b.对于使用Laya制作的游戏:我们把原本放在工程代码里的入口代码提取出来,完成Stage的初始化。这样我们就可以做绘制了。
[图片]
2)不依赖引擎直接绘制
在第一种方案中,优化的方向也是尽量减少第一个场景的资源。但是忽略了一个很耗时的过程,引擎初始化。这一步经测试,iOS在100ms以内,安卓在1-2s。如果能把安卓这1-2s的时间优化,想想都兴奋。
为了使首屏等待时间减少到极致,在引擎初始化之前,我们自己来渲染第一帧。我们的游戏使用的Cocos Creator引擎,默认使用WebGL。我们绘制了一个最简单的黑色三角形作为游戏的第一帧。
[代码]//顶点着色器程序
var VSHADER_SOURCE =
"attribute vec4 a_Position;" +
"void main() {" +
//设置坐标
"gl_Position = a_Position; " +
"} ";
//片元着色器var FSHADER_SOURCE =
"void main() {" +
//设置颜色
"gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);" +
"}";//获取canvas元素
GameGlobal.dycc = wx.createCanvas();//获取绘制二维上下文
var gl = dycc.getContext('webgl');
//编译着色器
var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, VSHADER_SOURCE);
gl.compileShader(vertShader);
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, FSHADER_SOURCE);
gl.compileShader(fragShader);
//合并程序
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertShader);
gl.attachShader(shaderProgram, fragShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
//获取坐标点
var a_Position = gl.getAttribLocation(shaderProgram, 'a_Position');
var n = initBuffers(gl,shaderProgram);
if(n<0){
console.log('Failed to set the positions');
}
// 清除指定<画布>的颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 清空 <canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n);
function initBuffers(gl,shaderProgram) {
var vertices = new Float32Array([
0.0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
var n = 3;//点的个数
//创建缓冲区对象
var vertexBuffer = gl.createBuffer();
if(!vertexBuffer){
console.log("Failed to create the butter object");
return -1;
}
//将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);
//向缓冲区写入数据
gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);
//获取坐标点
var a_Position = gl.getAttribLocation(shaderProgram, 'a_Position');
//将缓冲区对象分配给a_Position变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
//连接a_Position变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);
return n;
}
[代码]
有了第一帧的绘制,用户可以很快进入到游戏内,不会长时间的在官方白色的加载进度屏那里等待。我们还需要做一些其他处理,不然玩家看到的将是一个黑屏。后续的处理也有两种方案,一种就是如果你对WebGL比较熟悉,可以自行完成更复杂的绘制。另外一种方案就是巧妙利用官方提供的功能。我们这里详细说下后面一种方案。
为了盖住黑屏,我们需要绘制一张启动图。这时候我们使用官方提供的接口[代码]wx.createUserInfoButton[代码]来创建一个满屏的按钮,按钮的背景图就是我们的启动图。但是这时候如果用户点击屏幕,就会造成提示用户授权,这不是我们想要的。接着我们使用官方的另外一个接口[代码]wx.showLoading[代码],创建一个模态加载弹窗就可以解决这个问题了。有了这样一个首屏,剩下的就是尽快加载子包,开始真正的游戏内容。
四、Demo
希望大家也都能探索一下首屏加载的过程。我们提供了一个简单的demo示例,通过链接即可下载到。打开demo中的index.js文件,里面有操作步骤说明。
laya的例子:
链接: https://pan.baidu.com/s/18RS9HWpmp5l0V3WGQdNvzw
提取码: bdji
cocos的例子:
链接: https://pan.baidu.com/s/1c_UPwdlEFuqprmG-QGMvpQ
提取码: jggt
五、结语
以上就是我们的启动优化方案,欢迎各位游戏开发者交流或提出宝贵的建议,对游戏充满热爱的小伙伴也可以选择加入我们大禹网络。HR邮箱:guyifen@dayukeji.com