Cocos Creator開發中,我們既可以在屬性檢查器裏修改節點和組件,也能在腳本中動態修改。動態修改的好處是能夠在一段時間內連續地修改屬性、過渡屬性,實現漸變效果。腳本還能夠響應玩家輸入,能夠修改、創建和銷燬節點或組件,實現各種各樣的遊戲邏輯。要實現這些效果,你需要先在腳本中獲得你要修改的節點或組件。
在本篇教程,我們將介紹如何:
- 獲得組件所在的節點
- 獲得其它組件
- 使用 屬性檢查器 設置節點和組件
- 查找子節點
- 全局節點查找
- 訪問已有變量裏的值
獲得組件所在的節點
獲得組件所在的節點很簡單,只要在組件方法裏訪問 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 傳入一個類名。對用戶定義的組件而言,類名就是腳本的文件名,並且區分大小寫。例如 "SinRotate.js" 裏聲明的組件,類名就是 "SinRotate"。
var label = this.getComponent("cc.Label");
在節點上也有一個 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?");
}
}
獲得其它節點及其組件
僅僅能訪問節點自己的組件通常是不夠的,腳本通常還需要進行多個節點之間的交互。例如,一門自動瞄準玩家的大炮,就需要不斷獲取玩家的最新位置。Cocos Creator 提供了一些不同的方法來獲得其它節點或組件。
利用屬性檢查器設置節點
最直接的方式就是在 屬性檢查器 中設置你需要的對象。以節點爲例,這只需要在腳本中聲明一個 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;。腳本編譯之後,這個組件在 屬性檢查器 中看起來是這樣的:
http://www.cocos.com/docs/creator/scripting/access-node-component/player-in-inspector-null.png
接着你就可以將層級管理器上的任意一個節點拖到這個 Player 控件:
http://www.cocos.com/docs/creator/scripting/access-node-component/player-in-inspector.png
這樣一來它的 player 屬性就會被設置成功,你可以直接在腳本里訪問 player:
// Cannon.js
var Player = require("Player");
cc.Class({
extends: cc.Component,
properties: {
// 聲明 player 屬性
player: {
default: null,
type: cc.Node
}
},
start: function () {
var playerComp = this.player.getComponent(Player);
this.checkPlayer(playerComp);
},
// ...
});
利用屬性檢查器設置組件
在上面的例子中,如果你將屬性的 type 聲明爲 Player 組件,當你拖動節點 "Player Node" 到 屬性檢查器,player 屬性就會被設置爲這個節點裏面的 Player 組件。這樣你就不需要再自己調用 getComponent 啦。
// Cannon.js
var Player = require("Player");
cc.Class({
extends: cc.Component,
properties: {
// 聲明 player 屬性,這次直接是組件類型
player: {
default: null,
type: Player
}
},
start: function () {
var playerComp = this.player;
this.checkPlayer(playerComp);
},
// ...
});
你還可以將屬性的默認值由 null 改爲數組[],這樣你就能在 屬性檢查器 中同時設置多個對象。
不過如果需要在運行時動態獲取其它對象,還需要用到下面介紹的查找方法。
查找子節點
有時候,遊戲場景中會有很多個相同類型的對象,像是炮塔、敵人和特效,它們通常都有一個全局的腳本來統一管理。如果用 屬性檢查器 來一個一個將它們關聯到這個腳本上,那工作就會很繁瑣。爲了更好地統一管理這些對象,我們可以把它們放到一個統一的父物體下,然後通過父物體來獲得所有的子物體:
// CannonManager.js
cc.Class({
extends: cc.Component,
start: function () {
this.cannons = [];
this.cannons = this.node.getChildren();
}
});
這裏的 getChildren 是 cc.Node 原有的一個 API,可以獲得一個包含所有子節點的數組。
你還可以使用 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 + 文件名(不含路徑) 來獲取到對方 export 的對象。
// 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;
}
});