[LayaAirIDE 1.0]微信小游戏渲染开放域卡顿处理

注:
引擎版本:1.7.22(稍微低点的也是可以的,这个没测过。。)
2.0版本ui有开放域的控件,由于没推广,个人没用过,性能不知道如何,如果用过的人觉得官方的已经很好了,请无视该文档。。
代码示例在第四点,懒得看我啰嗦的文字的可直接跳到此处看示例。。

一、绘制开放域界面:
绘制界面基本都是大同小异,就是将sharedCanvas转成纹理再绘制到graphics上,这里推荐看下http://ask.layabox.com/question/15195这个文档,写的还算全面(虽说我不用,题外话)

二、如何有效的控制开放域界面的开关:
官方推荐的就是对上述的纹理设置其属性:.bitmap.alwaysChange = true;(可以将该属性理解为是否需要持续刷新纹理内容)
但是会出现一个问题——在安卓机上特卡,然后就有人说了需要定时将它设为false,但这不能根本解决问题。对于那种一次性的界面如“排行榜”等这样处理是可以的,且首次打开掉帧很严重没有根本解决;对于频繁刷新的界面如“下位即将超越的好友”等界面时,这种处理就不科学了,频繁的修改alwaysChange无法做到实时的刷新开放域界面,而且卡顿问题特严重。

三、解决方案:
1.从上述可以大致得出解决的方案:(1)减少开放域界面的渲染频率(重点);(2)一次性界面定时关闭渲染。

2.减少开放域界面的渲染频率:
(1)启动定时器,每隔5帧刷新开放域界面——时间单位5帧是个人认为的一个合理数值,对于频繁刷新的界面再不济2个时间单位即10帧(0.1~0.2秒之间)必会显示出界面,这个时间间隔是可以接受的(因异步加载图片导致界面出来较慢不属于该范畴),在一般的安卓机频繁刷新的界面的帧数保持在40帧到50帧之间,这个帧数是可以接受的。
注:对于开放域有动画的界面(当然一般不会把动画放在开放域。。卡不死你),因为开放域界面5帧刷一次,相当于开放域的实际帧数只有12=60/5帧,对动画来说,看起来就不会很流畅了,因此可以稍微缩小时间单位。

(2)刷新开放域界面方法,即定时器的回调方法(重点):
function onTimer() {
    // bitmap为纹理的bitmap属性,自行赋值
    var func = bitmap._source && bitmap.reloadCanvasData;
    func && func.call(bitmap);
}

3.关闭渲染:
个人认为5秒后停止定时器即可,因为使用了定时器去减缓渲染频率,因此这关闭就显得不是很重要,但是能尽量提高帧数还是要做的。这处理很简单,就是将定时器关了即可,不示范代码了。

四、代码示例(个人封装的开放域控件,TS版的,看不懂要JS版另外跟我说)

注:pfUtils是封装微信操作的工具类,这里我将下面用到的方法另外写出来,可自行替换成你们自己封装的方法。
// 开放域转纹理,这个没啥好说的,一般都是这么处理的
pfUtils.getShareCanvas = function() {
    return new Laya.Texture(sharedCanvas/*window.sharedCanvas*/);
};
// 通知开放域数据:我这里采用的是“字符串标志+另外数据”来说明该指令是什么操作以及操作的参数,下面用到两个标识:enter进入或刷新开放域界面,携带界面标识viewName和宽高;exit退出开放域界面,携带界面标识viewName,开放域的界面创建、刷新和退出因人而异,可自行修改成你们自己想要的格式
pfUtils.postMessage = function(action: string, data?: any) {
    var msg = {action, data};
    wx.postMessage(msg);
};

// --------正式代码开始--------
module lie {

    /**
     * 微信数据开放域控件
     * 使用:设置好控件大小、坐标及viewName即可,其它的无需管理(可在ui里拖入并设置好)
     * 注:该控件跟开放域的代码是配套的,不同开放域代码请勿使用该类
     */
    export class WXBitmap extends Laya.Sprite {

        private $viewName: string;
        private $texture: Laya.Texture;
        private $bitmap: any;
        private $timer: lie.Timer;

        public auto: boolean = true;    // 仅在未加入场景前修改有效,兼容ui

        constructor() {
            super();
            this.once(Laya.Event.DISPLAY, this, this.onCreate);
            this.once(Laya.Event.UNDISPLAY, this, this.onDestroy);
        }

        /**
         * 加入场景
         */
        protected onCreate(): void {
            var self = this;
            // 纹理
            var texture = self.$texture = pfUtils.getShareCanvas();
            var bitmap = self.$bitmap = texture.bitmap;
            self.graphics.drawTexture(texture);
            self.visible = false;
            self.$timer = new Timer(self.reloadCanvas, self, 5, false, true);
            self.auto && self.refresh();
        }

        /**
         * 离开场景
         */
        protected onDestroy(): void {
            var self = this;
            var post = pfUtils.postMessage;
            self.clear();
            post('exit', { view: self.$viewName });
            // post('pause');
        }

        /**
         * 设置界面名称
         */
        public set viewName(value: string) {
            this.$viewName = value;
        }

        /**
         * 刷新开放域界面
         * @param param 携带参数通知开放域
         */
        public refresh(param?: any): void {
            var self = this;
            var width = self.width;
            var height = self.height;
            var data = {
                view: self.$viewName,
                width: width,
                height: height
            };
            // 赋值属性
            for (let i in param)
                data[i] = param[i];
            self.visible = true;
            // 检测
            var texture = self.$texture;
            if (texture) {
                let timer = self.$timer;
                // 通知+延迟刷新界面
                let post = pfUtils.postMessage;
                // post('resume');
                post('enter', data);
                // 5秒后停止绘画
                self.clearTimeout();
                self.reloadCanvas();    // 先刷新一次
                timer.start();
                Laya.timer.once(5000, timer, timer.stop);
            }
        }

        /**
         * 重新渲染cavans界面
         */
        protected reloadCanvas(): void {
            var bitmap = this.$bitmap;
            var func = bitmap._source && bitmap.reloadCanvasData;
            func && func.call(bitmap);
        }

        /**
         * 清除延迟
         */
        protected clearTimeout(): void {
            var self = this;
            var timer = self.$timer;
            Laya.timer.clear(timer, timer.stop);
        }

        /**
         * 清除界面
         */
        protected clear(): void {
            var self = this;
            var texture = self.$texture;
            var timer = self.$timer;
            if (timer) {
                timer.clear();
                self.$timer = null;
            }
            if (texture) {
                texture.destroy(true);
                self.$texture = null;
                self.$bitmap = null;
            }
            self.clearTimeout();
            self.graphics.clear();
        }
    }
}

// 由于官方的定时器用起来不是很舒服,自己封装的定时器,不想用可直接替换成Laya.timer.frameLoop来替代,请自行修改跟Timer相关代码。
module lie {

    /**
     * 定时器,可启动、停止及统计已运行时间(单位毫秒),默认启动
     */
    export class Timer {

        private $call: Function;
        private $thisObj: any;

        private $running: boolean;
        private $runTime: number = 0;   // 已运行时间
        private $lastTime: number;      // 上一次时间

        /**
         * 默认状态就是创建一个每一帧刷新一次的计时器
         * @param call 回调方法
         * @param thisObj 回调对象
         * @param delay 延迟,默认1,isTime为true时表示毫秒,否则表示帧数
         * @param isTime 是否时间回调,默认false(时间回调、帧回调)
         * @param isStop 是否不需要直接运行
         */
        public constructor(call: Function, thisObj?: any, delay: number = 1, isTime?: boolean, isStop?: boolean) {
            this.$call = call;
            this.$thisObj = thisObj;
            isStop || this.start();
            Laya.timer[isTime ? 'loop' : 'frameLoop'](delay, this, this.update);
        }

        /**
         * 回调
         */
        protected update(): void {
            var self = this;
            if (self.$running) {
                self.$call.call(self.$thisObj);
            }
        }

        /**
         * 开始计时
         */
        public start(): void {
            var self = this;
            if (!self.$running) {
                self.$lastTime = Date.now();
                self.$running = true;
            }
        }

        /**
         * 停止计时
         */
        public stop(): void {
            var self = this;
            if (self.$running) {
                let nowT = Date.now();
                self.$runTime += nowT - self.$lastTime;
                self.$lastTime = nowT;
                self.$running = false;
            }
        }

        /**
         * 获取是否运行中
         */
        public get running(): boolean {
            return this.$running;
        }

        /**
         * 获取运行的时间
         */
        public get runTime(): number {
            var self = this;
            return self.$runTime + (self.running ?
                Date.now() - self.$lastTime : 0);
        }

        /**
         * 重置时间,归0
         */
        public reset(): void {
            var self = this;
            self.$runTime = 0;
            self.$lastTime = Date.now();
        }

        /**
         * 清除定时器,一经清除,将不可再用
         */
        public clear(): void {
            var self = this;
            Laya.timer.clear(self, self.update);
            self.$call = self.$thisObj = null;
        }
    }
}
已邀请:

Aar0n

赞同来自:

感谢分享!

zhangdiansong

赞同来自:

感谢分享,试过了,很有用

要回复问题请先

商务合作
商务合作