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 构造函数
Number、String、Boolean用作 cc 类型时将给出警告,并且将 分别视为 cc 类型CCFloat、CCString、CCBoolean。Cocos Creator 3D 内置属性类型标识。
CCInteger、CCFloat、CCBoolean、CCString是内置属性类型标识。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 原始类型
number、string、boolean, 则其 cc 类型分别为 Cocos Creator 3D 浮点数、Cocos Creator 3D 字符串、Cocos Creator 3D 布尔值。 - 否则,若值是对象类型,则相当于使用对象的构造函数指定了 cc 类型;
- 否则,属性的 cc 类型是未定义的。
关于 cc 类型如何影响 cc 属性以及对未定义 cc 类型的属性的处理,见:
下列代码演示了不同 cc 类型 的 cc 属性的声明:
import { _decorator, CCInteger, Node } from "cc";
const { ccclass, property } = _decorator;
@ccclass
class MyClass {
@property(CCInteger) // 声明属性 _id 的 cc 类型为 Cocos 整数
private _id = 0;
@property(Node) // 声明属性 _targetNode 的 cc 类型为 Node
private _targetNode: Node | null = null;
@property([Node]) // 声明属性 _children 的 cc 类型为 Node 数组
private _children: Node[] = [];
@property
private _count = 0; // 未声明 cc 类型,从初始化式的求值结果推断为 Cocos 浮点数
@property(String) // 警告:不应该使用构造函数 String
// 等价于 CCString
private _name: string = '';
@property
private _children2 = []; // 未声明 cc 类型,从初始化式的求值结果推断为:元素为未定义的 Cocos 数组
}
默认值
选项 default 指定了 cc 属性的默认值。
构造函数
通过 constructor 定义
CCClass 的构造函数使用 constructor 定义,为了保证反序列化能始终正确运行,constructor 不允许定义构造参数。
开发者如果确实需要使用构造参数,可以通过
arguments获取,但要记得如果这个类会被序列化,必须保证构造参数都缺省的情况下仍然能 new 出对象。
判断类型
判断实例
需要做类型判断时,可以用 TypeScript 原生的 instanceof:
class Sub extends Base {
}
let sub = new Sub();
console.log(sub instanceof Sub); //true
console.log(sub instanceof Base); //true
let base = new Base();
console.log(base instanceof Sub); // false
成员
实例变量
在构造函数中定义的实例变量不能被序列化,也不能在 属性检查器 中查看。
class Sprite{
//声明变量
url: string;
id: number;
constructor() {
//赋值
this.url = "";
this.id = 0;
}
}
如果是私有的变量,建议在变量名前面添加下划线
_以示区分。
实例方法
实例方法请在原型对象中声明:
class Sprite{
text: string;
constructor() {
this.text = "this is sprite"
}
// 声明一个名叫 "print" 的实例方法
print(){
console.log(this.text);
}
}
let obj = new Sprite();
// 调用实例方法
obj.print();
静态变量和静态方法
静态变量或静态方法可以用 statics 声明:
class Sprite{
static count=0;
static getBounds(){
}
}
静态成员会被子类继承,继承时会将父类的静态变量浅拷贝给子类,因此:
class Object{
static count= 11;
static range: { w: 100, h: 100 }
}
class Sprite extends Object{
}
console.log(Sprite.count); // 结果是 11,因为 count 继承自 Object 类
Sprite.range.w = 200;
console.log(Object.range.w); // 结果是 200,因为 Sprite.range 和 Object.range 指向同一个对象
如果你不需要考虑继承,私有的静态成员也可以直接定义在类的外面:
// 局部方法
doLoad(sprite){
// ...
};
// 局部变量
let url = "foo.png";
class Sprite{
load() {
this.url = url;
doLoad(this);
};
};
继承
父构造函数
请注意,不论子类是否有定义构造函数,子类实例化前父类的构造函数都会被自动调用。
class Node {
name: string;
constructor(){
this.name = "node";
}
}
class Sprite extends Node{
constructor() {
super();
// 子构造函数被调用前,父构造函数已经被调用过,所以 this.name 已经被初始化过了
console.log(this.name); // "node"
// 重新设置 this.name
this.name = "sprite";
}
}
let obj = new Sprite();
console.log(obj.name); // "sprite"
重写
所有成员方法都是虚方法,子类方法可以直接重写父类方法:
class Shape{
getName() {
return "shape";
}
};
class Rect extends Shape{
getName () {
return "rect";
}
};
let obj = new Rect();
console.log(obj.getName()); // "rect"
属性
属性是特殊的实例变量,能够显示在 属性检查器 中,也能被序列化。
属性和构造函数
属性不用在构造函数里定义,在构造函数被调用前,属性已经被赋为默认值了,可以在构造函数内访问到。如果属性的默认值无法在定义 CCClass 时提供,需要在运行时才能获得,你也可以在构造函数中重新给属性赋默认值。
class Sprite {
constructor() {
this.num = 1;
}
@property({type:CCInteger})
private num = 0;
}
不过要注意的是,属性被反序列化的过程紧接着发生在构造函数执行之后,因此构造函数中只能获得和修改属性的默认值,还无法获得和修改之前保存(序列化)的值。
属性参数
default参数
default 用于声明属性的默认值,声明了默认值的属性会被 CCClass 实现为成员变量。默认值只有在第一次创建对象的时候才会用到,也就是说修改默认值时,并不会改变已添加到场景里的组件的当前值。
当你在编辑器中添加了一个组件以后,再回到脚本中修改一个默认值的话,属性检查器 里面是看不到变化的。因为属性的当前值已经序列化到了场景中,不再是第一次创建时用到的默认值了。如果要强制把所有属性设回默认值,可以在 属性检查器 的组件菜单中选择 Reset。
default 允许设置为以下几种值类型:
- 任意 number, string 或 boolean 类型的值
null或undefined- 继承自
ValueType的子类,如Vec3,Color或Rect的实例化对象:@property({type:Vec3}) private pos = null; - 空数组
[]或空对象{}
visible参数
默认情况下,是否显示在 属性检查器 取决于属性名是否以下划线 _ 开头。如果以下划线开头,则默认不显示在 属性检查器,否则默认显示。
如果要强制显示在 属性检查器,可以设置 visible 参数为 true:
@property({visible:true})
private _num = 0;
如果要强制隐藏,可以设置 visible 参数为 false:
@property({visible:false})
private num = 0;
serializable参数
指定了 default 默认值的属性默认情况下都会被序列化,序列化后就会将编辑器中设置好的值保存到场景等资源文件中,并且在加载场景时自动还原之前设置好的值。如果不想序列化,可以设置serializable: false。
@property({serializable:false})
private num = 0;
type参数
当 default 不能提供足够详细的类型信息时,为了能在 属性检查器 显示正确的输入控件,就要用 type 显式声明具体的类型:
当默认值为 null 时,将 type 设置为指定类型的构造函数,这样 属性检查器 才知道应该显示一个 Node 控件。
@property({type:Node}) private enemy = null;当默认值为数值(number)类型时,将 type 设置为
cc.Integer,用来表示这是一个整数,这样属性在 属性检查器 里就不能输入小数点。@property({type:CCInteger}) private num = 0;当默认值是一个枚举(
Enum)时,由于枚举值本身其实也是一个数字(number),所以要将 type 设置为枚举类型,才能在 属性检查器 中显示为枚举下拉框。enum A{ c, d } Enum(A); @ccclass("test") export class test extends Component { @property({type:A}) accx:A=A.c; }
override参数
所有属性都将被子类继承,如果子类要覆盖父类同名属性,需要显式设置 override 参数,否则会有重名警告:
@property({type:CCString,tooltip:"my id",override:true})
private _id = "";
@property({displayName:"Name",override:true})
private _name = null;
private get name(){
return this._name;
}
更多参数内容请查阅 属性参数。
GetSet 方法
在属性中设置了 get 或 set 以后,访问属性的时候,就能触发预定义的 get 或 set 方法。
get
在属性中设置 get 方法:
@property({type:CCInteger})
private _num = 0;
private get num(){
return this._num;
}
get 方法可以返回任意类型的值。
这个属性同样能显示在 属性检查器 中,并且可以在包括构造函数内的所有代码里直接访问。
class Sprite{
_width: number;
constructor() {
this._width = 128;
console.log(this.width); // 128
}
@property({type:CCInteger})
private width = 0;
private get width(){
return this._width;
}
};
请注意:
设定了 get 以后,这个属性就不能被序列化,也不能指定默认值,但仍然可附带除了
default,serializable外的大部分参数。@property({type:CCInteger,tooltip: "The width of sprite"}) private _width = 0; private get width(){ return this._width; }get 属性本身是只读的,但返回的对象并不是只读的。用户使用代码依然可以修改对象内部的属性,例如:
@property _num=0; private get num(){ return this._num; } start(){ consolo.log(this.num); }
set
在属性中设置 set 方法:
@property({type:CCInteger})
private _width = 0;
set(value){
this._width = value
}
set 方法接收一个传入参数,这个参数可以是任意类型。
set 一般和 get 一起使用:
@property
_width=0;
private get width(){
return this._width;
}
set(value){
this._width = value;
}
如果没有和 get 一起定义,则 set 自身不能附带任何参数。
和 get 一样,设定了 set 以后,这个属性就不能被序列化,也不能指定默认值。