上一篇文章中,通过layout实现了scroollview的滚动,此时数据是固定的,为了使数据活起来,就需要写代码来实现, Cocos Creator
支持JavaScript
和TypeScript
。 一般使用的是TypeScropty
。
关于TypeScript与JavaScript的区别,可以访问教程 | 为什么选择使用 TypeScript ?
如果新建的cocos项目用的是Hello World
,那么他默认是JavaScript的语言,此时需要添加一些基本配置文件,让VS Code 更好的支持TypeScript。
开启方法很简单:
如果希望在原有项目中添加 TypeScript 脚本,并获得 VS Code 等 IDE 的完整支持,需要执行主菜单的 开发者 -> VS Code 工作流 -> 更新 VS Code 智能提示数据 和 开发者 -> VS Code 工作流 -> 添加 TypeScript 项目配置
结合官网的入门教程和阮一峰老师的TypeScript 教程的一些内容,先简单的写一些基础代码来熟悉TypeScript的写法。
设置脚本编译器
由于Cocos Creator本身不支持脚本编辑,我们编码的工具需要接触第三方的代码编辑器,比如常用的Vim, Sublime Text, Web Storm, VS Code...等。刚下载安装的Cocos Creator创建的脚本,双击打开,会跳转到网页,需要对其进行设置默认编译器。
属性
const {ccclass, property} = cc._decorator; // 从 cc._decorator 命名空间中引入 ccclass 和 property 两个装饰器
@ccclass // 使用装饰器声明 CCClass
export default class NewClass extends cc.Component { // ES6 Class 声明语法,继承 cc.Component
@property(cc.Label) // 使用 property 装饰器声明属性,括号里是属性类型,装饰器里的类型声明主要用于编辑器展示
label: cc.Label = null; // 这里是 TypeScript 用来声明变量类型的写法,冒号后面是属性类型,等号后面是默认值
// 也可以使用完整属性定义格式
@property({
visible: false
})
text: string = '这是一个属性';
string1 : string = '1'
// 成员方法
onLoad() {
}
}
对比这2行代码,不会报错,但是表示的意思有不一样。
string1 : String = '1'
string2 : string = '1'
ChatGPT是这样解释的
在 TypeScript 中,
String
和string
是两种不同的数据类型表示方式。
String
:String
是 JavaScript 中的原始包装对象,它是一个构造函数,可以用来创建字符串对象。因此,string1 : String = '1'
这行代码创建了一个字符串对象,而不是一个普通的字符串。string
:string
是 TypeScript 中表示字符串类型的关键字,它表示的是普通的字符串类型。因此,string2 : string = '1'
这行代码创建了一个普通的字符串类型变量。
总的来说,String
是 JavaScript 中的字符串对象构造函数,而string
是 TypeScript 中表示字符串类型的关键字。因此,在大多数情况下,我们应该使用string
来表示字符串类型。
类型的声明
properties
的方式声明
properties: {
// 声明的属性为基本 JavaScript 类型时,可以直接赋予默认值:
height: 20, // number
type: "actor", // string
loaded: false, // boolean
target: null, // object
// 声明的属性具备类型时(如:cc.Node,cc.Vec2 等),可以在声明处填写它们的构造函数来完成声明
target1: cc.Node,
pos: cc.Vec2,
// 当声明属性是一个数组时,可以在声明处填写它们的类型或构造函数来完成声明
any: [], // 不定义具体类型的数组
values: [cc.Vec2],
nodes: [cc.Node],
frames: [cc.SpriteFrame],
// 完整声明
// 有些情况下,我们需要为属性声明添加参数,这些参数控制了属性在 属性检查器 中的显示方式,以及属性在场景序列化过程中的行为
score: {
default: 0, // 设置属性的默认值
displayName: "属性检查器上展示的文案",
tooltip: "鼠标放上去的提示文字",
visible : false, // false的情况下, 属性检查器上不会显示该属性
type : number, // 限定属性的数据类型
serializable : false , // 设为 false 则不序列化(保存)该属性
}
// 数组的 default 必须设置为 [],如果要在 属性检查器 中编辑,还需要设置 type 为构造函数,枚举
names: {
default: [],
type: [string] // 用 type 指定数组的每个元素都是字符串类型
},
enemies: {
default: [],
type: [cc.Node] // type 同样写成数组,提高代码可读性
},
}
property
的方式声明
// 声明String
//显式声明类型String
@property(cc.String)
string2 = '';
//显式声明类型String
@property({
type:cc.String
})
string3 = '';
// 隐式声明类型,根据默认值匹配其类型
@property string4 = '';
// 声明数组
// 指定数据中元素的类型为node
@property([cc.Node])
public myNodes: cc.Node[] = [];
// 指定数据中元素的类型color
@property([cc.Color])
public myColors: cc.Color[] = [];
@property
public colors = [];
@property({
type : [cc.color]
}) color3 = [];
方法
显式声明方法的返回值
// 有返回值
function addNum(params:number ) : number {
params = params ++;
console.log(params.toString);
// 此时如果不return 会报错
return params;
}
// 无返回值
function addNum(params:number ) : void {
params = params ++;
console.log(params.toString);
// 此时return 会报错
return params;
}
不声明方法的返回值,在这种情况下, 你给不给返回值都可以
function addNum(params:number ) {
params = params ++;
console.log(params.toString);
// 此时return或不return 都不会报错
return params;
}
不同参数个数的写法
// 无参数
function addNum1() {
addNum2(1);
}
// 1个参数
function addNum2(params : number) {
addNum3(params,1);
}
// 2个参数
function addNum3(params1:number,params2:number) {
params1 = params1 + params2;
console.log(params1.toString);
return params1;
}
可选参数
上面的方法 如果定义了参数, 就必须要传入相应的参数,否则报错。
function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // 错误,缺少参数
let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确
如果想要参数为可选,可选参数使用问号标识 ?
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // 正确
let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确
自定义属性挂载到属性检查器中
在Hello.ts
文件中,定义出多个属性。保存后,将其拖到属性检查器中,查看解析出来的自定义属性。
const {ccclass, property} = cc._decorator;
@ccclass
export default class HelloClass extends cc.Component {
// 这些会被解析成属性并展示在属性检查器中
@property(cc.Label)
label: cc.Label = null;
@property({
type: cc.String
})
string1 = null;
@property
string2 : string = '属性String2';
@property(cc.String)
string3 = '属性String3';
@property
text: string = '属性Text';
@property
myOffset = new cc.Vec2(100, 100);
@property([cc.Node])
myNodes: cc.Node[] = [];
@property([cc.Color])
protected myColors: cc.Color[] = [];
@property private text1 = '';
// 这些不会被解析成属性并展示在属性检查器中
@property public colors = [];
@property({
type : [cc.color]
}) color3 = [];
// 成员方法
onLoad() {
}
funLoad(){
}
start () {
}
}
生命周期
onLoad
这个方法类似于iOS的- (instancetype)init;
(?)
onLoad
回调会在节点首次激活时触发,比如所在的场景被载入,或者所在节点被激活的情况下。在 onLoad
阶段,保证了你可以获取到场景中的其他节点,以及节点关联的资源数据。onLoad
总是会在任何 start
方法调用前执行,这能用于安排脚本的初始化顺序。通常我们会在 onLoad
阶段去做一些初始化相关的操作。
// 简易写法
onLoad() {
}
// 标准写法
protected onLoad(): void {
console.log('----------onload'+this);
}
start
这个方法类似于iOS的- (void)viewDidLoad;
(?)
start
回调函数会在组件第一次激活前,也就是第一次执行 update
之前触发。start
通常用于初始化一些需要经常修改的数据,这些数据可能在 update 时会发生改变。
protected start(): void {
console.log('----------start'+this);
}
update
这个方法类似于iOS的- (void)viewWillLayoutSubviews;
(?)
游戏开发的一个关键点是在每一帧渲染前更新物体的行为,状态和方位。这些更新操作通常都放在 update
回调中。
我的Demo中放了一个Scrollview,上一篇中的步骤进行操作,但是在打印日志中,看到这个方法在不停的被调用。 查看打印日志的方法: 键盘按键
F12
lateUpdate
这个方法类似于iOS的- (void)viewDidLayoutSubviews;
(?)
update
会在所有动画更新前执行,但如果我们要在动效(如动画、粒子、物理等)更新之后才进行一些额外操作,或者希望在所有组件的 update
都执行完之后才进行其它操作,那就需要用到 lateUpdate
回调。
onEnable
当组件的 enabled
属性从 false
变为 true
时,或者所在节点的 active
属性从 false
变为 true
时,会激活 onEnable
回调。倘若节点第一次被创建且 enabled
为 true
,则会在 onLoad
之后,start
之前被调用。
onDisable
当组件的 enabled
属性从 true
变为 false
时,或者所在节点的 active
属性从 true
变为 false
时,会激活 onDisable
回调。
onDestroy
这个方法类似于iOS的- (void)dealloc;
(?)
当组件或者所在节点调用了 destroy()
,则会调用 onDestroy
回调,并在当帧结束时统一回收组件。当同时声明了 onLoad
和 onDestroy
时,它们将总是被成对调用。也就是说从组件初始化到销毁的过程中,它们要么就都会被调用,要么就都不会被调用。
生命周期整个流程
一个组件从初始化到激活,再到最终销毁的完整生命周期函数调用顺序为:onLoad
-> onEnable
-> start
-> update
-> lateUpdate
-> onDisable
-> onDestroy
。
其中,onLoad
和 start
常常用于组件的初始化,只有在节点 activeInHierarchy
的情况下才能调用,并且最多只会被调用一次。除了上文提到的内容以及调用顺序的不同,它们还有以下区别:
节点激活时 | 组件 enabled 时才会调用? | |
---|---|---|
onLoad | 立即调用 | 否 |
start | 延迟调用 | 是 |
实战制作小游戏
有了之前的基础,可以开始自己动手根据官网的步骤制作一个小游戏,按照官网操作,除了的一些API调用,其他都很流畅。