微信小游戏开发工具介绍,不熟悉流程的伙伴视频我已经录好了!

视频地址:https://www.bilibili.com/video/av68584099

Cocos Creator 微信小游戏开发,我们需要使用到基本的4个工具:

file

  1. Cocos Creator:游戏内容生产,具体内容是游戏场景、预制体编辑,组件脚本配置;
  2. Chrome浏览器:游戏预览调试工具,也是 H5 游戏的运行环境;
  3. Visual Studio Code:代码编写工具,轻量、功能丰富,强烈推荐;
  4. 微信开发者工具:微信小游戏模拟环境、微信云开发可视化控制管理、游戏上传。

关于 Cocos Creator 游戏内容的开发,我们公众号上已经有了不少的教程,下面重点介绍微信开发者工具的使用和流程:

在 Creator 中配置微信开发者工具路径

file

构建发布到微信平台

file

  1. 选择构建窗口中,选择发布为微信小游戏;
  2. 默认为测试用微信小游戏 appid,仅支持微信开发者工具中运行游戏,这里填写你自己的微信小游戏appid;
  3. 点击构建,当构建完毕后,点击运行,Creator 会帮我们启动微信开发者工具;
    file
    如果你通过 Creator 启动微信开发者工具失败,需要在微信开发者工具主-菜单-设置-安全设计-端口服务,设置为开启即可。

真机预览

微信开发者工具,提供了一个游戏小游戏模拟器,你可以在这里预览、调试游戏,也可以通过真机预览。
file

点击主界面上的预览按钮,这时微信开发者工具,会将游戏资源、代码上传到小游戏开发环境中,注意整个游戏体积不能超过4M,上传成功能,会出现一个二维码,使用手机端微信扫描即可运行,此版本也称之为开发版。

上传游戏

游戏在模拟器、手机上没有问题后,就可以上传到微信后台,在主界面点击上传按钮,弹出如下提供框:
file

根据提示填写内容,点击上传按钮,游戏将被上传到微信后台。

体验版本

通过微信开发者工具上传到微信小程序后台,成为我们的预览版,将预览版本二维码发给伙伴们就可以体验到游戏了。
file

但在之前还需要,预览版体验者的微信号添加到成员管理-体验成员中,看下图红框:
file

提交审核

经体验版本检验,游戏也没有问题就可以提交审核了,如果是初次提交审核要填写的东西比较多,其中有两样比较重要的东西:

  1. 软件著作权
  2. 自审自查报告

这一步过了,就安静等待待审核通过吧!时间在一般在1~6小时不等。

发布上线

审核这关过了,那恭喜你!只差最后一步,在版本管理的审核一栏中,点击发布就可以了!这时你的游戏就可以给微信所有用户玩到了。

file

CreatorPrimer|飞机大战(二)

之前的飞机大战一我们实现了地图场景的滚动和简单的直上直下的子弹的发射,在之前的基础上,对线性发射器做了简单的修改,实现了如下效果:

飞机大战-子弹发射1.gif

飞机大战-子弹发射2.gif

下面我来就介绍一下,实现上面效果是如何实现的,先看视频:

1、子弹角度计算

子弹发射器

这里为子弹发射器增加了一个rotation属性用于控制子弹发射的角度,但是如何计算子弹的飞行终点坐标呢?

image.png

不好意思,请允许我重新翻开初中的数学关于三角函数章节,通过直角三角形中,角度与边的关系可以计算出飞行终点,我们根据上图,看下我们已知的参数:
1. 子弹角度β,是我们的组件属性
2. 飞行距离r,这里为了简单我使用了常用cc.winSize.height

因此通过三角公式可以得出:

x = r * sin(β)
y = r * cos(β)

需要注意的是JS中Math.sin\cos函数中的参数是弧度单位,每1角度为 Math.PI / 180 弧度,下面看终点的计算代码:

 _emmitNode() {
        //创建子弹
        let node = cc.instantiate(this.prefab);
        node.position = this.offset.add(this.node.position);
        node.parent = this.node.parent;
        node.rotation = this.rotation;

        //计算终点
        let endPoint = cc.v2();
        endPoint.x = cc.winSize.height * Math.sin(this.rotation * RADIAN);
        endPoint.y = cc.winSize.height * Math.cos(this.rotation * RADIAN);

        //计算飞行持续时间
        let distance = endPoint.sub(node.position).mag();
        let duration = distance / this.speed;

        //运行动作
        let moveBy = cc.moveBy(duration, endPoint);
        let removeSelf = cc.removeSelf();
        let sequence = cc.sequence(moveBy, removeSelf);
        node.runAction(sequence);
    }

2. 动态旋转

动态旋转很简单,在update每帧调用函数中,不断修改rotation属性值,看下面代码:

update(dt) {
     if (this.spin === 0) {
         return;
    }
    this.rotation += dt * this.spin;
}

spin在这里是一个旋绕速度参数,相同于电风扇上的摇头马达,通过子弹产生速度、飞行速度、旋转速度你可以创造了各种样式花丽的子弹效果,下面是我弄的几张截图:

给飞机挂了两个带spin参数的发射器,一个spin为360顺时针旋转,一个spin值为-360逆时针旋转,像对一凤凰的翅膀。

这个是挂了4个发射器,起始rotation分别为0、90、180、270,飞行速度快一些,spin值都是一样的,像刮起的凤凰旋风。

3. 小结

我们看似复杂的子弹效果,其实每一个都是用的直线动作,通过挂载多个子弹发射器,调节枪口角度、角度动态旋转可以生成出各式花样。工程源码可以在公众上回复“子弹发射器”或“LineEmmiter”获取,感谢你的阅读!

从一次失败的换皮方案

之前做了一个用于教学的消除游戏Demo,主要是用于教美术,由于美术资源会有很多套,而且希望都能在手机上试玩体验。因此需要编写一个换皮打包的脚本,这样我就可以偷懒了!

1. 换皮方案

想到的简单方案是替换 Cocos Creator 构建输出的图片,这也是之前使用Cocos2d-x\lua\js时期使用的方法。

在实践此方案之前我还做了一番分析,当时信心满满,结果却掉进了坑里。

优点

先说一下我想到的该方案的优点,主要诱惑是不用多次构建,当时心里还乐滋滋的!

只构建一次资源,之后都在这个build出的资源基础上进行图片替换,如果是出web-mobile包直接将文件复制进去就完事。

如果是android、iOS替换完图片后要执行一次编译打包,使用Cocos Creator的命令行可以搞定。

难点

Cocos Creator 2.x构建输出的资源文件被更换为UUID命令,需要找到项目图片与构建图片文件名之间的对应关系,不过有之前折腾模块化子游戏时做过一个小插件,生成的资源地图文件可以搞定这个问题。

缺陷

此方案缺陷有很多:
1. 只能替换散图,项目中也不能使用自动图集
2. 自己生成图集也不行,因为图集的plist数据不好定位,找不到地方替换
3. 也不能替换其它数据文件

缺陷暂时处理不了只能人工绕道了,反正我只是为这个小demo项目解决问题,暂不考虑通用了。

2. 资源地图

要建立构建资源与项目资源的文件的对应关系,我之前做的插件,在构建完成时,会在项目temp目录下生成一个assets-map.json的文件,文件内容如下:

[
    {
        "nativePath": "res/raw-assets/ae/ae8e3d60-767e-493b-8a4d-6b564e7ecab0.png",
        "url": "main menu/back icon.png/back icon"
    },
    {
        "nativePath": "res/raw-assets/5d/5de7dc2b-df4c-404d-9f68-d799da224356.png",
        "url": "main menu/eject_btn.png/eject_btn"
    },
    {
        "nativePath": "res/raw-assets/03/038a47ce-d322-4654-a80d-0419c739168f.png",
        "url": "main menu/help_btn.png/help_btn"
    },
    {
        "nativePath": "res/raw-assets/a9/a91e438f-38c3-4cc9-9f86-3138b2afdafa.png",
        "url": "main menu/next_scene.png/next_scene"
    },
    {
        "nativePath": "res/raw-assets/68/68a612ed-3e86-43c2-8883-535bc7711af4.png",
        "url": "main menu/prev_scene.png/prev_scene"
    }
    ...
]

上面JSON数组中,每个元素的nativePath字段是构建文件路径,以可看到构建输出的uuid文件名,url字段是项目assets目录下的文件。

有了这个映射地图文件,就可以定位到构建资源进行文件替换了,大概流程如下:
1. 遍历某套皮肤目录中的jpg、png文件,并拿到assets-map.json中查找是否存在对应的url。
2. 存在url,取出nativePath字段,将皮肤目录中遍历时的当前文件复制到nativePath

当然,在过程中还需要拼接好完整路径,皮肤文件的路径要与项目匹配、图片尺寸要一致等,在此就不多说了。

3. 杯具产生

脚本编写好了,将换皮后的游戏在浏览器中尝试一下,见下图:

image.png

图中上半部分是游戏在编辑器中的效果,下半部分是资源替换后的运行效果,杯具了!
替换进去的图标被裁切了一部分!两套图片在尺寸上是一样的规格,为什么会这样呢?

将要替换的文件直接放从Cocos Creator项目assets目录,发现图片的meta文件有变化:

image.png

变化的内容正是图片SpriteFrame的属性,原始版本的图片透明区要大一些,约束框要小一点,替换资源的透明区要小一些约束框要大一点,构建资源虽然更新了图片,但SpriteFrame没更新,因此出现图片被裁切问题!

4. 结语

问题知道了,此路暂时不通,只好把图片导入Cocos Creator项目,还是走重新构编译的路子。

虽然方案失败了,还是想刨点有价值的东西,如果你对生成的assets-map文件感兴趣可以在公众号中回复:assets-map资源地图获取该插件。

使用方法:
1. 将插件放入项目package目录
2. build项目等待完成
3. 打开项目temp目录,会发现一个assets-map.json文件。


感谢关注「奎特尔星球」公众号,欢迎大家来稿,愿我们一起成长!

CreatorPrimer|飞机大战(一)

前两天在Cocos官方公众号上学习了「大掌教」的Cocos Creator 2.x Camera教程,总算是对摄像机组件有了一个初步的认识。乘热打铁,Shawn即刻就使用Camera摄像机练习了一个飞机游戏的,目前主要实现3个功能:
1. 无限滚动背景
2. 控制飞机移动
3. 子弹发射

我们现看下面游戏视频:

1. 无限滚动背景

滚动背景我们当然是使用最新的摄像机来实现,我这里做了一个卷轴摄像机组件ScrollCamera,我们现来看一下组件暴露的属性:
滚动摄像机

ScrollCamera组件很像真实世界中的摄像机的推进器,Speed是推进速度,LoopGrounds是一个节点数组,他们是一组可首尾衔接的精灵节点,看下图:
可首尾衔接的精灵节点

我们再看一下ScrollCamera组件的代码:


cc.Class({ editor: { requireComponent: cc.Camera, //前置要求摄像机组件 }, extends: cc.Component, properties: { speed: 300, //滚动速度 loopGrounds: [cc.Node], //循环节点 }, start () { //获取节点上的摄像机组件 this.camera = this.getComponent(cc.Camera); }, /** *每帧更新函数 *1. 更新摄像机位置 *2. 检查循环节点,设置新位置 **/ update(dt) { //获取当前节点 let current = this.loopGrounds[0]; //计算当前节点在摄像机中的位置 let pt = this.camera.getWorldToCameraPoint(current.position); //当前节点超出摄像机范围(摄像机可视范围就是屏幕大小) if (pt.y <= -cc.winSize.height) { //取最后一个地图节点 let last = this.loopGrounds[this.loopGrounds.length - 1]; //将当前节点从数组中移除 this.loopGrounds.shift(); //将当前节点放到数组最后 this.loopGrounds.push(current); //将当前节点位置移动到最顶部位置 current.y = last.y + (last.height + current.height) / 2; } //更新摄像机节点位置 this.node.y += dt * this.speed; } });

推动摄像机的代码很简单,看update函数中的最后一行:

this.node.y += dt * this.speed;

update中前面的几行代码是在做loopGrounds节点的检查和位置更新,每一行都注释,这里就不再过多赘述了。

将这个组件直接拖动到场景编辑器或层级管理器,设置background节点为background分组:

分组设置

同时设置ScrollCamera的cullingMask属性只勾选background,看下图:
摄像机分组

通过上面的设置和ScrollCamera的十几代码,无限滚动背景就搞定了。

2. 控制飞机移动

不知道大家还记得公众号之前的一篇文章《Cocos Creator基础教程(11)—可拖拽组件》,我直接将Dragable.js组件脚本拿过来,挂载到飞机节点上就OK了,代码也很简单:

/**
 * 可拖动组件
 */
cc.Class({
    extends: cc.Component,

    onLoad() {
        //注册TOUCH_MOVE事件
        this.node.on(cc.Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
        cc.log('onload');
    },

    _onTouchMove(touchEvent) {
        //let location = touchEvent.getLocation();
        //this.node.position = this.node.parent.convertToNodeSpaceAR(location); 

        //获取触摸移动增量
        let delta = touchEvent.getDelta();
        //当前节点位置+增量,更新节点位置
        this.node.position = delta.add(this.node.position);
    }
});

_onTouchMove函数稍微调整了一下,之前使用方法的是当前节点设置为触摸点位置,需要将全局坐标转换为当前节点的父节点坐标(设置一个节点的位置,是设置它在其父节点中的位置),拖动时节点总是保持在移动点的中心,特别是在第一次拖动节点时会有一个跳跃感,不够平滑。

我这里简单改进了一下,通过获取移动增量再加上当前节点位置,可以拖动节点的任意位置,不会出现突然将节点拉动到手指中心的突兀感。

Shawn在做这个飞机游戏过程中也尝试了一下消灭病毒当下这个火热的游戏,他的整个屏幕任意位置都可以控制飞机的移动,它是怎么做的呢,大家先可以思考一下?

我们这里再修改一下Dragable组件,增加一个target节点属性,将它从飞机节点上移到外层foreground节点,看下图:

可拖拽目标节点

触摸事件发生在foreground节点上,但移动的是target属性所指向的节点,我们看下代码:

/**
 * 可拖动组件
 */
cc.Class({
    extends: cc.Component,
    properties: {
        target: cc.Node,
    },
    ...
    _onTouchMove(touchEvent) {
        //获取触摸移动增量
        let delta = touchEvent.getDelta();
        //如果this.target未设置,使用移动当前节点(兼容之前的用法)
        let node = this.target || this.node;
        //当前节点位置+增量,更新节点位置
        node.position = delta.add(node.position);
    }
});

代码就增加了一个target节点的定义,在TouchMove事件中检查this.target存在就用它,不存在默认移动当前节点,这样可以兼容曾经该组件的地方,不用做修改。

3. 子弹发射

飞机游戏的一个亮点就是子弹发射的华丽视觉效果,Shawn这里在网上找了些子弹特效图片,然后编辑了一个子弹Bullet的预制体,使用到之前文章《Cocos Creator基础教程(12)—精灵变身》中的SpriteEx.js组件,在上面配置了几张子弹图片,使用index属性可以方便切换子弹的表现效果,看下图:

SpriteEx.gif

Bullet子弹只是表现效果,要让子弹运动起来,我这里编写了一个LineEmmiter.js(线性发射器)的脚本,将它挂载到飞机节点上,用它来实例化Bullet预制体并让它动起来,先看一下LineEmmiter组件的属性:

线性发射器.png

之前的文章中提到过:组件为节点赋予能力,飞机节点上有一个Sprite可显示图片纹理,我们再挂上LineEmmiter组件,让它具有发射子弹的能力。

发射器的主要属性是子弹预制体、发射频率、子弹飞行速度,OffsetX属性要特别一点,它可以控制子弹与飞机的偏移位置,以实现同时发射多行子弹的效果,看下图:

多行发射.png

我们再看下发射器的组件代码:

cc.Class({
    extends: cc.Component,

    properties: {
        prefab: cc.Prefab,
        rate: 1,        //发射间隔
        speed: 1000,    //移动速度
        offsetX: 0,
    },

    start() {
        this.schedule(this._emmitNode, this.rate);
    },

    _emmitNode() {
        //实例化节点,设置位置&父节
        let node = cc.instantiate(this.prefab);
        node.position = this.node.position;
        node.x += this.offsetX;
        node.parent = this.node.parent;
        //计算子弹需要飞行的距离,飞行时间 = 距离 / 速度
        let distance = ((cc.winSize.height / 2) - this.node.y);
        let duration = distance / this.speed;
        //使用moveBy动作,完成后删除子弹节点
        let moveBy = cc.moveBy(duration, cc.v2(0, distance));
        let removeSelf = cc.removeSelf();
        let sequence = cc.sequence(moveBy, removeSelf);
        node.runAction(sequence);    
    }
});

发射器代码也很简单:
1. 实例化子弹节点
2. 让子弹飞起来

我们这里子弹是垂直飞行的,直接使moveBy动作就可以完成,子弹从当前飞机节点出发直到屏幕顶部结束,这是它飞行的距离根据公式:距离/速度=时间,计算每颗子弹的飞行时间,保证飞机在不同位置,所有子弹都是按同样的速度飞行。

4. 小结

本次教程我们实现了一个最小飞机游戏的简单原型,我们的核心地图滚动与子弹发射代码只有70多行,有没有觉得使用Cocos Creator开发游戏飞一般的简单呢…

不过还有很多欠缺的地方,比如:限制飞机不要跑出屏幕之外、子弹应该使用内存池进行优化,在功能上还缺少敌机生成、少子弹碰撞、得分计算等等,这些内容我们留到下次继续。

奎特尔星球

奎特尔社区趣闻播报

1. Cocos全3D动作独立大作—《诛将录》

视频:

第一眼看到游戏视频就被深深的震撼了,3D一直是Shawn一直未敢尝试的领域,2D都没搞清楚怕3D只会更打击人…不过看到这个游戏视频,而且还是以Cocos为基础开发出来的,Shawn心里也有些蠢蠢欲动。

我们还是先通过《诛将录》开发者了解一下游戏所设计的技术内容
1. 采用cocos3.x引擎修改版完成,自研编辑器工具链,整合引用particular univers 粒子系统,搭配新增3D特效编辑器,实现完整特效系统。
2. 场景编辑器,实现terrain编辑以及terrain烘培(而非高度图生成,极大降低顶点数以及高性能不受限的地表纹理刷),模型lightmap烘培(编辑器最大同时支持162组光照),简易固定材质编辑,以及模型自动打组/解组以实现塌陷/极大降低drawcalls。角色动画系统完善并解决了若干处动作融合等严重bug
3. 新增renderToTexture3D以支持postrendering,最终实现全场景扭曲/bloom泛光,动态模糊等效果。
4. 移除了引擎中原有场景和shader中光照相关,改为以预烘焙为主的近似模拟实现。
5. 游戏中大量支持了物理系统,沿用的是cocos集成的bullet物理引擎。
本作是全程联网的,故而支持pvp与双人闯关。
6. 主打流畅战斗体验和每一局都是全新开始概念的0氪金,可iphone5s以60fps流畅运行。

游戏预计将在2019年2月底上线taptap,先行测试版本是完全免费且不带广告的。

2. Creature2D骨骼动画工具

视频:

Creature有很强大的动画功能,很多复杂/耗时的手k动画例如:布料,肌肉,风效等等,在creature里头都非常容易实现。旨在为数字内容添加极其流畅的动画。

Creature是理想的动画工具,适用于游戏开发者,数字艺术家和网页设计师,他们希望添加特殊的动画魔术,让您的内容焕发活力,以及令人难以置信的简单和省时的方式制作出惊人复杂的动画。

3. Creator粒子编辑器

插件小王子新作,超强的Cocos Creator粒子编辑器,看下图:

这款粒子编辑器综合了市面上优秀粒子编辑器的优点,使用场景编辑的方式控制粒子参数,使用美术、策划人员也能快速上手。

插件使用说明:
1. 部分粒子参数进行了可视化图形操作
2. 内置了大量粒子模版,可在模版的基础上快速调整出来粒子效果
3. 粒子纹理支持直接拖拽
4. 生成的粒子文件.plist,,.plist 文件名以纹理图片文件名为准,比如你的纹理图片叫做sun.png,那么最终的粒子文件为 sun.plist, sun.png

更多资源和使用方法可以参考下面视频:
https://www.bilibili.com/video/av43854514/

个人开发都是孤独的,心疼一下撸的肝痛的小王!

image.png

插件下载地址:
http://store.cocos.com/stuff/show/178996.html

4. pbkiller支持Creator 2.1.1

image.png
感谢「悦雨」对pbkiller的反馈,pbkiller在Cocos Creator 2.1.1确实是有问题,Bug已经解决,新版本已经提交Cocos插件商店,目前还在审核中。

image.png

async.js在Cocos Creator中的应用

一、在Cocos Creator中使用async.js库

在Cocos Creator项目中async.js有两种引方式:
1. npm安装方式
2. 源码插件方式

下面分别介绍这两种的具体操作步骤

npm安装方式

使用npm管理三方模块,首先需要在Cocos Creator项目中初始化npm的包管理配置文件package.json,在输入行中输入:

> npm init

输入命令后,会要求输入一些信息,这不是我们的重点,一路回车即可,操作完成后会在项目根目录创建一个package.json的文件,内容如下:

生成package.json

然后我们使用npm安装async模块:

npm install async --save

输入上面命令后,会输出一些信息,没有意外,不到10秒就安装好了,看下图:

使用npm安装async.js

安装好后npm会在当前目录创建一个node_modules的目录,打开后你可以看到async模块目录:

确保node_modules下的async目录存在,就可以在项目中使用async的。

2. 源码插件方式

有的人可能不习惯使用npm方式,我们可以从npm模块中将async.js的发布源码复制到工程assets目录中,然后把node_modules目录删除,看下图:

只需要将aysnc.js或async.min.js其中一个文件入assets就可以了。将文件放入assets目录,激活Cocos Creator时会提示,是否需要设置为插件,看下图:

导入插件提示

设置为插件async会成为一个全局模块,在使用的地方不需要用require进行导入,直接直接使用即可。

以上这两种方式构建项目都可以让async.js在微信小游戏环境中运行。

二、 async.js应用场景

Shawn在使用async主要应对下面三种场景

1. 创建大量对象时减少卡顿

并发控制

上图中使用async.eachLimit可以控制在遍历array变量时的并发数量,每帧调用20次this._createTile函数,它是在实例化prefab是一个耗时的操作。

我这里做了一个小测试,比较使用普通循环与async异步实例化1000个prefab的情况:
【插入视频】

不知道你是否从视频中看出点什么? 使用普通循环创建时,可以看到视频中红色方块有瞬间的停顿,因为它是在1帧里面做了1000次,async异步实例化是将这1000次循环分散到了n帧去做(其实总消耗的时间会更长)。

2. 让异步逻辑流程更清晰

异步流程控制

当有一系列异步函数需要串行执行时,asyc.series可以很方便帮助我们解决此问题,从回调地狱中解脱出来。

3. 让异步动画灵活多变

动画的执行都需要有时间,因此也存在大量的异步控制逻辑,我们可以使用一个个的小函数将动画播放逻辑控制起来,并提供一个完成回调。

异步动画控制

然后再用async.series将它们串连起来,而且动画的播放顺序可以根据需求灵活调整。

Shawn之前还有一篇教程《英雄之舞—凌波微步(利用async.js编写异步动画)》中对async.js在动画控制中有更多的说明,如有兴趣可以参考此篇教程。

三、小结

异步编程是JavaScript语言的一大特色,异步控制的方案流行的还有Promises、async-await,async.js仅仅只是其中的一种,如果你有更好的JavaScript异步编程经验或方案、案例欢迎留言讨论。

源码:公众号中回复“async”即可获取


2019开工第一天,发现你的大天赋

今天是2019新年开工的第一天,想必大多数人已经进入工作状态,Shawn已经从老家回到了成都,虽然没有像大家一样正式上班,但在精神上也在努切换到工作模式。

我今天的工作仍然是继续买菜、做饭、洗碗、带娃…,不行!不行!不行!看着微信群里一个个的晒红包,我顿时向老婆咆哮道:“老板!新年开工,洗碗工也要发红包的哦!”

开工大吉

虽然以上工作艰巨,但是Shawn还是偷偷在Switch上玩了几把《暗黑破坏神Ⅲ》,我女儿看到我在玩,也强烈要求加入,一边玩一边说:

“这个游戏有什么好玩的嘛”,
“你们男生就喜欢玩这种打打杀杀的游戏”,
“就是感觉杀死很多怪物时很爽”,
“我觉得我好牛哦!”

我晕,这是要上瘾的节奏呀,赶快禁止,做点正事找本书来看!

image.png

这本书买了好两个月了,一直没怎么看,之前有人说我写的教程,断言说这肯定是策划写的,再三澄清没用好吧!那就尝试学点策划的东西。当感觉有点眼皮子打架时,一段文字把我惊醒:

天才的秘密是对工作的爱

简单提炼一下:
1. 小秘密:天赋有两种“小天赋”与“大天赋”;
2. 小天赋:先天擅长某种技能,并可轻松施展,但不一定享受其过程;
3. 大天赋:对工作的爱,发动自己一切有限的技能来完成任务,并能支持下去。

开卷有益,书中问道:

“游戏设计师的目标是什么?”
“游戏设计师的目标当然是设计游戏”

这个回答当然是不对的,书中讲道:

游戏设计师真正关注的是创造体验,必须尽可能领悟、理解,掌握人类体验的本质;游戏能够带来体验,但游戏并不是体验本身;玩家和游戏是真实存在的,但体验是想象中的。

为什么《暗黑破坏神Ⅲ》没几分钟就把一个从来没玩过的小朋友给带走了,正是因为它产生了一系列的爽快,自我感觉牛逼的体验!心中已经长草了,就一爽到底吧,还好假期还有几天,争取两天通关,封存Switch!让肉体与精神正真进入工作模式,在工作中挖掘出我们的大天赋。

2019开工第一天,你是怎么过的呢,欢迎讨论!

CreatorPrimer|编写一个版本号组件

在集合类游戏中,不论是大厅还是子游戏都会涉及到版本的更新,在开发调试阶段,检查更新是否生效的一个直观的方法就是观察版本号的变化,因此版本号的显示是游戏中不可缺少的细节,特别是集合类游戏。

1. 熟悉manifest

这里我们使用 Cocos Creator 提供的 AssetsManager 热更新框架所要求的 project.manifest 它是一个JSON格式的配置文件:

{
    "version": "0.0.1",
    "packageUrl": "http://192.168.1.100/update",
    "remoteManifestUrl": "http://192.168.1.100/update/hall-project.manifest",
    "remoteVersionUrl": "http://192.168.1.100/update/hall-version.manifest",
    "assets": {}
}

上面是一个hall-project.manifest,再看一个game-project.manifest内容如下:

{
    "version": "0.1.1",
    "packageUrl": "http://192.168.1.100/update",
    "remoteManifestUrl": "http://192.168.1.100/update/game-project.manifest",
    "remoteVersionUrl": "http://192.168.1.100/update/game-version.manifest",
    "assets": {}
}

获取版本号,其实就是读取 manifest 中的 version 字段,显示到一个 cc.Label 组件上。这对大多数人来说都是小菜一碟,但是Shawn发现,利用组件方式实现一个相对通用的版本号组件做法却并不多见。

2. VersionLabel组件

了解过 manifest,我们就可以开始动手编写一个基于 Cocos Creator 引擎提供的 AssetsManager 热更新框架的版本号组件,这里将组件取名为“VersionLabel”,先看一下组件提供的属性接口:

VersionLabel

对于组件的使者关心的是ModuleName属性,当你想显示不同游戏模块的版本时,只需要指定正确的ModuleName就可以了,下面是组件代码:

cc.Class({
    extends: cc.Component,
    editor: CC_EDITOR && {
        requireComponent: cc.Label, //强制依赖cc.Label组件
    },

    properties: {
        default: '0.0.0',
        moduleName: {
            default: '',
            notify(oldValue) {
                if (CC_EDITOR || oldValue === this.moduleName) {
                    return;
                }
                this._updateContent();
            } 
        }
    },

    start () {
        //获取Label组件
        this.label = this.getComponent(cc.Label);
        //更新版本内容
        this._updateContent();
    },

    /**
     * 更新内容
     */
    _updateContent() {
        //加载“resources/manifest/xxx-project.manifest”
        let url = `manifest/${this.moduleName}-project`;
        this._getManifestContent(url, (content) => {
            this._setVersion(content);
        });
    },

    /**
     * 
     * @param {String} url      resources以下路径
     * @param {Function} cb     异步回调函数,返回manifest上下文
     */
    _getManifestContent(url, cb) {
        cc.loader.loadRes(url, cc.Asset, null, (error, asset) => {
            if (error) {
                cb(null);
                return;
            }
            //通过nativeUrl读取文件内容
            let content = cc.loader.getRes(asset.nativeUrl);
            cb(content); 
        });
    },

    /**
     * 设置Label文本
     * @param {String} content 
     */
    _setVersion(content) {
        let data;
        try {
            data = JSON.parse(content);
            this.label.string = data.version;
        } catch(e) {
            cc.warn(e);
            this.label.string = this.default;
        }
    }
});

简单说明一下上面的代码:
1. 该组件提供了一个moduleName的属性,这里注意不要使用name属性,因为是‘name’是Cocos Creator组件内置属性,还有定义manifest文件需要按照一定文件名命规范,我这里的名命模版是“xxx-project.manifest”
2. 我们是将版本号文本显示到Label组件上,因此requireComponent: cc.Label 是定义该组件强制依赖cc.Label组件。使用它的好处是,当直接将该脚本拖动到场景或层级管理器时,会自动挂载一个cc.Label组件,增强组件的使用体验。
3. manifest文件是放在resources目录下的,虽然manifest内部是json格式,但目前cc.loader还不能直接解析manifest这个扩展名的文件内容,当使用cc.loader.loadRes加载后,只能获取到文件的基本信息,通过使用cc.loader.getRes(asset.nativeUrl)获取文件内容。

3. 读取搜索路径下的manifest

上面的组件代码还存在一个Bug,我们是读取的安装包中的manifest文件,看下面代码:

let url = `manifest/${this.moduleName}-project`;

此文件存在于 resources 目录,当运行在原生设备上它是存在于安装路径中,当游戏通过热更新下载了新的 manifest 文件,此路径是否还正确呢?当然就不对了,我们需要读取热更新包中的 manifest 文件才能获得正确的版本号,看下面代码:

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

    /**
     * 更新内容
     */
    _updateContent() {
        //如果为原生环境,尝试加载可写路径下的xxx-project.manifes文件
        if (cc.sys.isNative) {
            let remoteAssets = cc.path.join(jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/', 'remote-assets');
            let url = cc.path.join(remoteAssets, this.moduleName, '-project.manifest');
            //使用jsb函数读取文件内容
            let content = jsb.fileUtils.getStringFromFile(url);
            if (content) {
                this._setVersion(content);
                return;
            }
        }

        let url = `manifest/${this.moduleName}-project`;
        this._getManifestContent(url, (content) => {
            this._setVersion(content);
        });
    },

    ...
});

上面代码在_updateContent函数中检查当前如果为原生环境,先尝试读取可写路径/remote-assets/xxx-project.manifest文件,如果文件内容不存在才读取安装包resources目录下的manifest文件。

需要注意的是remote-assets是使用AssetsManager下载热更新包时指定的,你需要根据自己的实际情况设置正确的路径。

源码地址:https://github.com/ShawnZhang2015/CreatorPrimer

4. 小结

读取版本号这个逻辑上下文只需要关心到那里去获取版本字符串,使用组件的方式,可以将很多逻辑细节代码封装到一段小代码中独立执行,以达到与其它代码老死不相往来,从而有效减少耦合。

同时借助Cocos Creator的可视化属性面板暴露接口,可以让非程序员也能轻松使用组件搭建出游戏内容,不知道大家获取版本号是如何实现的呢,也欢迎分享你的实现方案。


奎特尔星球

梦想的另一支翅膀—Creator插件开发

前言

前天两吐槽Cocos插件商店的问题,迅速得到了Cocos大佬们的重视,当天晚上就将插件款收到了,在此感谢帮助过我的朋友们,也感谢批评过我的人,也感谢2018年的自己。

关于Cocos插件开发,在Cocos插件商店上架了11款插件的插件小王子「许彦峰」则是比Shawn有着更深刻的见解,先看一下小王子为Cocos插件商店的杰出贡献,请看下图:

小王子的插件大军

以上插件中有5款是收费插件,看下图:

image.png

其它插件都是免费的,它们不仅可以帮助我们的日常开发工作,也是学习插件开发的极佳参考与案例。

小王子的心声

使用Cocos Creator也有2年的时间了,说一下目前的使用体验吧!因为对插件比较熟悉,所以会偏重一些插件方面。

  • 编辑器API特别缺乏,特别是Scene,Prefab相关API,所以能够看到基本上插件商店的都是修改移动查找文件、游戏外围的Excel转换,文件资源压缩等一些插件工具,这些都是一些不痛不痒的功能,真正渗透到编辑器、引擎的插件几乎找不到。
  • 编辑器扩展使用的Web开发技术栈,这个对于从Native开发过来的小伙伴不是太友好,因为Web知识点太碎太庞大,而且Web技术更新非常快,当时从长远考虑,选择Web技术去开发插件是一件正确的方向,目前来说,插件开发门槛还是比较高。
  • 想要开发一些高度定制化的功能,必须得学习Electron,这个学习成本这里不再赘述,心塞心累!
  • 插件收入分成 6.5 : 3.5 比例太高,本来开发者数量不算是很多,所以插件设置价格过高肯定卖不出去,太低了,平台抽成又拿走了一部分,实际到开发者手中的屈指可数,看着一年百十来块的收入,根本就没有提现的动力。
  • 插件没有代码保护机制,下载的插件基本算是源代码,就是是做了代码混淆,也完全可以自行修改为己所用,这既是好事,又是坏事。
  • 插件审核速度几乎为0,全靠开发者催官方人员。
  • 个人录制的一些插件视频,几乎没什么人关注,也从侧面反映,creator在插件这块,如同鸡肋,食之无味弃之可惜。个人感觉,creator无论是在游戏产品层面,还是在插件层面,都缺少一款代表作,就像是当年凭借《刀塔传奇》,creator迅速占领市场,引爆手机游戏开发领域。如今的吃鸡游戏,虚幻引擎可谓是又风光了一把。
  • 对于插件下载这块,作者自己的付费插件下载还需要付费,也不能评论回复,感觉有些醉醉的,看下面:
    image.png
  • 后台管理,收到的评论,看的是一脸懵逼,查找起来特别费劲
  • Cocos Creator在动画编辑器这块,一直被广大开发者诟病,这里打个广告,近期在GitHub上发现了一个名叫Creature的一款专注动画的软件,竟然对Cocos Creator做了支持,而且还对微信小游戏做了特地的适配工作。后来联系到软件作者了解到竟然这款软件在中国有稳定的使用客户,creature runtime for creator 就是为Cocos Creator量身打造的,在惊叹Cocos Creator影响力之余,不仅唏嘘Creator Animation为啥亲儿子这么不争气。
  • 我身边的开发者有些是从Unity转型过来的,Unity现在已经不仅仅应用到游戏领域,已经融入到了各个行业,和Cocos Creator相比,实力不言而喻。所以一边骂一边使用Cocos Creator,已经成为了日常开发工作,所以,开发习惯一个引擎一个风格,总结一下,都是编辑器给惯的!

结语

上面是小王子对插件开发和Cocos Creator的一些肺腑之声,新的一年祝Cocos Creator不断突破,越来越好,愿更多的开发者能在Cocos Creator构建的平台上长出梦想的翅膀,2019我们一起加油,一起飞翔!

遇见未知的自己,奎特尔成长之路!

前言

有幸在安晓辉老师的「程序视界」填写了一份副业赚钱的调报告,受到安老师邀请,分享自己在这一年多来的转型之路,在开始之前先做个自我简介:

张晓衡
   软件开发工程师,独立游戏开发者兼职讲师
成就事件:
   - 连续2月在Cocos论坛刷帖回答问题,被官方大神友情关注
   - 参与首批Cocos付费插件商店建设,提交pbkiller插件
   - 开通「奎特尔星球」微信公众号,1年4个月原创100+篇教程
   - 授邀成为Cocos兼职讲师,开启转型之路
   - 在GitChat开启首篇付费教程
   - 个人开发微信小游戏 《奎特尔数字大冒险》

1. 低谷

我是从2013年底开始进入的游戏开发行业,从业3年多,近乎996的工作节奏,从卡牌到SLG,经历的每一个项目都如石沉大海,渐渐地对自己失去了信心,内心充满焦虑和恐惧!

从2016年底在家休息了两个月,当时真不想再做游戏了,学习一些非技术类的知识。其中《樊登读书会》对我的影响非常大,在两个月时间内,听了近50本书,特别是《翻转课堂的可汗学院》、《翻转式学习》、《匠人精神》让我深受启发,自己从事Cocos2d-js游戏开发这么些年,还是有些积累,能否让自己的经验分享出去,于是开始了自己第一篇文章《探索cocosH5正确的开发姿势》,零散地录制了些视频。

2017年初,因为经济压力,不得不去工作,进入了一家棋牌游戏公司。整整半年时间,自己都是处于抑郁状态,做着自己不喜欢的事情,对未来没信心!这段时间《樊登读书会》一直陪伴着我,特别是一行禅师的《正念的奇迹》、《和繁重的工作一起修行》让我在抑郁状态下,坚持认真工作。业余时间,我在简书上写点Cocos游戏开发的文章,每当收到他人的点赞或打赏,还是会特别高兴,为了让自己能分泌更多的多巴胺,我将部分教程也分享到Cocos论坛上,偶尔有幸还能上Cocos官方公众号。

2017年8月封一次意外的邮件点燃了我沉沦的激情,当时Cocos Creator游戏引擎的制作人王楠竟然会给我发邮件!!!邀请我参与Cocos付费插件商店的建设,将我文章中关于protobufjs的经验编写成Cocos Creator插件,而且还以收费的方式首批入驻Cocos付费插件商店。

pbkiller

花了两周时间,我的pbkiller提交了第一个版本,过了两天陆续有人找到我说:“购买了你的插件不会使用”,做视频、写说明、当客户服忙活了好一阵子,第一笔非工资入收,虽然数量不多但心中还是非常激动的!直到现在pbkiller卖出了170多份,看下图:

pbkiller销售记录

2. 公众号之路

我下来仔细回想、分析,能编写插件卖钱是因为,自己写的分享教程能对人有帮助,它不仅可以提高自己技术能力、表达能力,更重要的是,它还能对周围的人产生一定的影响,同时给自己提升多巴胺分泌,也算是对自己抑郁症状的一种自我治疗!

那段时间,我非常更热衷写技术教程,开通了一个微信订阅号取名为「奎特尔星球」(奎特尔就是Creator的音译),一是将曾经在简书上的文章搬了上去,二是自己编写了一些提搞游戏开发效率的组件代码放到了github,还为它写了好几篇风格迥异文章(武侠、动漫、无厘头),三是一有空就到论坛刷帖回答问题、收集素材!

「奎特尔星球」关注量每天两个、三个的样子,极其缓慢的增长!偶尔收到有网友来公众号留言,一是来告知插件或github上的代码有bug,二是咨询CocosCreator开发中遇到的问题,这些我将他们的做为公众号持续输出的重要来源,并真诚、耐心对待,因此有时也会收到网友们的赞赏或红包。

赞赏

虽然数目微不足到,但收到赞赏或红包之时,心情还是特别愉悦,不是因为能收到钱,更多的是能帮助到他人同时得到肯定 。到目前为止,现在公众号终于突破1000人,到今天已经有1030了!

公众号突破1000人关注

由于公众号的持续输出,也结识了一些朋友,也有人在默默的关注。为了能坚持下去,还得不断提升自己,我又开始迷恋上了《逻辑思维》和《得到》,购买了《硅谷来信》、《武志红的心理学课》、《超级个体》等一些课程,每天上下班的路上、晚上睡觉之时,就是我的得到时间,让我的思维方式一点一滴发生着微秒变化。

3. 道路崎岖

2017年12月31日,正好是元旦放假,女儿感冒生病主院,我折腾了两天,也倒下了,当时高烧40度(十年没感冒发烧过了),因为医院人满为患,连住院的资格者没有。老婆在医院照顾女儿,我高烧不退在家躺了4天,烧的全身发软有气无力,也吃不下东西。

在此时微信小游戏横空出世,我在元旦之夜,在医院时刚推了一篇小游戏的公众号文章,第二天社区又遇到一个问题:“protobuf不能在微信小游戏中使用”,当然我的pbkiller插件也不能幸免。

高烧的前一天我独自己在家,完全是个人生活都不能自理,我给父亲打电话,父亲第二天就过来照顾我,下午稍微有点力气,用了两天时间,我把小游戏protobufjs问题搞定,更新插件,在公众号上推送解决方案。

在家休息了4天,虽然烧已退,但人还是没什么力气,思维也变得迟钝,写公众号、泡论坛也渐渐少了,2018年上半年,似乎又进入了周期性循环,整个人又陷入一段抑郁期,公众号停止更新了103天。

唯独《得到》还在天天听,此时也关注安晓辉老师的「程序视界」有一段时间,也在幻想能做自己喜欢的事情,同时能养活自己。在2018年6月,在网上接到一个儿童教育公司的外包Demo任务,我当时不仅把甲方公司的任务搞定,而且还帮助培训他们的美术人员使用Cocos Creator游戏引擎,在我提供的脚本、开发环境之下可以独立创作内容,这为我打开了新的思路,我不仅可以分享经验给程序员,也可以帮助到美术、策划人员。

我狠下心辞去了工作,找儿童教育公司谈合作,以技术顾问的方式帮助他们将传统PPT开发成H5游戏的方式,签了一个半月的兼职合同。同时公众号又开始复活,推送的文章短小精干,不在追求高深的东西,图片用的也更多,定位Cocos Creator的初学者和美术、策划人员。

从此开始,我就踏入了自由职业的路子,在网络上帮助更多的人,也在线下去一些小公司帮助解决一些问题,但在经济上没有任何收益。由于之前写公众号的原因,有朋友将我推荐给一位掘金主编,他们策划了一个小册题目《使用Cocos Creator开发微信小游戏》,我当时也答应了下来,同时在策划自己的一个小游戏,计划是将游戏做成微信小游戏,以此为案例做成一个有体系的视频教程。

4. 讲师之路

现在有了自己的时间,每天就是写文章、写代码,还有每周两天时间去儿童教育公司,主要内容是:搭建内部开发环境、部署自动化构建,培训美术人员使用git、jenkins,以及Cocos Creator更多内容等,尽可能帮助他们能自己创作课件内容,我只是提供辅助。

我将新的开发协作方式整理推送到公众号,用了一个多月时间公众号从300+上升到600+,不过掘金小册被黄了,掘金要求教程内容一年能不能在其它平台上发布,我也没按时交稿。但是这段时间我也与更多的开发者沟通、交流,为我带来新的机会,有公司愿意聘请我做Cocos Creator讲师,兼职为他们的企业客户做培训。

我二话没说就答应了,对方公司提供了培训内容需求,第一次给了我四个题目,让我制作成PPT,完成后还需要他们的技术主管审核。激情再次被点燃,我每天在家花费近5~6个小时制作PPT,用了半个月时间,之间来回修改了两个版本还是不能得到公司的满意。

2018年9月,快临近培训任务时间,提前了三天去公司,第一天小范围试讲了一个上午,下午自己修改调整,晚上公司技术部主管陪我一起修改第一篇PPT,帮我指出问题,整理结构,将原来四篇PPT整合成两篇,我们一直忙到晚上10点。人好像是打了兴奋剂,内心也很忐忑,回到酒店继续修改课件到临晨1点左右,早上6点不到就醒了,翻身起床干活。就这样上午修改课件,下午试讲4~5个小时,中途大家给我提意见、找毛病,晚上回去整理调整,连续4天每天睡觉不足5个小时,但人还是很亢奋,最后终于以波澜不惊状态完成了两场培训,每次3个小时左右。

之后的三个多月中,断断续续接到了公司三次培训任务,我在空闲时间继续更新公众号、接点小外包、帮助朋友公司搭框架及CocosCreator的入指导,这段时间算是有了基本的收保障,但是极其不稳定,还好家人理解,看我虽然没正经上班,但是看我平时工作比上班还忙,暂时还能维持生存,虽然心中有抱怨,没有给我太大的压力,在此感动不已。

5. 被动收入

2018年11月底,年前的最后一次培训任务,此后的时间我将没有持续的收入,心中还感觉有些慌张。平时一直在读安晓辉老师的文章,安老师的一篇知乎live标题打动了我《如何构建被动收,赢得自由生活》,通过这次live的学习,以及安老师提供的建议,我下来开始筹备被动收入计划。

首先我注册了creator-star.cn的域名,搭建了「奎特尔星球」博客网站,公众号、网站相互关联。

然后我又在GitChat开了一场题目为《集合类游戏模块化与热更新实践 》,由于经验不足,又是自己的第一篇付费文章,不小心把题目写大了,写这篇文章整整用了10天的工作时间,终于按时交稿,这场Chat目前挣了近400块,也让更多人认识了我,为公众号带来了更多的关注量。

GitChat

一场GitChat毕竟是杯水车薪,网站在短期内也不会有什么收益,心又开始不安起来。2018年还剩下最后10天时间,我又重新拾起之前计划要做的小游戏《奎特尔数字大冒险》,联系上愿意低价帮助我的美术同学,为我的小游戏制作了一版游戏UI,曾经设计动画的同事提供我一个角色动画,就这样每天只睡5~6个小时,全身心投入到小游戏的开发,终于在2019年元旦后上线微信小游戏,下面是游戏视频演示连接:

《奎特尔数字大冒险》是一款数学小游戏,通过将怪兽身上的数字加起来,然后在答题板上手写数字,消灭来袭的怪兽,用以锻炼数学运算与工作记忆能力。

看下面的gif演示:
boss战

BOSS战,需要同时计算两道题,BOSS还会旋转干扰,考验瞬间记忆力,在小怪与BOSS之间暂存记忆、切换记忆。

dmx.gif

工作记忆是一种对信息进行暂时加工和贮存的容量有限的记忆系统,在许多复杂的认知活动中起重要作用。1974年,Baddeley和Hitch在模拟短时记忆障碍的实验基础上提出了工作记忆的三系统概念,用“工作记忆”(working memory,WM)替代原来的“短时记忆”(short-term memory, STM)概念。此后,工作记忆和短时记忆有了不同的意义和语境。

小游戏上线第四天就拿到了流量主权限(估计被系统扶持了),开通了复活激励视频广告,但游戏太过简陋又费脑子,直到前几天我才意识到《奎特尔数字大冒险》还并不能算是一个游戏,而是游戏化的脑力训练工具,因此活跃度非常低,上线20天广告收看下图:

小游戏广告收

调整好心态,目前自己的小游戏是凭着自己的感觉快速撸出来的,内容少、质量差是事实,接受就好,不过还有进步空间。更重要的是,在这段时间又结识到了不少的同行伙伴,有程序员、有美术、甚至还有公司的邀请,当然还有安晓辉老师,他们都有意向给我提供帮助或合作,这才是最为宝贵的收获。

在2018年是自己最为动荡的一年,9月的一篇公众号文章,我就立下志向:“助力个人和企业,让游戏(编程)成为教育的有力武器”

这一年起起落落,如何养得此心不动,认真做好当下的每一件事,王阳明说过:

举业不患妨功,唯患夺志”,夫谓之“夺志”,则已有“志”可夺;
倘若未有可夺之“志”,却又不可以不深思疑省而早图之。


欢迎关注「奎特尔星球」公众号,来我们一起成长!
奎特尔星球