CCClass 进阶参考

相比其它 JavaScript 的类型系统,CCClass 的特别之处在于功能强大,能够灵活的定义丰富的元数据。CCClass 的技术细节比较丰富,你可以在开发过程中慢慢熟悉。本文档将列举它的详细用法,阅读前需要先掌握 使用 cc.Class 声明类型。

原型对象参数说明

cc.Class({
    // 类名,用于序列化
    // 值类型:String
    name: "Character",

    // 基类,可以是任意创建好的 cc.Class
    // 值类型:Function
    extends: cc.Component,

    // 构造函数
    // 值类型:Function
    ctor: function () {},

    // 属性定义(方式一,直接定义)
    properties: {
        text: ""
    },

    // 属性定义(方式二,使用 ES6 的箭头函数,详见下文)
    properties: () => ({
        text: ""
    }),

    // 实例方法
    print: function () {
        cc.log(this.text);
    },

    // 静态成员定义
    // 值类型:Object
    statics: {
        _count: 0,
        getCount: function () {}
    },

    // 提供给 Component 的子类专用的参数字段
    // 值类型:Object
    editor: {
        disallowMultiple: true
    }
});

成员

实例变量
var Sprite = cc.Class({
    ctor: function () {
        // 声明实例变量并赋默认值
        this.url = "";
        this.id = 0;
    }
});
实例方法
var Sprite = cc.Class({
    ctor: function () {
        this.text = "this is sprite";
    },
    // 声明一个名叫 "print" 的实例方法
    print: function () {
        cc.log(this.text);
    }
});

var obj = new Sprite();
// 调用实例方法
obj.print();
静态变量和静态方法

静态变量或静态方法可以在原型对象的 statics 中声明

var Sprite = cc.Class({
    statics: {
        // 声明静态变量
        count: 0,
        // 声明静态方法
        getBounds: function (spriteList) {
            // ...
        }
    }
});

上面的代码等价于:

var Sprite = cc.Class({ ... });

// 声明静态变量
Sprite.count = 0;
// 声明静态方法
Sprite.getBounds = function (spriteList) {
    // ...
};

继承

执行顺序 : 父类的构造函数 -> 子类的构造函数

var Node = cc.Class({
    ctor: function () {
        this.name = "node";
    }
});
var Sprite = cc.Class({
    extends: Node,
    ctor: function () {
        // 子构造函数被调用前,父构造函数已经被调用过,所以 this.name 已经被初始化过了
        cc.log(this.name);    // "node"
        // 重新设置 this.name
        this.name = "sprite";
    }
});
var obj = new Sprite();
cc.log(obj.name);    // "sprite"

重写

所有成员方法都是虚方法,子类方法可以直接重写父类方法

var Shape = cc.Class({
    getName: function () {
        return "shape";
    }
});
var Rect = cc.Class({
    extends: Shape,
    getName: function () {
        return "rect";
    }
});
var obj = new Rect();
cc.log(obj.getName());    // "rect"
调用父类方法

使用 CCClass 封装的 this._super

var Shape = cc.Class({
    getName: function () {
        return "shape";
    }
});
var Rect = cc.Class({
    extends: Shape,
    getName: function () {

        var baseName = this._super();

        return baseName + " (rect)";
    }
});
var obj = new Rect();
cc.log(obj.getName());    // "shape (rect)"

属性

  1. 属性是特殊的实例变量,能够显示在 属性检查器 中,也能被序列化
  2. 属性不用在构造函数里定义
  3. 在构造函数被调用前,属性已经被赋为默认值了,可以在构造函数内访问到
var Sprite = cc.Class({
    ctor: function () {
        this.img = LoadImage();
    },
    properties: {
        img: {
            default: null,
            type: Image
        }
    }
});
属性参数

所有属性参数都是可选的,但至少必须声明 default, get, set 参数中的其中一个

default 参数

允许设置为以下几种值类型:

  • 任意 number, string 或 boolean 类型的值
  • null 或 undefined
  • 继承自 cc.ValueType 的子类,如 cc.Vec2, cc.Color 或 cc.Rect 的实例化对象:
properties: {
     pos: {
         default: new cc.Vec2(),
     }
 }
  • 空数组 [] 或空对象 {}
  • 一个允许返回任意类型值的 function,这个 function 会在每次实例化该类时重新调用,并且以返回值作为新的默认值
properties: {
     pos: {
         default: function () {
             return [1, 2, 3];
         },
     }
 }
visible 参数

如果以下划线开头,则默认不显示在 属性检查器,否则默认显示
_id 隐藏,id 显示

serializable 参数

指定了 default 默认值的属性默认情况下都会被序列化,序列化后就会将编辑器中设置好的值保存到场景等资源文件中,并且在加载场景时自动还原之前设置好的值。如果不想序列化,可以设置serializable: false

temp_url: {
    default: "",
    serializable: false
}
type 参数

当 default 不能提供足够详细的类型信息时,为了能在 属性检查器 显示正确的输入控件,就要用 type 显式声明具体的类型:

// 节点 Node
 enemy: {
      default: null,
      type: cc.Node
  }
  // 整型 Integer
    score: {
      default: 0,
      type: cc.Integer
  }
  // 枚举
    wrap: {
      default: Texture.WrapMode.Clamp,
      type: Texture.WrapMode
  }
url 参数

访问 Raw Asset 资源的 url

texture: {
    default: "",
    url: cc.Texture2D
},
override 参数

所有属性都将被子类继承,如果子类要覆盖父类同名属性,需要显式设置 override 参数,否则会有重名警告:

_id: {
    default: "",
    tooltip: "my id",
    override: true
},
属性延迟定义

两个类相互引用,脚本加载阶段就会出现循环引用,循环引用将导致脚本加载出错
将 properties 指定为一个 ES6 的箭头函数

// Game.js
var Game = cc.Class({
     properties: () => ({
         item: {
             default: null,
             // 引用 Item
             type: require("Item")
         }
     })
 });

 module.exports = Game;
// Item.js
 var Item = cc.Class({
     properties: () => ({
         game: {
             default: null,
             // 引用 Game
             type: require("Game")
         }
     })
 });

 module.exports = Item;

Get 方法

get 属性本身是只读的,但返回的对象并不是只读的

var Sprite = cc.Class({
      ...
      position: {
          get: function () {
              return this._position;
          },
      }
      ...
  });
  var obj = new Sprite();
  obj.position = new cc.Vec2(10, 20);   // 失败!position 是只读的!
  obj.position.x = 100;                 // 允许!position 返回的 _position 对象本身可以修改!

Set 方法

  1. 如果没有和 get 一起定义,则 set 自身不能附带任何参数。
  2. 和 get 一样,设定了 set 以后,这个属性就不能被序列化,也不能指定默认值。
width: {
    get: function () {
        return this._width;
    },
    set: function (value) {
        this._width = value;
    },
    type: cc.Integer,
    tooltip: "The width of sprite"
}

editor 参数

editor 只能定义在 cc.Component 的子类。

cc.Class({
  extends: cc.Component,

  editor: {

    // 允许当前组件在编辑器模式下运行。
    // 默认情况下,所有组件都只会在运行时执行,也就是说它们的生命周期回调在编辑器模式下并不会触发。
    //
    // 值类型:Boolean
    // 默认值:false
    executeInEditMode: false,

    // requireComponent 参数用来指定当前组件的依赖组件。
    // 当组件添加到节点上时,如果依赖的组件不存在,引擎将会自动将依赖组件添加到同一个节点,防止脚本出错。
    // 该选项在运行时同样有效。
    // 
    // 值类型:Function (必须是继承自 cc.Component 的构造函数,如 cc.Sprite)
    // 默认值:null
    requireComponent: null,

    // 脚本生命周期回调的执行优先级。
    // 小于 0 的脚本将优先执行,大于 0 的脚本将最后执行。
    // 该优先级只对 onLoad, onEnable, start, update 和 lateUpdate 有效,对 onDisable 和 onDestroy 无效。
    //
    // 值类型:Number
    // 默认值:0
    executionOrder: 0,

    // 当本组件添加到节点上后,禁止同类型(含子类)的组件再添加到同一个节点,
    // 防止逻辑发生冲突。
    // 
    // 值类型:Boolean
    // 默认值:false
    disallowMultiple: false,

    // menu 用来将当前组件添加到组件菜单中,方便用户查找。
    // 
    // 值类型:String (如 "Rendering/Camera")
    // 默认值:""
    menu: "",

    // 当设置了 "executeInEditMode" 以后,playOnFocus 可以用来设定选中当前组件所在的节点时,
    // 编辑器的场景刷新频率。
    // playOnFocus 如果设置为 true,场景渲染将保持 60 FPS,如果为 false,场景就只会在必要的时候进行重绘。
    // 
    // 值类型:Boolean
    // 默认值:false
    playOnFocus: false,

    // 自定义当前组件在 **属性检查器** 中渲染时所用的网页 url。
    // 
    // 值类型:String
    // 默认值:""
    inspector: "",

    // 自定义当前组件在编辑器中显示的图标 url。
    // 
    // 值类型:String
    // 默认值:""
    icon: "",

    // 指定当前组件的帮助文档的 url,设置过后,在 **属性检查器** 中就会出现一个帮助图标,
    // 用户点击将打开指定的网页。
    // 
    // 值类型:String
    // 默认值:""
    help: "",
  }
});