Cocos Creator入门(二)之技巧与细节

一、微信相关功能扩展与说明

  1.  微信授权:最近项目中遇到一个需求,那就是需要获取到玩家的头像昵称,然后上传给服务器,留作以后排行榜和玩家详情时显示,需要当游戏进行到某一个环节,比如新手引导结束或者进入大厅时,玩家点击某个地方弹出授权弹窗,于是便有了如下创建全屏透明微信授权button的代码。
    // 创建全屏透明文本授权按钮
    let systemInfo = window['wx'].getSystemInfoSync();
    let wxButton = window['wx'].createUserInfoButton({
                type: 'text',
                text: '',
                style: {
                    left: 0,
                    top: 0,
                    width: systemInfo.screenWidth,
                    height: systemInfo.screenHeight,
                    lineHeight: 40,
                    textAlign: 'center',
                }
            });
            wxButton.onTap((res) => {
                if (res.errMsg.indexOf('auth deny') > -1 || res.errMsg.indexOf('auth denied') > -1) {
                    cc.log('用户拒绝授权');
                    wxButton.destroy();
                } else {
                    cc.log('授权成功:', JSON.stringify(res));
                    wxButton.destroy();
                    // setUserData
                    let player: Player = Game.getInstance().player;
                    player.setNick(res.userInfo.nickName);
                    player.setAvatar(res.userInfo.avatarUrl);
                    player.setGender(res.userInfo.gender);
                    cc.log('player.attr:', JSON.stringify(player.attr));
                    // this.sendSetPlayerInfoReq();
                    // Game.getInstance().gNode.emit(EventConfig.EVT_WX_REFRESH_PLAYER_INFO);
                }
            });

     

  2. 微信头像加载细节:通过授权获取到的微信头像url是不包含格式后缀的,如果不声明加载文件类型就达不到预期的目的,所以在加载的时候需要添加文件的类型。

    cc.loader.load({url: avatarUrl, type: 'png'}, (err, texture) => {
        if (err) {
            return cc.log(err);
        }
        let sp: cc.SpriteFrame = new cc.SpriteFrame(texture);
        this.imgHead.getComponent(cc.Sprite).spriteFrame = sp;
    });

     

  3. 代码动态改变图片精灵为九宫格model: 话不多说,直接上代码。

    let Sprite = new cc.Sprite();
    let sp = Sprite.spriteFrame;
    Sprite.type = cc.Sprite.Type.SLICED;
    // 纹理的四个边距
    sp.insetBottom = 0;
    sp.insetTop = 0;
    sp.insetLeft = 0;
    sp.insetRight = 0;

     

  4. 微信加载子包代码:由于现在我参与的项目偏重度,资源以及代码量比较巨大,所以需要用到分包加载,分包加载的详细流程官方文档里面也说得比较详细,我这里就放一部分代码,仅供参考。

    cc.loader.downloader.loadSubpackage('sub-package', function (err) {
        if (err) {
            return console.error(err);
        }
        console.log('load subpackage successfully.');
        wxDownloader.init();
        window.boot();
    });

     

  5. IOS微信小游戏,切后台然后重回游戏,会存在背景音乐不恢复播放的问题,解决方法:监听音乐中断事件,然后手动重新播放。

    window['wx'].onAudioInterruptionEnd(() => {
        Util.showToast('onAudioInterruptionEnd');
        // do something to resume music
      Game.getInstance().eventDispatcher.dispatchEvent(EventConfig.EVT_WX_AUDIO_INTERUPTION_END);
    });

     

  6. 昵称截取:项目经常会遇到玩家昵称过长,如果全部显示的话,可能会让整个UI布局变得不美观,那么就会出现截取固定像素长度的昵称这个需求,如果只是简单的通过截取字符串的长度,那么那些昵称中包含数字和英文的玩家名字就会很短,那些昵称中包含微信表情的玩家昵称就会显示过长,那么截取最美观的方法就是按像素截取,通过判断字符的Unicode来区分数字、字母与汉字,一个汉字相当于2个字符的长度算,,截取昵称工具函数转载自麒麟子代码如下。

    /**
         * 
         * @param str      需要截取的字符串
         * @param maxChars 保留最大汉字长度
         * @param suffix   添加的后缀 注意,如果后缀不为Boolean(suffix) => false ,则要占用一个汉字的位置
         */
        static strClamp(str: string, maxChars: number, suffix?: any): string {
            let toCodePoint: Function = (unicodeSurrogates: string): any[] => {
                let result: any[] = [];
                let c: number = 0;
                let p: number = 0;
                let i: number = 0;
                while (i < unicodeSurrogates.length) {
                    let pos: number = i;
                    // 返回位置的字符的 Unicode 编码
                    c = unicodeSurrogates.charCodeAt(i++);
                    if (c === 0xfe0f) {
                        continue;
                    }
    
                    if (p) {
                        // 计算4字节的unicode
                        let value: number = (0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00));
                        result.push({
                            v: value,
                            pos: pos,
                        });
                        p = 0;
                    } else if (0xD800 <= c && c <= 0xDBFF) {
                        // 如果unicode编码在oxD800-0xDBff之间,则需要与后一个字符放在一起
                        p = c;
                    } else {
                        // 如果是2字节,直接将码点转为对应的十六进制形式
                        result.push({
                            v: c,
                            pos: pos
                        });
                    }
                }
                return result;
            }
            
            // 汉字算两个长度
            maxChars <<= 1;
    
            let codeArr: any[] = toCodePoint(str);
            let numChar: number = 0;
            let index: number = 0;
            for (let i = 0; i < codeArr.length; ++i) {
                let code: number = codeArr[i].v;
                let add: number = 1;
                if (code >= 128) {
                    add = 2;
                }
    
                //如果超过了限制,则按上一个为准
                if (numChar + add > maxChars) {
                    break;
                }
    
                index = i;
                //累加
                numChar += add;
            }
    
            if (codeArr.length - 1 === index) {
                return str;
            }
    
            let more: number = suffix ? 1 : 0;
            suffix = suffix ? suffix : '...';
            
            return str.substring(0, codeArr[index - more].pos + 1) + suffix;
        }

     

 二、优化的一些细节

  1. 分帧加载:当某个界面打开的时候一瞬间需要创建很多预制体的话,可以考虑开启多线程加载预制体,防止主线程被卡死,影响体验,具体实现代码如下,当然也可以使用ES6语法Generator 函数实现异步分帧加载。
    for (let i = 0; i < length; i++) {
        this.scheduleOnce(() =>{
            let item = cc.instantiate(this.prefab);
            // do init
            // 处理初始化完毕后的逻辑
            if (i === length -1) {
                // do something
            }
        }, 0.01);
    }

     

  2. 动态修改定时器的Interval:在实际项目中会遇到很多需求,比如一个发射炮弹的enemy,会随着游戏时间或者分数的增长需要越射越快,这时候如果用schedule实现这个功能的话,那么该如何去动态改变定时器的间隔呢?通过查看Cocos Creator schedule的源码发现,如果当前回调函数已经绑定过定时器,那么你再次调用schedule时不会设置新的定时器,而是改变之前定时器的时间间隔。(注意:如果定时器内的回调函数运行时报错,则会一直在定时器这里无限循环)

    if (timer && callback === timer._callback) {
        cc.log(1507, timer.getInterval(), interval)
        timer._interval = interval;
        return;
    }

     

资源的动态加载与释放

    

 

 

 

不积跬步无以至千里,通过项目实战学到的一些新东西和一些知识遗漏的地方,记录下来不仅能让自己以后再看的时候温故而知新, 也能让遇到类似问题的同学有一个新思路,菜是原罪,不断学习,才能变强~~~~共勉。

你可能感兴趣的:(原创,开发笔记,每日总结)