cc 类

将装饰器 ccclass 应用在类上时,此类称为 cc 类。cc 类注入了额外的信息以控制 Cocos Creator 3D 对该类对象的序列化、编辑器对该类对象的展示等。

ccclass

cc 类的各种特性是通过 ccclass(name) 的 cc 类选项参数来指定的。

cc 类名

选项 name 指定了 cc 类的名称。cc 类名应该是独一无二的。

当需要相应的 cc 类时,可以通过其 cc 类名来查找,例如:

  • 序列化。若对象是 cc 类对象,则在序列化时将记录该对象的 cc 类名,反序列化时将根据此名称找到相应的 cc 类进行序列化。

  • 当 cc 类是组件类时,Node 通过可以组件类的 cc 类名查找该组件;

cc 属性

当装饰器 property 应用在 cc 类的属性或访问器上时,此属性称为 cc 属性。与 cc 类类似,cc 属性注入了额外的信息以控制 Cocos Creator 3D 对该属性的序列化、编辑器对该属性的展示等。

property

cc 属性的各种特性是通过 property() 的 cc 属性选项参数来指定的。

cc 类型

选项 type 指定了属性的 cc 类型。

可以通过以下几种形式的参数指定类型:

  • 构造函数。构造函数所指定的类型就直接作为属性的 cc 类型。注意,当 Javascript 构造函数 NumberStringBoolean用作 cc 类型时将给出警告,并且将分别视为 cc 类型 CCFloatCCStringCCBoolean

  • Cocos Creator 3D 内置属性类型标识。CCIntegerCCFloatCCBooleanCCString 是内置属性类型标识。CCInteger 声明类型为 Cocos Creator 3D 整数CCFloat 声明类型为 Cocos Creator 3D 浮点数CCString 声明类型为 Cocos Creator 3D 字符串CCBoolean 声明类型为 Cocos Creator 3D 布尔值

  • 数组。通过将构造函数、Cocos Creator 3D 内置属性类型标识或数组作为数组元素时,属性被指定为 Cocos Creator 3D 数组。例如 [CCInteger] 就将类型声明为元素为Cocos Creator 3D 整数的 Cocos Creator 3D 数组。

若属性未指定 cc 类型,Cocos Creator 3D 将从属性的默认值或初始化式的求值结果推导其 cc 类型:

  • 若值的类型是 Javascript 原始类型 numberstringboolean,则其 cc 类型分别为 Cocos Creator 3D 浮点数、Cocos Creator 3D 字符串、Cocos Creator 3D 布尔值。
  • 否则,若值是对象类型,则相当于使用对象的构造函数指定了 cc 类型;
  • 否则,属性的 cc 类型是未定义的。关于 cc 类型如何影响 cc 属性以及对未定义 cc 类型的属性的处理,见:

  • 编辑器和属性类型

  • 序列化下列代码演示了不同 cc 类型 的 cc 属性的声明:
  1. import { _decorator, CCInteger, Node } from "cc";
  2. const { ccclass, property } = _decorator;
  3. @ccclass
  4. class MyClass {
  5. @property(CCInteger) // 声明属性 _id 的 cc 类型为 Cocos 整数
  6. private _id = 0;
  7. @property(Node) // 声明属性 _targetNode 的 cc 类型为 Node
  8. private _targetNode: Node | null = null;
  9. @property([Node]) // 声明属性 _children 的 cc 类型为 Node 数组
  10. private _children: Node[] = [];
  11. @property
  12. private _count = 0; // 未声明 cc 类型,从初始化式的求值结果推断为 Cocos 浮点数
  13. @property(String) // 警告:不应该使用构造函数 String
  14. // 等价于 CCString
  15. private _name: string = '';
  16. @property
  17. private _children2 = []; // 未声明 cc 类型,从初始化式的求值结果推断为:元素为未定义的 Cocos 数组
  18. }

默认值

选项 default 指定了 cc 属性的默认值。

构造函数

通过 constructor 定义

CCClass 的构造函数使用 constructor 定义,为了保证反序列化能始终正确运行,constructor 不允许定义构造参数

开发者如果确实需要使用构造参数,可以通过 arguments 获取,但要记得如果这个类会被序列化,必须保证构造参数都缺省的情况下仍然能 new 出对象。

判断类型

判断实例

需要做类型判断时,可以用 TypeScript 原生的 instanceof

  1. class Sub extends Base {
  2. }
  3. let sub = new Sub();
  4. console.log(sub instanceof Sub); //true
  5. console.log(sub instanceof Base); //true
  6. let base = new Base();
  7. console.log(base instanceof Sub); // false

成员

实例变量

在构造函数中定义的实例变量不能被序列化,也不能在 属性检查器 中查看。

  1. class Sprite{
  2. //声明变量
  3. url: string;
  4. id: number;
  5. constructor() {
  6. //赋值
  7. this.url = "";
  8. this.id = 0;
  9. }
  10. }

如果是私有的变量,建议在变量名前面添加下划线 _ 以示区分。

实例方法

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

  1. class Sprite{
  2. text: string;
  3. constructor() {
  4. this.text = "this is sprite"
  5. }
  6. // 声明一个名叫 "print" 的实例方法
  7. print(){
  8. console.log(this.text);
  9. }
  10. }
  11. let obj = new Sprite();
  12. // 调用实例方法
  13. obj.print();

静态变量和静态方法

静态变量或静态方法可以用 statics 声明:

  1. class Sprite{
  2. static count=0;
  3. static getBounds(){
  4. }
  5. }

静态成员会被子类继承,继承时会将父类的静态变量浅拷贝给子类,因此:

  1. class Object{
  2. static count= 11;
  3. static range: { w: 100, h: 100 }
  4. }
  5. class Sprite extends Object{
  6. }
  7. console.log(Sprite.count); // 结果是 11,因为 count 继承自 Object 类
  8. Sprite.range.w = 200;
  9. console.log(Object.range.w); // 结果是 200,因为 Sprite.range 和 Object.range 指向同一个对象

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

  1. // 局部方法
  2. doLoad(sprite){
  3. // ...
  4. };
  5. // 局部变量
  6. let url = "foo.png";
  7. class Sprite{
  8. load() {
  9. this.url = url;
  10. doLoad(this);
  11. };
  12. };

继承

父构造函数

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

  1. class Node {
  2. name: string;
  3. constructor(){
  4. this.name = "node";
  5. }
  6. }
  7. class Sprite extends Node{
  8. constructor() {
  9. super();
  10. // 子构造函数被调用前,父构造函数已经被调用过,所以 this.name 已经被初始化过了
  11. console.log(this.name); // "node"
  12. // 重新设置 this.name
  13. this.name = "sprite";
  14. }
  15. }
  16. let obj = new Sprite();
  17. console.log(obj.name); // "sprite"

重写

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

  1. class Shape{
  2. getName() {
  3. return "shape";
  4. }
  5. };
  6. class Rect extends Shape{
  7. getName () {
  8. return "rect";
  9. }
  10. };
  11. let obj = new Rect();
  12. console.log(obj.getName()); // "rect"

属性

属性是特殊的实例变量,能够显示在 属性检查器 中,也能被序列化。

属性和构造函数

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

  1. class Sprite {
  2. constructor() {
  3. this.num = 1;
  4. }
  5. @property({type:CCInteger})
  6. private num = 0;
  7. }

不过要注意的是,属性被反序列化的过程紧接着发生在构造函数执行之后,因此构造函数中只能获得和修改属性的默认值,还无法获得和修改之前保存(序列化)的值。

属性参数

default 参数

default 用于声明属性的默认值,声明了默认值的属性会被 CCClass 实现为成员变量。默认值只有在第一次创建对象的时候才会用到,也就是说修改默认值时,并不会改变已添加到场景里的组件的当前值。

当你在编辑器中添加了一个组件以后,再回到脚本中修改一个默认值的话,属性检查器 里面是看不到变化的。因为属性的当前值已经序列化到了场景中,不再是第一次创建时用到的默认值了。如果要强制把所有属性设回默认值,可以在 属性检查器 的组件菜单中选择 Reset。

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

  • 任意 number, string 或 boolean 类型的值
  • nullundefined
  • 继承自 ValueType 的子类,如 Vec3, ColorRect 的实例化对象:
  1. @property({type:Vec3})
  2. private pos = null;
  • 空数组 [] 或空对象 {}

visible 参数

默认情况下,是否显示在 属性检查器 取决于属性名是否以下划线 _ 开头。如果以下划线开头,则默认不显示在 属性检查器,否则默认显示。

如果要强制显示在 属性检查器,可以设置 visible 参数为 true:

  1. @property({visible:true})
  2. private _num = 0;

如果要强制隐藏,可以设置 visible 参数为 false:

  1. @property({visible:false})
  2. private num = 0;

serializable 参数

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

  1. @property({serializable:false})
  2. private num = 0;

type 参数

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

  • 当默认值为 null 时,将 type 设置为指定类型的构造函数,这样 属性检查器 才知道应该显示一个 Node 控件。
  1. @property({type:Node})
  2. private enemy = null;
  • 当默认值为数值(number)类型时,将 type 设置为 cc.Integer,用来表示这是一个整数,这样属性在 属性检查器 里就不能输入小数点。
  1. @property({type:CCInteger})
  2. private num = 0;
  • 当默认值是一个枚举(Enum)时,由于枚举值本身其实也是一个数字(number),所以要将 type 设置为枚举类型,才能在 属性检查器 中显示为枚举下拉框。
  1. enum A{
  2. c,
  3. d
  4. }
  5. Enum(A);
  6. @ccclass("test")
  7. export class test extends Component {
  8. @property({type:A})
  9. accx:A=A.c;
  10. }

override 参数

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

  1. @property({type:CCString,tooltip:"my id",override:true})
  2. private _id = "";
  3. @property({displayName:"Name",override:true})
  4. private _name = null;
  5. private get name(){
  6. return this._name;
  7. }

更多参数内容请查阅 属性参数

GetSet 方法

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

get

在属性中设置 get 方法:

  1. @property({type:CCInteger})
  2. private _num = 0;
  3. private get num(){
  4. return this._num;
  5. }

get 方法可以返回任意类型的值。这个属性同样能显示在 属性检查器 中,并且可以在包括构造函数内的所有代码里直接访问。

  1. class Sprite{
  2. _width: number;
  3. constructor() {
  4. this._width = 128;
  5. console.log(this.width); // 128
  6. }
  7. @property({type:CCInteger})
  8. private width = 0;
  9. private get width(){
  10. return this._width;
  11. }
  12. };

请注意:

  • 设定了 get 以后,这个属性就不能被序列化,也不能指定默认值,但仍然可附带除了 default, serializable 外的大部分参数。
  1. @property({type:CCInteger,tooltip: "The width of sprite"})
  2. private _width = 0;
  3. private get width(){
  4. return this._width;
  5. }
  • get 属性本身是只读的,但返回的对象并不是只读的。用户使用代码依然可以修改对象内部的属性,例如:
  1. @property
  2. _num=0;
  3. private get num(){
  4. return this._num;
  5. }
  6. start(){
  7. consolo.log(this.num);
  8. }

set

在属性中设置 set 方法:

  1. @property({type:CCInteger})
  2. private _width = 0;
  3. set(value){
  4. this._width = value
  5. }

set 方法接收一个传入参数,这个参数可以是任意类型。

set 一般和 get 一起使用:

  1. @property
  2. _width=0;
  3. private get width(){
  4. return this._width;
  5. }
  6. set(value){
  7. this._width = value;
  8. }

如果没有和 get 一起定义,则 set 自身不能附带任何参数。和 get 一样,设定了 set 以后,这个属性就不能被序列化,也不能指定默认值。