目录
一、基础知识
1. 编程语言选择:C,C++,java,javascript,lua,流行的是c++,js;多媒体接口DirectX,OpenGl,SDL;
游戏引擎:unity 3d——跨平台(pc、游戏机,作品比如王者荣耀,仙剑6)、虚幻——游戏机、cry engine3——游戏机、白鹭(egret)——html游戏,比如网页游戏、cocos 2d——移动端游戏;
2. 本次使用引擎为CocosCreator(cocos 2d升级版),编程语言js,后台是java。
3.微信对es6的支持:(1)不支持动态执行js代码,即不支持eval执行JS代码、不支持new function创建函数;(2)string.normalize:(ios8 ios9不支持)、array.values(ios8,android不支持、)、array.includes(ios8不支持)、Proxy(ios8、ios9、android不支持)。
ES6数据类型:Number、String 、Boolean、 Object、 null 、undefined和symbol。
(1)symbol的使用场景:
使用symbol来作为对象的属性名,比如 const NAME = Symbol() let obj = {[Name]:'代码'},使用object.keys(obj)和Object.getOwnPropertyNames(obj)不能获取到这个key。对应的API是Object.getOwnPropertySymbols(obj)和Reflect.ownKeys(obj);使用symbol来替代常量。
(2)解构赋值
是对赋值运算符的扩展,针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。主要有基本用法:let [a, b, c] = [1, 2, 3]、let [a, b, c, d, e] = 'hello';可嵌套:let [a, [[b], c]] = [1, [[2], 3]];忽略:let [a, , b] = [1, 2, 3]; 剩余运算符:let [a, ...b] = [1, 2, 3]、let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};默认值:let [a = 3, b = a] = []、let {a = 10, b = 5} = {a: 3};
(3)函数
命名函数:function f1(){}
匿名函数:function (){}
自执行函数:(函数)(实参)——(function (n1,n2){console.log("这是匿名函数的自执行的第一种写法,结果为:"+(n1+n2))})(10,100) ; (函数(实参))——(function (n1,n2){console.log("这是匿名函数的自执行的第二种写法,结果为:"+(n1+n2))}(10,100))
箭头函数:
基本用法——let f = x=>x+x f(100) 200;当箭头函数要返回对象的时候,为了区分于代码块,要用 **()** 将对象包裹起来:let f = (id,name) => ({id: id, name: name});
箭头函数的this:箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象,首先从它的父级作用域中找,如果父级作用域还是箭头函数,再往上找,如此直至找到this的指向。
Math函数:随机数——Math.random();向上取整——Math.ceil();向下取整——Math.floor();四舍五入——Math.round();绝对值——Math.abs();开方:let z = Math.sqrt(x * x + y * y);反正弦:let angle = Math.asin(y / z) / Math.PI * 180;幂计算:Math.pow(10,2) = 100
(4)Map&Set
①Map
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值;
Map 和 Object 的区别:一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值;Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是;Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
②Set
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Set 中的特殊值:Set 对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:+0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复;undefined 与 undefined 是恒等的,所以不重复;NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个,不重复。
Array 转 Set:let mySet = new Set(["value1", "value2", "value3"]);Set 转 Array:let myArray = [...mySet];String 转 Set:let mySet = new Set('hello'); // Set(4) {"h", "e", "l", "o"}
数组去重:let mySet = new Set([1, 2, 3, 4, 4]); [...mySet];
并集:let a = new Set([1, 2, 3]);let b = new Set([4, 3, 2]); let union = new Set([...a, ...b]);
交集:let intersect = new Set([...a].filter(x => b.has(x)))
差集:let difference = new Set([...a].filter(x => !b.has(x)));
(5)面向对象编程
ES6是支持class关键字的:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
cocos creator需要向下兼容,采用的是借助函数允许嵌套的机制来实现类的
function createPerson(name){
//1、原料
let obj=new Object();
//2、加工
obj.name=name;
obj.showName=function(){
alert(this.name);
}
//3、出场
return obj;
}
let p1=createPerson('播仔');
p1.showName();
// 或者
function CreatePerson(name){
this.name=name;
this.showName=function(){
alert(this.name);
}
}
let p1=new CreatePerson('播仔');
(6)横竖屏切换
cocos creator默认窗口是横屏的,我们做项目需要改回竖屏的,可以使用下面步骤更改:
- 项目设置-切换到项目预览选项,修改为640*960,保存
- 修改canvas,设置宽高Design Resolution为640*960
二. cocos creator
1. 项目文件夹功能介绍:
assets:放置本地资源、脚本和第三方库文件。另外版本控制文件需要一并提交到版本控制系统。
library:assets导入后自动生成的,删除文件夹可重新生成。
local:布局信息,可使用菜单-布局-恢复
package:扩展插件文件夹
settings:项目相关设置
temp:cocos creator临时文件夹
project.json:和assets一起作为验证cocos creator项目合法的标志。内容不需要关心
构建目标(build):项目-构建发布,软件自动创建build目录、
使用:设置-数据编辑,修改外部编辑器,选择webstorm64.exe,双击脚本就可以使用外部编辑器了;项目设置-模块预览-分辨率改为竖屏分辨率。
2. 基本概念
(1)场景:树形结构,由各种层次关系的节点组成
(2)节点和组件:工作流程的核心,组件式架构又称作组件-实体进系统,即以组件而非继承的方式进行实体的构建。节点承载组件的实体,组件挂载节点,一个节点可以挂载多个组件(js脚本)。
(3)坐标:右手坐标系,向右为X轴正方向,向上为Y轴正方向,延屏幕向外是Z轴正方向。
(4)事件响应:
①触摸事件(API):
this.node.on:`TOUCH_START` 当手指触摸到屏幕时; `TOUCH_MOVE` 当手指在屏幕上移动时;`TOUCH_END` 当手指在目标节点区域内离开屏幕时;`TOUCH_CANCEL` 当手指在目标节点区域外离开屏幕时。具体可参看:Node · Cocos Creator
事件的三个阶段和事件冒泡:鼠标或触摸会被系统调用dispachEvent方法触发,触发的过程包括捕获(_getCapturingTargets)、目标、冒泡阶段(_getBubblingTargets,stopPropagation拦截)。
②键盘事件:
事件类型:KEY_DOWN和KEY_UP
事件类型回调方法:cc.systemEvent.on,参看SystemEvent · Cocos Creator
③自定义事件:
设置监听事件
this.node.on("fire",function (msg) {
cc.log("自定义事件:","fire",msg)
},this);
发射事件,两种方式:emit和dispatchEvent
// 1. emit方式,只能派送给自己
this.node.emit("fire","开火!");
// 2. dispatchEvent方式,EventCustom参数2--> 表示事件是否向上传递
let msg = new cc.Event.EventCustom("fire",true);
msg.detail = "远程开火!"
this.node.dispatchEvent(msg)
3.游戏资源管理
(1)图片资源
单张图片导入:首先拖动到assets下,接下来可以通过拖动到Canvas下,也可以新建渲染节点,比如sprite精灵,将图片拖动到右侧属性编辑器sprite frame里面。
图集资源(Atlas,Sprite Sheet)导入:图集文件是同名的 **plist** 和 **png**,将这两个文件同时拖拽到 **资源管理器** 中,就可以生成可以在编辑器和脚本中使用的图集资源了,工具比如TexturePacker、Zwoptex。
(2)声音资源
两种加载方式:WebAudio、DomAudio方式。WebAudio优点是兼容性好,问题比较少,缺点是占用的内存资源过多;比较大的音频如背景音乐,建议使用 DOM Audio。
(3)预制体(Prefabs)
这个概念是引自Unity引擎的,它是一种可以被重复使用的游戏对象。将编辑好的节点从层级管理器中拖到资源管理器就可以创建预制体了。
(4)字体资源
三类字体资源:系统字体(Use System Font),动态字体(TTF格式的字体资源)和位图字体(由fnt格式的字体文件和一张png图片组成,工具比如bmfont、hiero)
(5)粒子系统
按照预先的设计不断产生新的粒子,每个粒子不断地随机变化运动,这样叠加的宏观效果就是粒子系统,可以达到栩栩如生的模拟视觉效果。特点:包含大量物理微粒对象(粒子)、宏观特性上每一个粒子都符合主要的物理规律、微观特性上在符合规律的基础上,每个粒子都有自己的随机性和独特性、过程动态特性上每一个粒子都是动态的,在移动中不断变化,在每个模拟中都是不断自己更新自己的。工具比如Particle Designer、www.effecthub.com在线制作。
使用:直接将 **plist**资源拖到**资源管理器** 中,就可以使用了;或者创建渲染节点-粒子系统,修改file为自己的plist文件,点击同步按钮,勾掉custom选框。
(6)Tiled地图集资源
在手机游戏的开发中,为了节约内存空间,一般使用图素拼接的方法组成整个地图。Tiled地图编辑器的下载地址为http://www.mapeditor.org/。地图所需资源有.tmx 地图数据和.png 图集纹理。
使用:cocos creator中使用地图资源,直接拖动,或者将地图资源拖动到已创建 TiledMap 组件的 Tmx File 属性中。
(7)骨骼动画
骨骼动画资源是由 [Spine](http://zh.esotericsoftware.com/) 所导出的数据格式。骨骼动画的特点是,需要做动画的物体对象本身不记录位移、旋转、缩放、变形信息,而是通过了第三方的“骨骼”物体记录动画信息,然后物体对象本身只记录受到骨骼物体影响的权重。在播放的时候,通过骨骼物体的关键帧和物体对象记录的权重,让动画重现。骨骼动画所需资源有`.json/.skel` 骨骼数据、`.png` 图集纹理、`.txt/.atlas` 图集数据。
使用:将骨骼动画资源拖动到 **层级管理器**或**场景编辑器**;从 **资源管理器** 中将骨骼动画资源(json文件)拖动到 **属性检查器** Spine 组件的 Skeleton Data 属性中。修改属性检查器的Animation选项,就可以看到动画了。
三.脚本编程
Cocos Creator 的脚本主要是通过扩展组件来进行开发的,支持**JavaScript** 和 TypeScript 两种脚本语言。通过编写脚本组件,并将它赋予到场景节点中来驱动场景中的物体。可以通过声明属性,将脚本中需要调节的变量映射到 **属性检查器**(Properties) 中,供策划和美术调整。于此同时,你也可以通过注册特定的回调函数,来帮助你初始化,更新甚至销毁节点。
1. 节点和组件
(1)创建和使用脚本组件
在资源编辑器中通过点击"创建"按钮来添加并选择 JavaScript创建一份组件脚本。将脚本添加到场景节点中,实际上就是为这个节点添加一份组件,在 **属性检查器** 的最下方有一个 **添加组件** 的按钮,点击按钮并选择 **添加用户脚本 ->xx.js** 添加编写的脚本组件。
(2)使用 cc.Class 声明类型
`cc.Class` 是一个很常用的 API,用于声明 Cocos Creator 中的类,把使用 cc.Class 声明的类叫做 **CCClass**。 具体用法如下:
①定义一个 CCClass
调用 `cc.Class`,传入一个原型对象,在原型对象中以键值对的形式设定所需的类型参数,就能创建出所需要的类。以下代码用 cc.Class 创建了一个类型,并且赋给了 `Sprite` 变量。同时还将类名设为 "sprite",类名用于序列化,一般可以省略。
let Sprite = cc.Class({
name: "sprite",
});
②实例化
`Sprite` 变量保存的是一个 JavaScript 构造函数,可以直接 new 出一个对象:
let obj = new Sprite();
③判断类型
cc.log(obj instanceof Sprite);
④实例方法
let Sprite = cc.Class({
// 声明一个名叫 "print" 的实例方法
print: function () { }
});
⑤继承
let Rect = cc.Class({
extends: Sprite
});
// 最终合并的代码如下:
let Sprite = cc.Class({
name:"sprite",
print:function (){
cc.log("print function")
}
});
let SpriteSon = cc.Class({
extends:Sprite
})
cc.Class({
// ... ...
start () {
let sprite = new Sprite()
cc.log(sprite)
cc.log(sprite instanceof Sprite)
sprite.print()
let spriteSon = new SpriteSon()
spriteSon.print()
},
})
(3)声明自定义属性
当声明的属性为基本 JavaScript 类型时,可以直接赋予默认值;当声明的属性具备类型时(如:`cc.Node`,`cc.Vec2` 等),可以在声明处填写他们的构造函数来完成声明。
properties: {
height:20,
type:'actor',
target: cc.Node,
pos: cc.Vec2,
any: [],
frames: [cc.SpriteFrame],
bools: [cc.Boolean],
score: {
default: 0,
displayName: "得分",
tooltip: "设置玩家初始得分",
}
当声明属性是一个数组时,可以在声明处填写他们的类型或构造函数来完成声明,如:
properties: {
any: [], // 不定义具体类型的数组
bools: [cc.Boolean],
strings: [cc.String],
floats: [cc.Float],
ints: [cc.Integer],
values: [cc.Vec2],
nodes: [cc.Node],
frames: [cc.SpriteFrame],
}
有些情况下,需要为属性声明添加参数,这些参数控制了属性在 **属性检查器** 中的显示方式,以及属性在场景序列化过程中的行为。 以下为常用参数:
- **default**: 设置属性的默认值,这个默认值仅在组件**第一次**添加到节点上时才会用到
- **type**: 限定属性的数据类型
- **visible**: 设为 false 则不在 **属性检查器** 面板中显示该属性
- **serializable**: 设为 false 则不序列化(保存)该属性
- **displayName**: 在 **属性检查器** 面板中显示成指定名字
- **tooltip**: 在 **属性检查器** 面板中添加属性的 Tooltip
当参数为数组时,default 必须设置为 `[]`,属性编辑器参数后面的值为长度,修改长度,回车即可显示数组子项。
(4)脚本生命周期的回调函数
目前提供给用户的生命周期回调函数主要有onLoad- onEnable- start- update- lateUpdate- onDisable- onDestroy
onLoad:回调会在节点首次激活时触发,比如所在的场景被载入,或者所在节点被激活的情况下。在 `onLoad` 阶段,保证了你可以获取到场景中的其他节点,以及节点关联的资源数据。onLoad 总是会在任何 start 方法调用前执行,这能用于安排脚本的初始化顺序。通常我们会在 `onLoad` 阶段去做一些初始化相关的操作。
onEnable:当组件的 `enabled` 属性从 `false` 变为 `true` 时,或者所在节点的 `active` 属性从 `false` 变为 `true`时,会激活 `onEnable` 回调。倘若节点第一次被创建且 `enabled` 为 `true`,则会在 `onLoad` 之后,`start` 之前被调用。
start:`start` 回调函数会在组件第一次激活前,也就是第一次执行 `update` 之前触发。`start` 通常用于初始化一些中间状态的数据,这些数据可能在 update 时会发生改变,并且被频繁的 enable 和 disable。
update:游戏开发的一个关键点是在每一帧渲染前更新物体的行为,状态和方位。这些更新操作通常都放在 `update`回调中。以下控制节点的y轴位置往上,主要体现在FPS。参数dt是距离上一帧的时间,1/FPS。
cc.Class({
extends: cc.Component,
update: function (dt) {
this.node.y+=1;
}
});
lateUpdate:`update` 会在所有动画更新前执行,但如果我们要在动效(如动画、粒子、物理等)更新之后才进行一些额外操作,或者希望在所有组件的 `update` 都执行完之后才进行其它操作,那就需要用到 `lateUpdate` 回调。
lateUpdate: function (dt) {
this.node.rotation += 20;
}
onDisable:当组件的 `enabled` 属性从 `true` 变为 `false` 时,或者所在节点的 `active` 属性从 `true` 变为 `false`时,会激活 `onDisable` 回调。
onDestroy:当组件或者所在节点调用了 `destroy()`,则会调用 `onDestroy` 回调,并在当帧结束时统一回收组件。该方法先调用onDisable,再调用OnDestroy。
(5)访问节点和其他组件
①获得组件所在的节点:访问 `this.node` 变量即可,let node = this.node
②获得当前节点下的其它组件:用 `getComponent` 这个 API,查找想要的组件。let label = this.getComponent(cc.Label);// 其中cc.Label就是要查找的组件。
③获得其它节点及其下的组件:仅仅能访问节点自己的组件通常是不够的,脚本通常还需要进行多个节点之间的交互。比如一门自动瞄准玩家的大炮,就需要不断获取玩家的最新位置。
**使用属性检查器设置节点**:在 `properties` 里面声明了一个 `player` 属性,默认值为 null,并且指定它的对象类型为 `cc.Node`,然后将层级管理器上的任意一个节点拖到这个 Player 控件, player 属性就会被设置成功,能能够直接在脚本里访问 player。
// cannon.js
cc.Class({
extends: cc.Component,
properties: {
// 声明 player 属性
player: {
default: null,
type: cc.Node
}
},
start: function () {
cc.log("The player is " + this.player.name);
let playerComp = this.player
playerComp.fire();
},
// ...
});
这段代码在 properties
里面声明了一个 player
属性,默认值为 null,并且指定它的对象类型为 cc.Node
。这就相当于在其它语言里声明了 public cc.Node player = null;
。脚本编译之后,就可以将层级管理器上的任意一个节点拖到这个 Player 控件,它的 player 属性就会被设置成功,可以直接在脚本里访问 player。如果在player.js里面定义一个开炮的方法:
// player.js
cc.Class({
extends: cc.Component,
properties: {
},
start () {
},
fire(){
cc.log("fire!")
}
});
在cannon.js里面就可以调用了。另外将属性的默认值由 `null` 改为数组 `[]`,就能在 **属性检查器** 中同时设置多个对象。
④查找子节点:游戏场景中会有很多个相同类型的对象,像是炮塔、敌人和特效,为了更好地统一管理这些对象,我们可以把它们放到一个统一的父物体下,然后通过父物体来获得所有的子物体。API有:let children = this.node.children;this.node.getChildByName("Cannon");如果子节点的层次较深,你还可以使用 `cc.find`,`cc.find` 将根据传入的路径进行逐级查找,当 `cc.find` 只传入第一个参数时,将从场景根节点开始逐级查找:cc.find("Cannon/Sub", this.node);全局名称查找,只传入第一个参数时,将从场景根节点开始逐级查找:this.backNode = cc.find("Canvas/Hello/Cannon");
⑤常用节点和组件接口:
激活/关闭节点:this.node.active = false;当节点是关闭状态时,它的所有组件都将被禁用。
若节点原先就处于 **可被激活** 状态,修改 `active` 为 true 就会立即触发激活操作,在场景中重新激活该节点和节点下所有 active 为 true 的子节点,该节点和所有子节点上的所有组件都会被启用,他们中的 `update` 方法之后每帧会执行,这些组件上如果有 `onEnable` 方法,这些方法将被执行。
如该节点原先就已经被激活,修改 `active` 为 false 就会立即触发关闭操作,在场景中隐藏该节点和节点下的所有子节点,该节点和所有子节点上的所有组件都将被禁用,也就是不会再执行这些组件中的 `update` 中的代码,这些组件上如果有 `onDisable` 方法,这些方法将被执行。可以通过节点上的只读属性 `activeInHierarchy`来判断它当前是否已经激活
更改节点的父节点:this.node.parent = parentNode; 或者:
this.node.removeFromParent(false);
parentNode.addChild(this.node);
注意`removeFromParent` 通常需要传入一个 `false`,否则默认会清空节点上绑定的事件和 action 等。通过 创建和销毁节点 介绍的方法创建出新节点后,要为节点设置一个父节点才能正确完成节点的初始化。
索引节点的子节点:`this.node.children` 将返回节点的所有子节点数组。`this.node.childrenCount` 将返回节点的子节点数量。以上两个 API 都只会返回节点的直接子节点,不会返回子节点的子节点。
更改节点的变换:主要有位置、旋转、缩放、尺寸变换。
****更改节点位置****:以下都是相对于父节点的位置变换。
// 方法一
this.node.x = 100;
this.node.y = 50;
// 方法二
this.node.setPosition(100, 50);
this.node.setPosition(cc.v2(100, 50));
****更改节点旋转****:逆时针旋转,该节点以及以下子节点同时旋转。
// 方法一
this.node.rotation = 90;
// 方法二
this.node.setRotation(90);
****更改节点缩放****:setScale` 传入单个参数时,会同时修改 `scaleX` 和 `scaleY`
// 方法一
this.node.scaleX = 2;
this.node.scaleY = 2;
// 方法二
this.node.setScale(2);
this.node.setScale(2, 2);
****更改节点尺寸****:
// 方法一
this.node.setContentSize(100, 100);
this.node.setContentSize(cc.size(100, 100));
// 方法二
this.node.width = 100;
this.node.height = 100;
****更改节点锚点位置****:
// 方法一
this.node.anchorX = 1;
this.node.anchorY = 0;
// 方法二
this.node.setAnchorPoint(1, 0);
****颜色和不透明度****:
this.node.color = cc.Color.RED;
this.node.opacity = 128; // 255是完全不透明
其他常用组件接口:
`this.node`:该组件所属的节点实例;
`this.enabled`:是否每帧执行该组件的 `update` 方法,同时也用来控制渲染组件是否显示;`update(dt)`:作为组件的成员方法,在组件的 `enabled` 属性为 `true` 时,其中的代码会每帧执行;
`onLoad()`:组件所在节点进行初始化时(节点添加到节点树时)执行;
`start()`:会在该组件第一次 `update` 之前执行,通常用于需要在所有组件的 `onLoad` 初始化完毕后执行的逻辑。
⑥创建和销毁节点
**动态创建节点**:通过 `new cc.Node()` 并将它加入到场景中,可以实现整个创建过程;另外如果使用cc.loader.loadRes动态加载图片,图片必须放在resources文件夹里面。以下‘sheep’就是相对于resources的路径
cc.Class({
extends: cc.Component,
properties: {
},
start: function () {
let self = this;
// 加载资源,spriteFrame即加载的资源对象
cc.loader.loadRes("sheep",cc.SpriteFrame,function (err,spriteFrame) {
let node = new cc.Node("sprite");
let sp = node.addComponent(cc.Sprite);
sp.spriteFrame = spriteFrame;
node.parent = self.node;
node.setContentSize(100,100);
});
},
});
**克隆已有节点**:通过 `cc.instantiate` 方法完成。要拖动层次管理器里面的节点加入节点到target里面。
cc.Class({
extends: cc.Component,
properties: {
target: {
default: null,
type: cc.Node,
},
},
start: function () {
var node = cc.instantiate(this.target);
node.parent = this.node;
node.setPosition(0, 0);
},
});
**创建预制节点**
和克隆已有节点相似,你可以设置一个预制(Prefab)并通过 `cc.instantiate` 生成节点。要拖动资源管理器里面的预制体加入节点到target里面。
cc.Class({
extends: cc.Component,
properties: {
target: {
default: null,
type: cc.Prefab,
},
},
start: function () {
var node = cc.instantiate(this.target);
node.parent = this.node;
node.setPosition(0, 0);
},
});
**销毁节点**:
**** `node.destroy()` 函数****,可以销毁节点。销毁节点并不会立刻被移除,而是在当前帧逻辑更新结束后,统一执行。当一个节点销毁后,该节点就处于无效状态,可以通过 `cc.isValid` 判断当前节点是否已经被销毁。
****destroy 和 removeFromParent 的区别****:调用一个节点的 `removeFromParent` 后,它不一定就能完全从内存中释放,因为有可能由于一些逻辑上的问题,导致程序中仍然引用到了这个对象。因此如果一个节点不再使用了,请直接调用它的 `destroy` 而不是 `removeFromParent`。`destroy` 不但会激活组件上的 `onDestroy`,还会降低内存泄露的几率,同时减轻内存泄露时的后果。总之,如果一个节点不再使用,`destroy` 就对了,不需要 `removeFromParent` 也不需要设置 `parent` 为 `null`。
cc.Class({
extends: cc.Component,
properties: {
target: cc.Node,
},
start: function () {
// 5 秒后销毁目标节点
setTimeout(function () {
this.node.destroy();
}.bind(this), 5000);
},
update: function (dt) {
if (cc.isValid(this)) {
this.target.rotation += 10.0;
}
},
2.加载和切换场景
在Cocos Creator的脚本中,可以使用导演类的loadScene方法加载场景,loadScene可以传递一个或两个参数,第一个参数是场景名称,第二个参数是场景加载完毕后的回调函数,场景加载完毕后的回调函数可以进行必要的初始化或数据传递操作。
(1)使用
// 常规方式
cc.director.loadScene("MyScene");
// 预加载的方式
cc.director.preloadScene("table", function () {
cc.log("Next scene preloaded");
});
(2)通过常驻节点进行场景资源管理和参数传递
引擎同时只会运行一个场景,当切换场景时,默认会将场景内所有节点和其他实例销毁。如果我们需要用一个组件控制所有场景的加载,或在场景之间传递参数数据,就需要将该组件所在节点标记为「常驻节点」,使它在场景切换时不被自动销毁,常驻内存。
// 设置常驻场景
cc.game.addPersistRootNode(myNode);
// 取消场主场景
cc.game.removePersistRootNode(myNode);
`myNode` 设置为常驻节点,挂在上面的组件都可以在场景之间持续作用,可以用这样的方法来储存类似玩家信息,或下一个场景初始化时需要的各种数据。
三.资源管理
在 Creator 中,所有继承自 `cc.Asset` 的类型都统称资源如 `cc.Texture2D`, `cc.SpriteFrame`, `cc.AnimationClip`, `cc.Prefab` 等。它们的加载是统一并且自动化的,相互依赖的资源能够被自动预加载。
(1)、替换单色精灵组件背景
节点管理器新建单色渲染节点,关联资源管理器新建的js脚本(sprite_frame拖拽添加图片资源)。js脚本设置如下:
// say-hello.js
cc.Class({
extends: cc.Component,
properties: {
sprite_frame:{
default: null,
type:cc.SpriteFrame
}
},
onLoad () {
let sprite = this.node.getComponent(cc.Sprite)
sprite.spriteFrame = this.sprite_frame;
},
});
在 **属性检查器** 里设置资源虽然很直观,但资源只能在场景里预先设好,没办法动态切换。
(2)、动态加载资源
`cc.loader.loadRes` 专门加载那些位于 resources 目录下的 Asset。调用时,传入相对 resources 的路径即可,并且路径的结尾处 **不能包含文件扩展名**。
onLoad () {
// Player为预制体
let self = this;
cc.loader.loadRes("Player",function (error, prefab){
let newNode = cc.instantiate(prefab);
newNode.setPosition(100,100)
self.node.addChild(newNode);
})
},
图片设置为 Sprite 后,将会在 **资源管理器** 中生成一个对应的 SpriteFrame。但如果直接加载 `test assets/image`,得到的类型将会是 cc.Texture2D。你必须指定第二个参数为资源的类型,才能加载到图片生成的 cc.SpriteFrame:
// 加载 SpriteFrame
let self = this;
cc.loader.loadRes("sheep", cc.SpriteFrame, function (err, spriteFrame) {
self.node.getComponent(cc.Sprite).spriteFrame = spriteFrame;
});
(3). 资源的释放
`loadRes` 加载进来的单个资源如果需要释放,可以调用 `cc.loader.releaseRes`,`releaseRes` 可以传入和 `loadRes` 相同的路径和类型参数。
cc.loader.releaseRes("sheep", cc.SpriteFrame);
cc.loader.releaseRes("sheep");
// 释放特定的 Asset 实例
cc.loader.releaseAsset(spriteFrame);
四、使用动作系统
Cocos Creator 提供的动作系统可以在一定时间内对节点完成位移,缩放,旋转等各种动作。
Cocos引擎的Action动作类并不是一个在屏幕中显示的对象,动作必须要依托于Node节点类及它的子类的实例才能发挥它的作用,Cocos中的动作不仅包括位置移动等,还包括跳跃、旋转,甚至是对象透明度的变化和颜色的渐变。这些基本动作可以构成各种复杂的动作,也可以通过sequence形成一个完整的动作序列。
(1)基本API
// 创建一个移动动作 2FPS之后移动到100,100的位置
let action = cc.moveTo(2, 100, 100);
// 执行动作
this.node.runAction(action);
// 停止一个动作
this.node.stopAction(action);
// 停止所有动作
this.node.stopAllActions();
(2)动作类型
①基础动作:比如cc.moveTo、cc.rotateBy、cc.scaleTo等;基础动作分为时间间隔动作和即时动作,前面提到的都是时间间隔动作,它们全部继承自 cc.ActionInterval;后者则是立即发生的,比如用来调用回调函数的 cc.callFunc;用来隐藏节点的 cc.hide,它们全部继承自 cc.ActionInstant。
②容器动作:以不同的方式将动作组织起来
**顺序动作 `cc.sequence`**:让一系列子动作按顺序一个个执行
// 让节点左右来回移动
let seq = cc.sequence(cc.moveBy(0.5, 200, 0), cc.moveBy(0.5, -200, 0));
node.runAction(seq);
**同步动作 `cc.spawn`**:同步动作可以同步执行对一系列子动作,子动作的执行结果会叠加起来修改节点的属性。
// 让节点在向上移动的同时缩放
let spawn = cc.spawn(cc.moveBy(0.5, 0, 50), cc.scaleTo(0.5, 0.8, 1.4));
node.runAction(spawn);
**重复动作 `cc.repeat`**:用来多次重复一个动作。
// 让节点左右来回移动,并重复5次
let seq = cc.repeat(
cc.sequence(cc.moveBy(2, 200, 0),cc.moveBy(2, -200, 0)), 5);
node.runAction(seq);
** 永远重复动作 `cc.repeatForever` **:这个动作容器可以让目标动作一直重复,直到手动停止。
// 让节点左右来回移动并一直重复
let seq = cc.repeatForever(
cc.sequence(cc.moveBy(2, 200, 0),cc.moveBy(2, -200, 0))
);
**速度动作 `cc.speed`**:速度动作可以改变目标动作的执行速率,让动作更快或者更慢完成。
// 让目标动作速度加快一倍,相当于原本2秒的动作在1秒内完成
let action = cc.speed(
cc.spawn(
cc.moveBy(2, 0, 50),
cc.scaleTo(2, 0.8, 1.4)
), 2);
node.runAction(action);
链式 API,动作对象支持以下三个 API:`repeat`、`repeatForever`、`speed`,这些 API 都会返回动作对象本身,支持继续链式调用。
// 一个复杂的跳跃动画
this.jumpAction = cc.sequence(
cc.spawn(
cc.scaleTo(0.1, 0.8, 1.2),
cc.moveTo(0.1, 0, 10)
),
cc.spawn(
cc.scaleTo(0.2, 1, 1),
cc.moveTo(0.2, 0, 0)
),
cc.delayTime(0.5),
cc.spawn(
cc.scaleTo(0.1, 1.2, 0.8),
cc.moveTo(0.1, 0, -10)
),
cc.spawn(
cc.scaleTo(0.2, 1, 1),
cc.moveTo(0.2, 0, 0)
)
// 以1/2的速度慢放动画,并重复5次
).speed(2).repeat(5);
(3)动作回调
用以下的方式声明:let finished = cc.callFunc(this.myMethod, this, opt),第一个参数是处理回调的方法,即可以使用 CCClass 的成员方法,也可以声明一个匿名函数;第二个参数指定了处理回调方法的 context(也就是绑定 this),第三个参数是向处理回调方法的传参。
let finished = cc.callFunc(function(target, score) {
cc.log("动作回调");
}, this, 100);
声明的回调动作 `finished`可以配合 `cc.sequence` 来执行一整串动作并触发回调。
let myAction = cc.sequence(cc.moveBy(1, cc.v2(0, 100)), cc.fadeOut(1), finished);
在同一个 sequence 里也可以多次插入回调:
let myAction = cc.sequence(cc.moveTo(1, cc.v2(0, 0)), finished1, cc.fadeOut(1), finished2);
由于动作是不能被立即删除,在 cc.callFunc 中不应该停止自身动作。