CCClass 进阶参考

使用 cc.Class 声明类型。

术语

  • cc.Class 声明的类。
  • cc.Class 时传入的字面量参数。
  • -

    原型对象参数说明

    所有原型对象的参数都可以省略,用户只需要声明用得到的部分即可。
    1. 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 }});

    类名

    cc.js.getClassName 来获得类名,使用 cc.js.getClassByName 来查找对应的类。对在项目脚本里定义的组件来说,序列化其实并不使用类名,因此那些组件不需要指定类名。对其他类来说,类名用于序列化,如果不需要序列化,类名可以省略。

    构造函数

    通过 ctor 定义

    ctor 定义,为了保证反序列化能始终正确运行,ctor 不允许定义构造参数

    arguments 获取,但要记得如果这个类会被序列化,必须保证构造参数都缺省的情况下仍然能 new 出对象。

    __ctor__ctor 一样,但是允许定义构造参数,并且不会自动调用父构造函数,因此用户可以自行调用父构造函数。__ctor__ 不是标准的构造函数定义方式,如果没有特殊需要请一律使用 ctor 定义。

    判断类型

    判断实例

    instanceof

    1. var Sub = cc.Class({ extends: Base});var sub = new Sub();cc.log(sub instanceof Sub); // truecc.log(sub instanceof Base); // truevar base = new Base();cc.log(base instanceof Sub); // false

    判断类

    cc.isChildClassOf 来判断两个类的继承关系:

    1. var Texture = cc.Class();var Texture2D = cc.Class({ extends: Texture});cc.log(cc.isChildClassOf(Texture2D, Texture)); // true

    isChildClassOf 同样会返回 true。

    成员

    实例变量

    属性检查器 中查看。

    1. var Sprite = cc.Class({ ctor: function () { // 声明实例变量并赋默认值 this.url = ""; this.id = 0; }});

    _ 以示区分。

    实例方法

    实例方法请在原型对象中声明:

    1. 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 中声明:

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

    上面的代码等价于:

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

    浅拷贝给子类,因此:

    1. var Object = cc.Class({ statics: { count: 11, range: { w: 100, h: 100 } }});var Sprite = cc.Class({ extends: Object});cc.log(Sprite.count); // 结果是 11,因为 count 继承自 Object 类Sprite.range.w = 200;cc.log(Object.range.w); // 结果是 200,因为 Sprite.range 和 Object.range 指向同一个对象

    如果你不需要考虑继承,私有的静态成员也可以直接定义在类的外面:

    1. // 局部方法function doLoad (sprite) { // ...};// 局部变量var url = "foo.png";var Sprite = cc.Class({ load: function () { this.url = url; doLoad(this); };});

    继承

    父构造函数

    请注意,不论子类是否有定义构造函数,子类实例化前父类的构造函数都会被自动调用。

    1. 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"

    因此你不需要尝试调用父类的构造函数,否则父构造函数就会重复调用。

    1. var Node = cc.Class({ ctor: function () { this.name = "node"; }});var Sprite = cc.Class({ extends: Node, ctor: function () { Node.call(this); // 别这么干! this._super(); // 也别这么干! this.name = "sprite"; }});

    中。

    重写

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

    1. 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

    1. 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)"

    方法二:使用 JavaScript 原生写法:

    1. var Shape = cc.Class({ getName: function () { return "shape"; }});var Rect = cc.Class({ extends: Shape, getName: function () { var baseName = Shape.prototype.getName.call(this); return baseName + " (rect)"; }});var obj = new Rect();cc.log(obj.getName()); // "shape (rect)"

    cc.js.extend 来实现继承。

    属性

    属性检查器 中,也能被序列化。

    属性和构造函数

    不用在构造函数里定义,在构造函数被调用前,属性已经被赋为默认值了,可以在构造函数内访问到。如果属性的默认值无法在定义 CCClass 时提供,需要在运行时才能获得,你也可以在构造函数中重新给属性赋默认值。

    1. var Sprite = cc.Class({ ctor: function () { this.img = LoadImage(); }, properties: { img: { default: null, type: Image } }});

    之后,因此构造函数中只能获得和修改属性的默认值,还无法获得和修改之前保存(序列化)的值。

    属性参数

    default, get, set 参数中的其中一个。

    default 用于声明属性的默认值,声明了默认值的属性会被 CCClass 实现为成员变量。默认值只有在第一次创建对象的时候才会用到,也就是说修改默认值时,并不会改变已添加到场景里的组件的当前值。 属性检查器 里面是看不到变化的。因为属性的当前值已经序列化到了场景中,不再是第一次创建时用到的默认值了。如果要强制把所有属性设回默认值,可以在 属性检查器 的组件菜单中选择 Reset。 default 允许设置为以下几种值类型:

  1. nullundefined
  2. cc.ValueType 的子类,如 cc.Vec2, cc.Colorcc.Rect 的实例化对象:
    1. properties: { pos: { default: new cc.Vec2(), } }
  3. [] 或空对象 {}
    1. properties: { pos: { default: function () { return [1, 2, 3]; }, } }
    属性检查器 取决于属性名是否以下划线 _ 开头。如果以下划线开头,则默认不显示在 属性检查器,否则默认显示。 属性检查器,可以设置 visible 参数为 true:
    1. properties: { _id: { // 下划线开头原本会隐藏 default: 0, visible: true }}
    visible 参数为 false:
    1. properties: { id: { // 非下划线开头原本会显示 default: 0, visible: false }}
    default 默认值的属性默认情况下都会被序列化,如果不想序列化,可以设置serializable: false
    1. temp_url: { default: "", serializable: false}
    default 不能提供足够详细的类型信息时,为了能在 属性检查器 显示正确的输入控件,就要用 type 显式声明具体的类型:
  • 属性检查器 才知道应该显示一个 Node 控件。
  1. enemy: { default: null, type: cc.Node }
  • cc.Integer,用来表示这是一个整数,这样属性在 属性检查器 里就不能输入小数点。
  1. score: { default: 0, type: cc.Integer }
  • cc.Enum)时,由于枚举值本身其实也是一个数字(number),所以要将 type 设置为枚举类型,才能在 属性检查器 中显示为枚举下拉框。
  1. wrap: { default: Texture.WrapMode.Clamp, type: Texture.WrapMode }

属性检查器 中选取资源,或者能正确序列化,你需要指定 url 参数:

  1. texture: { default: "", url: cc.Texture2D},

获取和加载资源: Raw Asset

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

  1. _id: { default: "", tooltip: "my id", override: true},name: { get: function () { return this._name; }, displayName: "Name", override: true}

属性参数。

如果两个类相互引用,脚本加载阶段就会出现循环引用,循环引用将导致脚本加载出错:

  • Game.js
  1. var Item = require("Item"); var Game = cc.Class({ properties: { item: { default: null, type: Item } } }); module.exports = Game;
  • Item.js
  1. var Game = require("Game"); var Item = cc.Class({ properties: { game: { default: null, type: Game } } }); module.exports = Item;

因此我们提倡使用以下的属性定义方式:

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

这种方式就是将 properties 指定为一个 ES6 的箭头函数(lambda 表达式),箭头函数的内容在脚本加载过程中并不会同步执行,而是会被 CCClass 以异步的形式在所有脚本加载成功后才调用。因此加载过程中并不会出现循环引用,属性都可以正常初始化。

箭头函数的用法符合 JavaScript 的 ES6 标准,并且 Creator 会自动将 ES6 转义为 ES5,用户不用担心浏览器的兼容问题。 你可以这样来理解箭头函数:

  1. // 箭头函数支持省略掉 `return` 语句,我们推荐的是这种省略后的写法:properties: () => ({ // <- 箭头右边的括号 "(" 不可省略 game: { default: null, type: require("Game") }})// 如果要完整写出 `return`,那么上面的写法等价于:properties: () => { return { game: { default: null, type: require("Game") } }; // <- 这里 return 的内容,就是原先箭头右边括号里的部分}// 我们也可以不用箭头函数,而是用普通的匿名函数:properties: function () { return { game: { default: null, type: require("Game") } };}

GetSet 方法

在属性中设置了 get 或 set 以后,访问属性的时候,就能触发预定义的 get 或 set 方法。

get

在属性中设置 get 方法:

  1. properties: { width: { get: function () { return this.__width; } }}

这个属性同样能显示在 属性检查器 中,并且可以在包括构造函数内的所有代码里直接访问。

  1. var Sprite = cc.Class({ ctor: function () { this.__width = 128; cc.log(this.width); // 128 }, properties: { width: { get: function () { return this.__width; } } }});

请注意:

  • default, serializable 外的大部分参数。
  1. width: { get: function () { return this.__width; }, type: cc.Integer, tooltip: "The width of sprite" }
  • get 属性本身是只读的,但返回的对象并不是只读的。用户使用代码依然可以修改对象内部的属性,例如:
  1. 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

在属性中设置 set 方法:

  1. width: { set: function (value) { this._width = value; }}

set 方法接收一个传入参数,这个参数可以是任意类型。 set 一般和 get 一起使用:

  1. width: { get: function () { return this._width; }, set: function (value) { this._width = value; }, type: cc.Integer, tooltip: "The width of sprite"}

和 get 一样,设定了 set 以后,这个属性就不能被序列化,也不能指定默认值。

editor 参数

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

  1. cc.Class({ extends: cc.Component, editor: { // requireComponent 参数用来指定当前组件的依赖组件。 // 当组件添加到节点上时,如果依赖的组件不存在,引擎将会自动将依赖组件添加到同一个节点,防止脚本出错。 // 该选项在运行时同样有效。 // // 值类型:Function (必须是继承自 cc.Component 的构造函数,如 cc.Sprite) // 默认值:null requireComponent: null, // 当本组件添加到节点上后,禁止同类型(含子类)的组件再添加到同一个节点, // 防止逻辑发生冲突。 // // 值类型:Boolean // 默认值:false disallowMultiple: false, // menu 用来将当前组件添加到组件菜单中,方便用户查找。 // // 值类型:String (如 "Rendering/Camera") // 默认值:"" menu: "", // 允许当前组件在编辑器模式下运行。 // 默认情况下,所有组件都只会在运行时执行,也就是说它们的生命周期回调在编辑器模式下并不会触发。 // // 值类型:Boolean // 默认值:false executeInEditMode: false, // 当设置了 "executeInEditMode" 以后,playOnFocus 可以用来设定选中当前组件所在的节点时, // 编辑器的场景刷新频率。 // playOnFocus 如果设置为 true,场景渲染将保持 60 FPS,如果为 false,场景就只会在必要的时候进行重绘。 // // 值类型:Boolean // 默认值:false playOnFocus: false, // 自定义当前组件在 **属性检查器** 中渲染时所用的网页 url。 // // 值类型:String // 默认值:"" inspector: "", // 自定义当前组件在编辑器中显示的图标 url。 // // 值类型:String // 默认值:"" icon: "", // 指定当前组件的帮助文档的 url,设置过后,在 **属性检查器** 中就会出现一个帮助图标, // 用户点击将打开指定的网页。 // // 值类型:String // 默认值:"" help: "", }});

属性参数参考。