评论

cocos微信小游戏启动首屏优化

cocos微信小游戏启动首屏优化


一、基本信息
引擎版本cocos creator 2.3.3

二、参考文章
1、最佳实践: https://developers.weixin.qq.com/minigame/dev/guide/performance/perf-action-start2.html
2、Cocos Creator 微信小游戏平台启动与包体优化(首屏渲染耗时降低 50%): https://forum.cocos.org/t/cocos-creator-50/94999

三、优化结果对比
3.1 优化前加载速度: 8605ms

3.2优化后加载速度:4366ms

四、优化前资料准备
根据微信官方文档启动优化最佳实践图示:

优化启动速度主要有6点:
1.精简包内资源: 本项目已优化过
2.分包加载: 可以尝试,具体步骤见5.2
3.引擎插件能力: 可以尝试,打包时选择允许引擎分离即
4.预下载能力: 仍处于内测中
5.降低首屏渲染所需资源数目: 可以尝试,具体步骤见5.1
6.尽快渲染: 可以尝试,具体步骤见5.3

五、优化实践
有了思路之后根据复杂度由简入繁按照如下步骤开始优化

5.1 降低首屏渲染所需资源数目
启动页只放一张背景图,除了跳转至加载页不做其他处理,减少启动页的资源加载和逻辑处理

cc.Class({
    extends: cc.Component,
    properties: {
        
    },
    // LIFE-CYCLE CALLBACKS:

    // onLoad () {},

    start () {
        cc.director.loadScene("LoginScene");
    },

    // update (dt) {},
});

修改完后加载速度如图

5.2 分包加载

除了启动页以外,其他所有代码都放到子包中加载(官方文档),启动页代码稍作修改

cc.Class({
    extends: cc.Component,
    properties: {
        
    },

    // LIFE-CYCLE CALLBACKS:

    // onLoad () {},

    start () {
        cc.loader.downloader.loadSubpackage('subpackage', function (err) {
            if (err) {
                return console.error(err);
            }
            cc.director.loadScene("LoginScene");
        });
        
    },

    // update (dt) {},
});

修改完后:

5.3 尽快渲染-引擎加载前自绘制启动页

根据参考文档(https://forum.cocos.org/t/cocos-creator-50/94999)进行操作
1.自定义微信发布模板(定制项目构建流程),将首屏渲染代码及图片放到发布模板中,按照文档操作,项目根目录想创建目录build-templates,然后创建wechatgame目录

2.在 wechatgame 目录下新建 first-screen.js 脚本,拷贝以下内容:


// Vertex shader program
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +
    'attribute vec2 a_TexCoord;\n' +
    'varying vec2 v_TexCoord;\n' +
    'void main() {\n' +
    '  gl_Position = a_Position;\n' +
    '  v_TexCoord = a_TexCoord;\n' +
    '}\n';

// Fragment shader program
var FSHADER_SOURCE =
    '#ifdef GL_ES\n' +
    'precision mediump float;\n' +
    '#endif\n' +
    'uniform sampler2D u_Sampler;\n' +
    'varying vec2 v_TexCoord;\n' +
    'void main() {\n' +
    '  gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
    '}\n';

/**
 * Create a program object and make current
 * @param gl GL context
 * @param vshader a vertex shader program (string)
 * @param fshader a fragment shader program (string)
 * @return true, if the program object was created and successfully made current 
 */
function initShaders(gl, vshader, fshader) {
    var program = createProgram(gl, vshader, fshader);
    if (!program) {
        console.log('Failed to create program');
        return false;
    }

    gl.useProgram(program);
    gl.program = program;

    return true;
}

/**
 * Create the linked program object
 * @param gl GL context
 * @param vshader a vertex shader program (string)
 * @param fshader a fragment shader program (string)
 * @return created program object, or null if the creation has failed
 */
function createProgram(gl, vshader, fshader) {
    // Create shader object
    var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
    var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
    if (!vertexShader || !fragmentShader) {
        return null;
    }

    // Create a program object
    var program = gl.createProgram();
    if (!program) {
        return null;
    }

    // Attach the shader objects
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);

    // Link the program object
    gl.linkProgram(program);

    // Check the result of linking
    var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!linked) {
        var error = gl.getProgramInfoLog(program);
        console.log('Failed to link program: ' + error);
        gl.deleteProgram(program);
        gl.deleteShader(fragmentShader);
        gl.deleteShader(vertexShader);
        return null;
    }
    return program;
}

/**
 * Create a shader object
 * @param gl GL context
 * @param type the type of the shader object to be created
 * @param source shader program (string)
 * @return created shader object, or null if the creation has failed.
 */
function loadShader(gl, type, source) {
    // Create shader object
    var shader = gl.createShader(type);
    if (shader == null) {
        console.log('unable to create shader');
        return null;
    }

    // Set the shader program
    gl.shaderSource(shader, source);

    // Compile the shader
    gl.compileShader(shader);

    // Check the result of compilation
    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
        var error = gl.getShaderInfoLog(shader);
        console.log('Failed to compile shader: ' + error);
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}

function initVertexBuffers(gl, vertices) {
    var verticesTexCoords = vertices || new Float32Array([
        // Vertex coordinates, texture coordinate
        -1, 1, 0.0, 1.0,
        -1, -1, 0.0, 0.0,
        1, 1, 1.0, 1.0,
        1, -1, 1.0, 0.0,
    ]);

    var n = 4; // The number of vertices

    // Create the buffer object
    var vertexTexCoordBuffer = gl.createBuffer();
    if (!vertexTexCoordBuffer) {
        console.log('Failed to create the buffer object');
        return -1;
    }

    // Bind the buffer object to target
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

    var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
    //Get the storage location of a_Position, assign and enable buffer
    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    if (a_Position < 0) {
        console.log('Failed to get the storage location of a_Position');
        return -1;
    }
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
    gl.enableVertexAttribArray(a_Position);  // Enable the assignment of the buffer object

    // Get the storage location of a_TexCoord
    var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
    if (a_TexCoord < 0) {
        console.log('Failed to get the storage location of a_TexCoord');
        return -1;
    }
    // Assign the buffer object to a_TexCoord variable
    gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
    gl.enableVertexAttribArray(a_TexCoord);  // Enable the assignment of the buffer object

    return n;
}

function initTextures(gl, n, imgPath) {
    var texture = gl.createTexture();   // Create a texture object
    if (!texture) {
        console.log('Failed to create the texture object');
        return false;
    }

    // Get the storage location of u_Sampler
    var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
    if (!u_Sampler) {
        console.log('Failed to get the storage location of u_Sampler');
        return false;
    }
    var image = wx.createImage();  // Create the image object
    if (!image) {
        console.log('Failed to create the image object');
        return false;
    }
    // Register the event handler to be called on loading an image
    image.onload = function () { loadTexture(gl, n, texture, u_Sampler, image); };
    // Tell the browser to load an image
    image.src = imgPath;
    return true;
}

function loadTexture(gl, n, texture, u_Sampler, image) {
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // Flip the image's y axis
    // Enable texture unit0
    gl.activeTexture(gl.TEXTURE0);
    // Bind the texture object to the target
    gl.bindTexture(gl.TEXTURE_2D, texture);

    // Set the texture parameters
    // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

    // Set the texture image
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);

    // Set the texture unit 0 to the sampler
    gl.uniform1i(u_Sampler, 0);

    gl.clear(gl.COLOR_BUFFER_BIT);   // Clear <canvas>

    gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); // Draw the rectangle
}

function drawImg(imgPath) {
    const vertices = new Float32Array([
        -1, 1, 0.0, 1.0,
        -1, -1, 0.0, 0.0,
        1, 1, 1.0, 1.0,
        1, -1, 1.0, 0.0,
    ]);
    // Retrieve <canvas> element
    // var canvas = document.getElementById('webgl');

    // Get the rendering context for WebGL
    // var gl = getWebGLContext(canvas);
    const gl = wx.__first__canvas.getContext("webgl");
    if (!gl) {
        console.log('Failed to get the rendering context for WebGL');
        return;
    }

    // Initialize shaders
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.log('Failed to intialize shaders.');
        return;
    }

    // Set the vertex information
    var n = initVertexBuffers(gl, vertices);
    if (n < 0) {
        console.log('Failed to set the vertex information');
        return;
    }

    // Specify the color for clearing <canvas>
    gl.clearColor(1.0, 1.0, 1.0, 1.0);

    // Set texture
    if (!initTextures(gl, n, imgPath)) {
        console.log('Failed to intialize the texture.');
        return;
    }

exports.drawImg = drawImg;

3.拷贝首屏图片到同目录下,重命名为 bg.png

4.创建一个脚本 game-backup.js ,拷贝以下内容:
“use strict”;


// 渲染首屏(开发者工具执行有渲染问题,所以开发者工具上不渲染)

wx.__first__canvas = wx.createCanvas();

const data = wx.getSystemInfoSync();

if (data.platform != "devtools") {
    const first_scene = require("./first-screen.js");
    first_scene.drawImg("bg.png");
}
// 加载引擎代码写在这后面

5.打开 Cocos Creator,点击右上角的编辑器进入编辑器安装目录
6.找到 builtin 目录,再依次进入adapters/platforms/wechat/wrapper/builtin 目录,打开 Canvas.js 文件入修改如下代码:

// 修改前
export default function Canvas() {
  const canvas = wx.createCanvas()

// 修改后
let first = true
export default function Canvas() {

8.先试一下能否正常运行,可以的话把 game.js 删除,用 game-backup.js 替换 game.js,game.js在替换的时候注意远程服务器地址配置(remoteDownloader.REMOTE_SERVER_ROOT = “”)需要根据实际情况修改

至此完成优化。

最后一次编辑于  2021-05-18  
点赞 3
收藏
评论

1 个评论

  • 猫先生
    猫先生
    2021-05-18

    赞,学习学习

    2021-05-18
    赞同
    回复
登录 后发表内容