Cocos Creator開發中訪問節點和組件的方法總結

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;
    }
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章