• 访问节点和组件
    • 获得组件所在的节点
    • 获得其它组件
    • 获得其它节点及其组件
      • 利用属性检查器设置节点
      • 利用属性检查器设置组件
      • 查找子节点
      • 全局名字查找
    • 访问已有变量里的值
      • 通过模块访问

    访问节点和组件

    你可以在 属性检查器 里修改节点和组件,也能在脚本中动态修改。动态修改的好处是能够在一段时间内连续地修改属性、过渡属性,实现渐变效果。脚本还能够响应玩家输入,能够修改、创建和销毁节点或组件,实现各种各样的游戏逻辑。要实现这些效果,你需要先在脚本中获得你要修改的节点或组件。

    在本篇教程,我们将介绍如何

    • 获得组件所在的节点
    • 获得其它组件
    • 使用 属性检查器 设置节点和组件
    • 查找子节点
    • 全局节点查找
    • 访问已有变量里的值

    获得组件所在的节点

    获得组件所在的节点很简单,只要在组件方法里访问 this.node 变量:

    1. start(){
    2. let node = this.node;
    3. node.setPosition(0.0,0.0,0.0);
    4. }

    获得其它组件

    你会经常需要获得同一个节点上的其它组件,这就要用到 getComponent 这个 API,它会帮你查找你要的组件。

    1. import { _decorator, Component, LabelComponent } from "cc";
    2. const { ccclass, property } = _decorator;
    3. @ccclass("test")
    4. export class test extends Component {
    5. private label: any = null
    6. start(){
    7. this.label = this.getComponent(LabelComponent);
    8. let text = this.name + 'started';
    9. // Change the text in Label Component
    10. this.label.string = text;
    11. }
    12. }

    你也可以为 getComponent 传入一个类名。对用户定义的组件而言,类名就是脚本的文件名,并且区分大小写。例如 "SinRotate.ts" 里声明的组件,类名就是 "SinRotate"。

    1. let rotate = this.getComponent("SinRotate");

    在节点上也有一个 getComponent 方法,它们的作用是一样的:

    1. start() {
    2. console.log( this.node.getComponent(LabelComponent) === this.getComponent(LabelComponent) ); // true
    3. }

    如果在节点上找不到你要的组件,getComponent 将返回 null,如果你尝试访问 null 的值,将会在运行时抛出 "TypeError" 这个错误。因此如果你不确定组件是否存在,请记得判断一下:

    1. import { _decorator, Component, LabelComponent } from "cc";
    2. const { ccclass, property } = _decorator;
    3. @ccclass("test")
    4. export class test extends Component {
    5. private label: any =null;
    6. start() {
    7. this.label = this.getComponent(LabelComponent);
    8. if (this.label) {
    9. this.label.string = "Hello";
    10. }
    11. else {
    12. console.error("Something wrong?");
    13. }
    14. }
    15. }

    获得其它节点及其组件

    仅仅能访问节点自己的组件通常是不够的,脚本通常还需要进行多个节点之间的交互。例如,一门自动瞄准玩家的大炮,就需要不断获取玩家的最新位置。Cocos Creator 3D提供了一些不同的方法来获得其它节点或组件。

    利用属性检查器设置节点

    最直接的方式就是在 属性检查器 中设置你需要的对象。以节点为例,这只需要在脚本中声明一个 type 为 Node 的属性:

    1. // Cannon.ts
    2. import { _decorator, Component, Node } from "cc";
    3. const { ccclass, property } = _decorator;
    4. @ccclass("Cannon")
    5. export class Cannon extends Component {
    6. // 声明Player属性
    7. @property({type:Node})
    8. private player = null;
    9. }

    这段代码在 properties 里面声明了一个 player 属性,默认值为 null,并且指定它的对象类型为 Node。这就相当于在其它语言里声明了 public Node player = null;。脚本编译之后,这个组件在 属性检查器 中看起来是这样的:

    player-in-inspector-null

    接着你就可以将层级管理器上的任意一个节点拖到这个 Player 控件:

    player-in-inspector

    这样一来它的 player 属性就会被设置成功,你可以直接在脚本里访问 player:

    1. // Cannon.ts
    2. import { _decorator, Component, Node } from "cc";
    3. const { ccclass, property } = _decorator;
    4. @ccclass("Cannon")
    5. export class Cannon extends Component {
    6. @property({type:Node})
    7. private player = null;
    8. start() {
    9. console.log("The player is " + this.player.name);
    10. }
    11. }

    利用属性检查器设置组件

    在上面的例子中,如果你将属性的 type 声明为 Player 组件,当你拖动节点 "Player Node" 到 属性检查器,player 属性就会被设置为这个节点里面的 Player 组件。这样你就不需要再自己调用 getComponent 啦。

    1. // Cannon.ts
    2. import { _decorator, Component } from "cc";
    3. const { ccclass, property } = _decorator;
    4. import { Player } from "Player";
    5. @ccclass("Cannon")
    6. export class Cannon extends Component {
    7. @property({type:Player})
    8. private player = null;
    9. start(){
    10. let PlayerComp = this.player;
    11. }
    12. }

    你还可以将属性的默认值由 null 改为数组[],这样你就能在 属性检查器 中同时设置多个对象。不过如果需要在运行时动态获取其它对象,还需要用到下面介绍的查找方法。

    查找子节点

    有时候,游戏场景中会有很多个相同类型的对象,像是炮塔、敌人和特效,它们通常都有一个全局的脚本来统一管理。如果用 属性检查器 来一个一个将它们关联到这个脚本上,那工作就会很繁琐。为了更好地统一管理这些对象,我们可以把它们放到一个统一的父物体下,然后通过父物体来获得所有的子物体:

    1. // CannonManager.ts
    2. import { _decorator, Component, Node } from "cc";
    3. const { ccclass, property } = _decorator;
    4. @ccclass("CannonManager")
    5. export class CannonManager extends Component {
    6. start() {
    7. let cannons = this.node.children;
    8. //...
    9. }
    10. }

    你还可以使用 getChildByName

    1. this.node.getChildByName("Cannon 01");

    如果子节点的层次较深,你还可以使用 findfind 将根据传入的路径进行逐级查找:

    1. find("Cannon 01/Barrel/SFX", this.node);

    全局名字查找

    find 只传入第一个参数时,将从场景根节点开始逐级查找:

    1. this.backNode = find("Canvas/Menu/Back");

    访问已有变量里的值

    如果你已经在一个地方保存了节点或组件的引用,你也可以直接访问它们

    通过模块访问

    你可以使用 import 来实现脚本的跨文件操作,让我们看个示例:

    1. // Global.ts, now the filename matters
    2. import { _decorator, Component, Node } from "cc";
    3. const { ccclass, property } = _decorator;
    4. @ccclass("Global")
    5. export class Global extends Component {
    6. public static backNode:any=null;
    7. public static backLabel:any=null;
    8. }

    每个脚本都能用 import{ } from + 文件名(不含路径) 来获取到对方 exports 的对象。

    1. // Back.ts
    2. import { _decorator, Component, Node } from "cc";
    3. const { ccclass, property } = _decorator;
    4. // this feels more safe since you know where the object comes from
    5. import{Global}from "./Global";
    6. @ccclass("Back")
    7. export class Back extends Component {
    8. onLoad(){
    9. Global.backNode=this.node;
    10. Global.backLabel=this.getComponent(cc.LabelComponent);
    11. }
    12. }
    1. // AnyScript.ts
    2. import { _decorator, Component, Node } from "cc";
    3. const { ccclass, property } = _decorator;
    4. // this feels more safe since you know where the object comes from
    5. import{Global}from "./Global";
    6. @ccclass("AnyScript")
    7. export class AnyScript extends Component {
    8. start () {
    9. var text = "Back";
    10. Global.backLabel.string=text;
    11. }
    12. }

    继续前往 常用节点和组件接口。