加载预置
instantitate(); //加载prfab
获得组件所在的节点
获得组件所在的节点很简单,只要在组件方法里访问 this.node 变量:
start: function () {
var node = this.node;
node.x = 100;
}
获得其它组件
你会经常需要获得同一个节点上的其它组件,这就要用到 getComponent 这个 API,它会帮你查找你要的组件。
start: function () {
var label = this.getComponent(cc.Label);
var text = this.name + ' started';
// Change the text in Label Component
label.string = text;
}
你也可以为 getComponent 传入一个类名。
var label = this.getComponent("cc.Label");
对用户定义的组件而言,类名就是脚本的文件名,并且区分大小写。例如 “SinRotate.js” 里声明的组件,类名就是 “SinRotate”。
var rotate = this.getComponent("SinRotate");
在节点上也有一个 getComponent 方法,它们的作用是一样的:
start: function () {
cc.log( this.node.getComponent(cc.Label) === this.getComponent(cc.Label) ); // true
}
如果在节点上找不到你要的组件,getComponent 将返回 null,如果你尝试访问 null 的值,将会在运行时抛出 “TypeError” 这个错误。因此如果你不确定组件是否存在,请记得判断一下:
start: function () {
var label = this.getComponent(cc.Label);
if (label) {
label.string = "Hello";
}
else {
cc.error("Something wrong?");
}
}
利用属性检查器设置节点
最直接的方式就是在 属性检查器 中设置你需要的对象。以节点为例,这只需要在脚本中声明一个 type 为 cc.Node 的属性:
// Cannon.js
cc.Class({
extends: cc.Component,
properties: {
// 声明 player 属性
player: {
default: null,
type: cc.Node
}
}
});
这段代码在 properties 里面声明了一个 player 属性,默认值为 null,并且指定它的对象类型为 cc.Node。这就相当于在其它语言里声明了 public cc.Node player = null;。脚本编译之后,这个组件在 属性检查器 中看起来是这样的:
接着你就可以将层级管理器上的任意一个节点拖到这个 Player 控件:
start: function () {
var playerComp = this.player.getComponent(Player);
this.checkPlayer(playerComp);
},
利用属性检查器设置组件
在上面的例子中,如果你将属性的 type 声明为 Player 组件,当你拖动节点 “Player Node” 到 属性检查器,player 属性就会被设置为这个节点里面的 Player 组件。这样你就不需要再自己调用 getComponent 啦。
properties: {
// 声明 player 属性,这次直接是组件类型
player: {
default: null,
type: Player
}
},
start: function () {
var playerComp = this.player;
this.checkPlayer(playerComp);
},
你还可以将属性的默认值由 null 改为数组[],这样你就能在 属性检查器 中同时设置多个对象。
不过如果需要在运行时动态获取其它对象,还需要用到下面介绍的查找方法。
查找子节点
有时候,游戏场景中会有很多个相同类型的对象,像是炮塔、敌人和特效,它们通常都有一个全局的脚本来统一管理。如果用 属性检查器 来一个一个将它们关联到这个脚本上,那工作就会很繁琐。为了更好地统一管理这些对象,我们可以把它们放到一个统一的父物体下,然后通过父物体来获得所有的子物体:
cc.Class({
extends: cc.Component,
start: function () {
var cannons = this.node.children;
// ...
}
});
你还可以使用 getChildByName:
this.node.getChildByName("Cannon 01");
如果子节点的层次较深,你还可以使用 cc.find,cc.find 将根据传入的路径进行逐级查找:
cc.find("Cannon 01/Barrel/SFX", this.node);
全局名字查找
当 cc.find 只传入第一个参数时,将从场景根节点开始逐级查找:
this.backNode = cc.find("Canvas/Menu/Back");
通过全局变量访问
你应当很谨慎地使用全局变量,当你要用全局变量时,应该很清楚自己在做什么,我们并不推荐滥用全局变量,即使要用也最好保证全局变量只读。
让我们试着定义一个全局对象 window.Global,这个对象里面包含了 backNode 和 backLabel 两个属性。
// Globals.js, this file can have any name
window.Global = {
backNode: null,
backLabel: null,
};
由于所有脚本都强制声明为 “use strict”,因此定义全局变量时的 window. 不可省略。
接着你可以在合适的地方直接访问并初始化 Global:
/
/ Back.js
cc.Class({
extends: cc.Component,
onLoad: function () {
Global.backNode = this.node;
Global.backLabel = this.getComponent(cc.Label);
}
});
初始化后,你就能在任何地方访问到 Global 里的值:
// AnyScript.js
cc.Class({
extends: cc.Component,
// start 会在 onLoad 之后执行,所以这时 Global 已经初始化过了
start: function () {
var text = 'Back';
Global.backLabel.string = text;
}
});
访问全局变量时,如果变量未定义将会抛出异常。
添加全局变量时,请小心不要和系统已有的全局变量重名。
你需要小心确保全局变量使用之前都已初始化和赋值。
通过模块访问
如果你不想用全局变量,你可以使用 require 来实现脚本的跨文件操作,让我们看个示例:
// Global.js, now the filename matters
module.exports = {
backNode: null,
backLabel: null,
};
每个脚本都能用 require + 文件名(不含路径) 来获取到对方 exports 的对象。
// Back.js
// this feels more safe since you know where the object comes from
var Global = require("Global");
cc.Class({
extends: cc.Component,
onLoad: function () {
Global.backNode = this.node;
Global.backLabel = this.getComponent(cc.Label);
}
});
// AnyScript.js
// this feels more safe since you know where the object comes from
var Global = require("Global");
cc.Class({
extends: cc.Component,
// start 会在 onLoad 之后执行,所以这时 Global 已经初始化过了
start: function () {
var text = "Back";
Global.backLabel.string = text;
}
});
引用模块
require
除了 Creator 提供的接口,所有用户定义的模块都需要调用 require 来访问。例如我们有一个组件定义在 Rotate.js:
// Rotate.js
cc.Class({
extends: cc.Component,
// ...
});
现在要在别的脚本里访问它,可以:
var Rotate = require("Rotate");
require 返回的就是被模块导出的对象,通常我们都会将结果立即存到一个变量(var Rotate)。传入 require 的字符串就是模块的文件名,这个名字不包含路径也不包含后缀,而且大小写敏感。
这里我们定义了一个新的组件叫 SinRotate,它继承自 Rotate,并对 update 方法进行了重写。
同样的这个组件也可以被其它脚本接着访问,只要用 require("SinRotate")。
备注:
- require 可以在脚本的任何地方任意时刻进行调用。 游戏开始时会自动 require
- 所有脚本,这时每个模块内部定义的代码就会被执行一次,之后无论又被 require 几次,返回的始终是同一份实例。
- 调试时,可以随时在 Developer Tools 的 Console 中 require 项目里的任意模块。
定义模块
定义组件
每一个单独的脚本文件就是一个模块,例如前面新建的脚本 Rotate.js:
// Rotate.js
var Rotate = cc.Class({
extends: cc.Component,
properties: {
speed: 1
},
update: function () {
this.transform.rotation += this.speed;
}
});
当你在脚本中声明了一个组件,Creator 会默认把它导出,其它脚本直接 require 这个模块就能使用这个组件。
定义普通 JavaScript 模块
模块里不单单能定义组件,实际上你可以导出任意 JavaScript 对象。假设有个脚本 config.js
// config.js
var cfg = {
moveSpeed: 10,
version: "0.15",
showTutorial: true,
load: function () {
// ...
}
};
cfg.load();
现在如果我们要在其它脚本中访问 cfg 对象:
// player.js
var config = require("config");
cc.log("speed is", config.moveSpeed);
结果会有报错:”TypeError: Cannot read property ‘moveSpeed’ of null”,这是因为 cfg 没有被导出。由于 require 实际上获取的是目标脚本内的 module.exports 变量,所以我们还需要在 config.js 的最后设置 module.exports = config:
// config.js - v2
var cfg = {
moveSpeed: 10,
version: "0.15",
showTutorial: true,
load: function () {
// ...
}
};
cfg.load();
module.exports = cfg;
这样 player.js 便能正确输出:”speed is 10”。
module.exports 的默认值:
当你的 module.exports 没有任何定义时,Creator 会自动优先将 exports 设置为脚本中定义的 Component。如果脚本没定义 Component 但是定义了别的类型的 CCClass,则自动把 exports 设为定义的 CCClass。
备注:
- 在 module 上增加的其它变量是不能导出的,也就是说 exports 不能替换成其它变量名,系统只会读取 exports 这个变量。
激活/关闭节点
节点默认是激活的。除了在编辑器中切换节点的激活、关闭状态,也可以通过以下代码:
this.node.active = true;
如该节点的所有父节点原先都已激活,此时执行该行代码就意味着:
在场景中重新激活该节点和所有子节点,除非子节点单独设置过关闭
该节点和所有子节点上的所有组件都会被启用,他们中的 update 方法之后每帧会执行
这些组件上如果有 onEnable 方法,这些方法将被执行
this.node.active = false;
如该节点的任一父节点原先就已关闭,此时执行该行代码就不会触发任何行为。
如该节点的所有父节点原先都已激活,此时执行该行代码就意味着:
在场景中隐藏该节点和所有子节点
该节点和所有子节点上的所有组件都将被禁用,也就是不会再执行这些组件中的 update 中的代码
这些组件上如果有 onDisable 方法,这些方法将被执行
更改节点的父节点
假设父节点为 parentNode,子节点为 this.node
您可以:
this.node.parent = parentNode;
或
this.node.removeFromParent(false);
parentNode.addChild(this.node);
这两种方法是等价的。
注意:
- removeFromParent 通常需要传入一个 false,否则默认会清空节点上绑定的事件和 action 等。
- 通过 创建和销毁节点 介绍的方法创建出新节点后,要为节点设置一个父节点才能正确完成节点的初始化。
常用组件接口
cc.Component 是所有组件的基类,任何组件都包括如下的常见接口(假设我们在该组件的脚本中,以 this 指代本组件):
this.node:该组件所属的节点实例
this.enabled:是否每帧执行该组件的 update 方法,同时也用来控制渲染组件是否显示
update(dt):作为组件的成员方法,在组件的 enabled 属性为 true 时,其中的代码会每帧执行
onLoad():组件所在节点进行初始化时(节点添加到节点树时)执行
start():会在该组件第一次 update 之前执行,通常用于需要在所有组件的 onLoad 初始化完毕后执行的逻辑