基于Egret的大型项目转微信小游戏开发总结——各种坑处理


本文链接: http://51meaning.cn/blog/?p=412   转载请注明转载自:我要MEANING

小游戏这阵在做基于Egret的微信小游戏,项目很大,也踩了很多坑,特总结如下,供各位难兄难弟参考!

1、window.Main=Main;换成window.Main=项目实际入口Main[带包类路径];

2、目前白鹭小游戏支持解析 xml 了吗,该如何使用?
答:
升级小游戏支持库到1.0.12版本
在微信开发者工具的 Console 控制台输入egret.wxgame.version应输出1.0.12
访问http://developer.egret.com/cn/github/egret-docs/Engine2D/minigame/minigameFAQ/xmldom.zip下载小游戏xml支持库
解压支持库,并拷贝到微信小游戏目录
打开小游戏目录中 game.js
在 egret.runEgret 之前加入代码:window.DOMParser = require("./xmldom/xmldom.js").DOMParser;

3、所有自定义eui.skin修改
创建exml文件

<?xml version="1.0" encoding="utf-8"?>
<e:Skin class="gameskin.XXXBtn" states="up,down" xmlns:e="http://ns.egret.com/eui">
<e:Image width="320" height="152" source="xxx_png" source.down="xxx_png" />
</e:Skin>

引用
this.skinName = "gameskin.XXXBtn;// exml文件class name

4、JSON.parse时加上item != ""的判断,即

if(item != undefined && item != null && item != ""){
    obj = JSON.parse(item);
}

5、egret.localStorage.getItem(key);时,key要是字符串,如果传入的是string类型的数字,也可能在存取时出问题,如果出问题,改成egret.localStorage.getItem(“”+key);即可。

6、改用AssetsManager,不支持RES.getVersionController().getVirtualUrl(url),要把RES.getRes(RES.getVersionController().getVirtualUrl(url));换成RES.getRes(url);

7、图片尺寸长宽都不能超过2048。

8、设置分包加载、资源缓存,参考如下链接
分包加载: http://developer.egret.com/cn/github/egret-docs/Engine2D/minigame/package/index.html
资源缓存: http://developer.egret.com/cn/github/egret-docs/Engine2D/minigame/usingcache/index.html

9、修改EgretSubPackageLoading.js,可以添加启动页面/背景。

10、main.min.js过大时,可以进行分割。
js脚本如下,文件路径根据自己情况进行修改(标红处)

var path = require("path");

var fs = require("fs");

var referenceRegExp = /\<(?!\s*\/)\s*([\w:$]*)/g;//组件类名匹配

var nameSpaceRegExp = /namespace\s+([a-zA-Z0-9$_]*)/g;//代码中命名空间匹配组

var classRegExp = /(class|enum)\s+([a-zA-Z0-9$_]*)(?=(\s+extends)|(\s+implements)|(\s*{)|(<[^>]*>))/g;//代码中类名匹配组

var referenceMap = {};//类名到全限定类名映射

var needAddToGlobalNameSpaces = [];//需要添加到window上的命名空间

var needAddGlobalReferenceClassList = [];//需要添加都window上的类名

var componentReferenceClassList = [];//自定义组件的类名

//TODO — 注意换成你自己的配置啦……

var splitConfig = {

    codeMinifyPath: path.resolve("stage0/main.min.js"),

    firstPartCodeFileName: "main_part1.min.js",

    secondPartCodeFileName: "main_part2.min.js",

    firstPartCodeFilePath: "stage1",

    secondPartCodeFilePath: "stage2",

    splitSepStr: "__reflect(DebugPlatform.prototype,\"DebugPlatform\",[\"Platform\"]),window.platform||(window.platform=new DebugPlatform);"

};

generateClassReferenceCode();

/**

 * 找出所有exml皮肤文件中用到的自定义的组件

 * 生成在window对象上的引用代码

 * @returns {string}

 */

function generateClassReferenceCode() {

    let codeContent = fs.readFileSync(splitConfig.codeMinifyPath, "utf-8");

    let index = codeContent.indexOf(splitConfig.splitSepStr);

    if (index === -1) {

        console.warn(`在代码中未找到分割字符串${splitConfig.splitSepStr}`);

        return;

    }

    let codeContent1 = codeContent.substring(0, index + splitConfig.splitSepStr.length);

    let codeContent2 = codeContent.substr(index + splitConfig.splitSepStr.length);

    let testRegExp = /__reflect\s*\(\s*[^,]+,\s*”([^\"]*)"/g;

    //处理part1

    needAddGlobalReferenceClassList.length = 0;

    needAddToGlobalNameSpaces.length = 0;

    referenceMap = {};

    let mathArray = testRegExp.exec(codeContent1);

    let className;

    let nameSpaceName;

    while (mathArray) {

        let fullClassName = mathArray[1];

        if (fullClassName.indexOf(".") !== -1) {

            nameSpaceName = fullClassName.split(".")[0];

            className = fullClassName.split(".")[1];

            referenceMap[className] = fullClassName;

            if (needAddToGlobalNameSpaces.indexOf(nameSpaceName) === -1) {

                needAddToGlobalNameSpaces.push(nameSpaceName);

            }

            if (needAddGlobalReferenceClassList.indexOf(className) === -1) {

                needAddGlobalReferenceClassList.push(className);

            } else {

                console.log(`代码中存在两个相同的类名${className},即使在同样的命名空间下也必行!`);

            }

        } else {

            needAddGlobalReferenceClassList.push(fullClassName);

            referenceMap[fullClassName] = fullClassName;

        }

        mathArray = testRegExp.exec(codeContent1);

    }

    let referenceCodeStr = ";window.egret=egret;";

    needAddToGlobalNameSpaces.forEach((nameSpace) => {

        referenceCodeStr += "window[\"" + nameSpace + "\"]=" + nameSpace + ";"

    });

    needAddGlobalReferenceClassList.forEach((className) => {

        referenceCodeStr += "window[\"" + className + "\"]=” + referenceMap[className] + ";"

    });

    codeContent1 += referenceCodeStr;

    //处理part2

    let firstPartNameSpaces = needAddToGlobalNameSpaces.concat();

    let firstPartClassReference = needAddGlobalReferenceClassList.concat();

    needAddGlobalReferenceClassList.length = 0;

    needAddToGlobalNameSpaces.length = 0;

    referenceMap = {};

    mathArray = testRegExp.exec(codeContent2);

    while (mathArray) {

        let fullClassName = mathArray[1];

        if (fullClassName.indexOf(".") !== -1) {

            nameSpaceName = fullClassName.split(".")[0];

            className = fullClassName.split(".")[1];

            referenceMap[className] = fullClassName;

            if (needAddToGlobalNameSpaces.indexOf(nameSpaceName) === -1) {

                needAddToGlobalNameSpaces.push(nameSpaceName);

            }

            if (needAddGlobalReferenceClassList.indexOf(className) === -1) {

                needAddGlobalReferenceClassList.push(className);

            } else {

                console.log(`代码中存在两个相同的类名${className},即使在同样的命名空间下也必行!`);

            }

        } else {

            needAddGlobalReferenceClassList.push(fullClassName);

            referenceMap[fullClassName] = fullClassName;

        }

        mathArray = testRegExp.exec(codeContent2);

    }

    referenceCodeStr = ";window.egret=egret;";

    needAddToGlobalNameSpaces.forEach((nameSpace) => {

        referenceCodeStr += "window[\"" + nameSpace + "\"]=" + nameSpace + ";"

    });

    needAddGlobalReferenceClassList.forEach((className) => {

        referenceCodeStr += "window[\"" + className + "\"]=” + referenceMap[className] + ";"

    });

    //part2中必须引入part1中的所有命名空间以及类名定义,否则part2运行时一堆找不到

    let part1CodeDeclare = "";

    if (firstPartNameSpaces.length) {

        firstPartNameSpaces.forEach(ns => {

            part1CodeDeclare += "var " + ns + "=window[\"" + ns + "\"];";

        })

    }

    if (firstPartClassReference) {

        firstPartClassReference.forEach(classzz => {

            part1CodeDeclare += "var " + classzz + "=window[\"" + classzz + "\"];";

        })

    }

    codeContent2 = "var __reflect=this&&this.__reflect||function(t,e,i){t.__class__=e,i?i.push(e):i=[e],t.__types__=t.__types__?i.concat(t.__types__):i},__extends=this&&this.__extends||function(t,e){function i(){this.constructor=t}for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);i.prototype=e.prototype,t.prototype=new i};" + part1CodeDeclare + codeContent2 + referenceCodeStr;

    fs.writeFileSync(path.join(splitConfig.firstPartCodeFilePath, splitConfig.firstPartCodeFileName), codeContent1, "utf-8");

    fs.writeFileSync(path.join(splitConfig.secondPartCodeFilePath, splitConfig.secondPartCodeFileName), codeContent2, "utf-8");

}

最后,使用cmd执行命令:node 脚本名.js,

11、渲染模式,egret在发布时不会自动生成,这点确实让人有些郁闷,为此查了好一阵游戏卡的问题。需手动在game.js下设置为renderMode: "webgl", 请写在—-auto option end—-后面,这样不会每次编译都被删除掉。

12、需手动指定liberary下needCache方法的缓存文件,否则不缓存。

13、声音播放有回音,可以修改egret.wxgame.js  HtmlSoundChannel注释 audio.volume = 0;(高版本问题可能修复了)

14、短声音播放不出来的处理:将声音资源放在小游戏项目下,使用wx.createInnerAudioContext()来播放。(高版本问题可能修复了)

this.audio = wx.createInnerAudioContext();
this.audio.src = "xxx.mp3";
this.audio.stop();
this.audio.play();

15、支付问题处理:
access_token missing hint 41001错误
仔细看文档发现要把access_token放到url里 https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN而不是放到post参数里。官方的这个文档有点迷,说实话。

16、屏幕适配问题,伪代码如下

 let displayWidth = this.stage.stageWidth;// 屏幕宽
 let displayHeight = this.stage.stageHeight;// 屏幕高
 let stageHeight = displayHeight;// 实际高
 let stageWidth = Math.round(游戏默认宽* displayHeight /游戏默认高);// 按比例缩放,得到实际宽度
 if(stageWidth > displayWidth) {
        stageWidth = displayWidth;
  }
 let scaleX = stageWidth / 游戏默认宽;// 新宽度/游戏默认宽
 let scaleY = stageHeight / 游戏默认高;// 新高度/游戏默认高度
 this.scaleY = scaleY;//关键点
 this.scaleX = scaleX; //关键点
 this.x = (displayWidth – stageWidth) / 2;// 居中对齐

17、处理刘海屏手机显示
升级微信小游戏基础库版本到2.7.0或以上。
使用wx.getSystemInfo(Object object)获取safeArea. top,如果大于0,基本判断是刘海屏,可以选择将游戏整体ui下移。

18、5.2.27版本,小游戏socket不好用,可以在egret.wxgame.js中尝试注释掉WXSocket相关代码。
QQ20190915-111549@2x

19、审核注意事项
1)对于玩家允许玩家自定义输入的内容:自定义昵称、聊天室、个人资料签名、个人空间描述等场景,需调用微信公众平台内容安全API(imgSecCheck、msgSecCheck)验证输入。对于检测结果有害的内容,请及时清理或拦截,确保小游戏提供内容及用户产生内容符合相关法律法规的要求。
2)务必在微信后台配置request、socket、uploadFille、downloadFile域名,并且每个域名都支持https访问。检验方法:小游戏在非开启Debug调试情况下正常打开并运行。

3+
avatar