Cocos Creator 开发小游戏的实用技巧
2019.04.28 by cocos
教程

作者介绍:

大家好,我是麒麟子, 开源棋牌《幼麟棋牌-四川麻将》(泄漏版叫 《达达麻将》)作者,成都幼麟科技创始人。

自 09 年进入游戏行业以来,不知不觉已经度过了十个春秋。曾经我也血气方刚,曾经我也青春年少。岁月如梭,光阴似箭,如今已是而立之年。退去了青涩懵懂,换来了满身的意气风发。希望在下一个十年,能够用自己所学的经验,浪迹这个充满爱恨情仇的游戏江湖。

麒麟子往期文章:

Part I:「幼麟麻将」全套源码讲解

Part II:服务端和网络通讯知识解析

Part III:伪3D渲染布局知识解析

「幼麟麻将」全套源码上线Cocos扩展商店!

教程介绍:

《麒麟子 Cocos Creator 实用技巧》为麒麟子的随笔记录,主要记录一些麒麟子在使用 Cocos Creator 制作棋牌和微信小游戏的时候,遇到的问题的解决方案。希望能够用自己所学,为遇到困难的同学带来一定的帮助,为 Cocos 社区尽一份绵薄之力,为游戏教育行业注入一丝丝清流。

截止今日,技巧系列共计更新了 5 则,包括:

(1)如何正确地显示微信头像

(2)微信名字截断(支持表情)

(3)微信小游戏中音效中断问题处理

(4)打包原生 App 截图白屏解决方案

(5)技能 CD 效果制作

……

更多实用教程陆续更新中,欢迎感兴趣的开发者关注论坛原贴。

原贴地址:

https://forum.cocos.com/t/cocos-creator/76914


技巧一:如何正确地显示微信头像

不管是游戏 APP,还是 H5,又或者是微信小游戏,但凡接入了微信登录的应用,都可能需要显示微信头像。

在 Cocos Creator 中,我们常见的显示方法是像下面这样:

var headimg = 'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83erD6MOUwRKV9NyBAqnoFDTnltzAe2zWOkKxyDOFibVBb1ZV5CaATJwYAuNqZ5sXMBC4c8iacaHDf8RA/132';

cc.loader.load({url:headimg,type:'jpg'},function(err,tex){
self.icon.spriteFrame = new cc.SpriteFrame(tex);
});

这样做大部分情况下是没有问题的,但容易踩到两个坑:

假如用户在微信中上传的头像不是 JPG 格式,将会显示为黑屏

假如是 H5 中使用上述代码,会提示跨域访问

而最近新出了一个奇怪的事情,就是 Android 系统 7.0+ 的机器,在 4G 网下无法正常显示微信头像,包括腾讯《欢乐斗地主》里的排行榜也显示不出来。

这个问题我猜测,是 4G 的 Android 7.0+ 的 HTTP 头和其他环境下不一样,导致腾讯拒绝了头像访问,应该是封杀某音的时候,误伤。

解决这个问题最直接的办法,就是在自己的服务器上,配置一条 NGINX 转发协议。

server {
listen 80;
#server_name h5.ooxx.cn;

root /root/wwwroot/;

location /image {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass $arg_url;
}
}

假如,我们的外网IP或者域名是 h5.ooxx.cn, 端口是 80,或者其他的。我们修改上面的访问方式为如下:

var headimg = 'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83erD6MOUwRKV9NyBAqnoFDTnltzAe2zWOkKxyDOFibVBb1ZV5CaATJwYAuNqZ5sXMBC4c8iacaHDf8RA/132';
var url = 'http://h5.ooxx.cn:port/image?url=' + headimg + '&sb=213.jpg';
cc.loader.load(url,function(err,tex){
self.icon.spriteFrame = new cc.SpriteFrame(tex);
});

这样改的原因如下:

1、假如你做的是 H5 项目,h5.ooxx.cn 域名刚好就是你的页面加载域名,那么你将处于同域中,不再有跨域问题。

2、当我们请求最后合成的 URL 时,NGINX 会将 URL 参数作为请求地址,转发出去,并且将获取到的信息,原路返回。而我们添加的 proxy_redirect off 将会抹去我们系统机型为我们添加的各种 HTTP HEADER,不会再出现 Android 7.0+ 4G 网加载不了的问题。

3、添加 &sb=213.jpg 参数,是为了让 cc.loader.load 函数识别到这是一个图片加载,但由于不是强制的填写 type,即使 PNG 也是可以正常显示的。

C 姐补充:微信头像加载问题,我们在 Cocos Creator v2.0.10 和 v2.1.1 也会完善,到时候将会直接支持噢!


技巧二:微信名字截断(支持表情)

在我们日常游戏开发中,经常会面临将玩家名字截断的需求。

假如玩家是在我们游戏中创建的名字,那么可以简单粗暴地禁止玩家使用手机表情输入即可。

但如果我们是第三方账号登录,且使用了第三方账号的用户昵称,那么这个就不好保证了。

因此,为了配合界面的显示,我们通常需要在特定界面上进行名字截断。

举一个例子,假如我的名字是 麒麟子?? ,我们一眼看过去,是 5 个字。

但在计算机中可不是 5 个字,即使是全部用 UTF8 表示字符的 Javascript 中,console.log('麒麟子?哈?'.length) 也是8.

也就是说表情符号占用了 4 个字符,如果我们按 UTF8 去截断,必然会导致乱码。

因此我们定义以下规则:

ascii 字符,算 1 个字符

汉字,算 2 个字符

手机系统表情,算2 个字符 (因为它们虽然占了 4 个字符,但是从显示宽度上来看,依然是 2 个字符)。

下面的函数 strClamp 可助你一臂之力, 这是麒麟子从《幼麟棋牌》最新 5.0 框架中抠出来的代码,希望能够帮助到大家。

//str 需要截断的字符串
//maxChars 保留的汉字长度
//suffix 添加的后缀 (注意,如果后缀不为null或者'' ,则要占用一个汉字的位置,具体看下方的示例代码)

function strClamp(str, maxChars, suffix) {
var toCodePoint = function(unicodeSurrogates) {
var r = [], c = 0, p = 0, i = 0;
while (i < unicodeSurrogates.length) { var pos = i; c = unicodeSurrogates.charCodeAt(i++);//返回位置的字符的 Unicode 编码 if (c == 0xfe0f) { continue; } if (p) { var value = (0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)); r.push({ v: value, pos: pos, }); //计算4字节的unicode p = 0; } else if (0xD800 <= c && c <= 0xDBFF) { p = c; //如果unicode编码在oxD800-0xDBff之间,则需要与后一个字符放在一起 } else { r.push({ v: c, pos: pos }); //如果是2字节,直接将码点转为对应的十六进制形式 } } return r; } suffix = suffix==null? '...' : suffix; maxChars *= 2; var codeArr = toCodePoint(str); var numChar = 0; var index = 0; for (var i = 0; i < codeArr.length; ++i) { var code = codeArr[i].v; var add = 1; if (code >= 128) {
add = 2;
}

//如果超过了限制,则按上一个为准
if (numChar + add > maxChars){
break;
}

index = i;

//累加
numChar += add;
}

if(codeArr.length - 1 == index){
return str;
}

var more = suffix? 1:0;

return str.substring(0, codeArr[index - more].pos + 1) + suffix;
}
//示例:

var str = '麒麟子?哈?;
strClamp(str,3,'...') // 得到 麒麟...
strClamp(str,3,'') // 得到 麒麟子


技巧三:微信小游戏中音效中断问题处理

音效可谓是一个小游戏的灵魂了。

某些玩法离开了音效更是不可能,比如《别踩白块》,或者一些以速度为主的游戏类型。

麒麟子公司最近有一款斗地主上微信小游戏,另外还做了两款休闲类小游戏。

当我们觉得差不多大功告成的时候,有用户反馈说,背景音乐突然就没了,要返回大厅再进游戏场景才有。

我当时第一反应就是,正在播放的音乐被干掉了,重新播放又是 OK 的。

基于这个假设,那就表示这不是 Cocos Creator 的锅。

经过多方测试,找到了触发微信小游戏声音消失的重现办法:

(1)打接电话

(2)触摸 iPhone X 底部的那个白色操作条

接下来就是一顿,百度,论坛,微信文档操作,最后锁定了一个微信 API

wx.onAudioInterruptionEnd

监听音频中断结束事件,在收到 onAudioInterruptionBegin 事件之后,小程序内所有音频会暂停,收到此事件之后才可再次播放成功。

微信小游戏官方文档地址:

https://developers.weixin.qq.com/minigame/dev/api/wx.onAudioInterruptionEnd.html

与它配套的还有一个 wx.onAudioInterruptionBegin 事件,但是我们是要恢复播放,只需要处理这个end 事件就好了。

解决办法:

加上这个解决办法,是方便急着解决 BUG 的同学们,忽略之前的分析过程,直接锁定最终结果拿走,不谢!

wx.onAudioInterruptionEnd(function(){
//强行暂停音乐 如果不暂停,调用resumeMusic是无效的,因为是微信让声音消失了
cc.audioEngine.pauseMusic();
//恢复音乐播放,比如调用 cc.audioEngine.resumeMusic();
//self.refreshBG();
//console.log('refreshBG');
});

注意:必须要先强行调用 cc.audioEngine.pauseMusic,再调用 cc.audioEngine.resumeMusic, 因为这个声音是微信小游戏关掉的,Cocos Creator 的音乐管理器并不知情。


技巧四:打包原生 App 截图白屏解决方案

大家在做棋牌 App 或者一些特定需求的时候,需要截取当前游戏屏幕内容保存。

我们一般是采用 cc.RenderTexture 来截图并保存到游戏的可写目录。

有时候会遇上,截出来的图片是白屏,或者部分白屏。

经过多方测试,我们发现,是 Mask 的锅,用了 cc.Mask 的界面,截图的时候,就会遇上这样的问题。

如果遇上这样的问题,只需要检查你的 cc.RenderTexture 初始化的时候,是否少了参数。最主要的是第三个参数,一定要是 RGBA8888

var texture = new cc.RenderTexture(w, h, cc.Texture2D.PIXEL_FORMAT_RGBA8888, gl.DEPTH24_STENCIL8_OES);

完整示例如下:

function captureScreen(){
var size = cc.director.getWinSize();
var fileName = "result_share.jpg";
var fullPath = jsb.fileUtils.getWritablePath() + fileName;
if (jsb.fileUtils.isFileExist(fullPath)) {
jsb.fileUtils.removeFile(fullPath);
}
var width = Math.floor(size.width);
var height = Math.floor(size.height);
var texture = new cc.RenderTexture(width, height, cc.Texture2D.PIXEL_FORMAT_RGBA8888, gl.DEPTH24_STENCIL8_OES);
texture.setPosition(cc.p(size.width / 2, size.height / 2));
texture.begin();
cc.director.getRunningScene().visit();
texture.end();
texture.saveToFile(fileName, cc.IMAGE_FORMAT_JPG);
}


技巧五:技能 CD 效果制作

此效果不仅可以用于技能 CD,一些按钮的 CD 也是可以用的。

麒麟子一开始预备了两个套路:

一、是准备 100 张图片,然后根据 CD 进度进行切换

对于第一个方案,肯定是可行的,且不需要引擎提供特殊支持。而由于麒麟子的 PS 功力有限,没有折腾出来。值得说明的是,此方案适用于任何引擎,只要有对应的美术图片配合就行。

二、绘制 100 个 Graphics 作为缓存,然后根据 CD 进度进行切换

麒麟子创建了一个节点,添加了一个 cc.Graphics 组件。最后发现,cc.Graphics 的 arc 函数,并不能绘制出我想要的效果。既然没有直接支持的函数,想必 Cocos Creator 并不推荐这样的操作,放弃了。

思索了半分钟,抱着试一试的心态,打开了 ProgressBar 的组件,毕竟,CD 效果从本质上来说,是一个倒着播放的进度条。

三、最终,找到了基于 ProgressBar 组件的解决方案

1、场景树右键 --> 新建 --> UI 组件 -->ProgressBar 组件

2、修改 New ProgressBar 以及其子节点 bar 的宽高, 调为正方形,且锚点为 0.5,0.5

3、修改 bar 的填充模式,如下(注意红色箭头部分)

4、修改 New ProgressBar 的参数如下(注意红色箭头部分)

然后拖动 Progress 就能看到变化了。

四、DEMO

DEMO 向大家展示了以下内容:

技能点击响应

技能 CD 根据不同的时间,进行 CD 效果旋转

为了匹配技能效果,我旋转了技能 CD 层的节点

DEMO 源代码地址:

https://gitee.com/qilinzi/qlz_ccc_tips(目录 05_skill_cd)


写在最后

我建了两个群,一个是棋牌的,一个是非棋牌的,大家有兴趣的可以加入。
幼麟棋牌社区官方群:176294790
Cocos Creator 交流中心:727901932

非常感谢麒麟子带来的 Cocos Creator 干货分享,相信对于 Cocos 新手小白党应该很有学习意义,上述提及到的跟引擎相关的问题,我们也将在后续版本中进行完善,期待麒麟子后续带来更多的教程。

同时也非常感谢各位开发者对 Cocos 社区所做出的贡献以及对 Cocos 的信任与支持,在引擎的使用过程中,如有遇到任何问题,或者是有对 Cocos 的意见和建议,欢迎大家多多在社区中发帖交流噢,祝大家周末愉快!