Cocos Creator 入门笔记

推荐文档https://www.tslang.cn/docs/home.html

(一)、从装机箱谈到面向对象再到游戏引擎

一台电脑的组装是有不同的零部件组成的。再组成一个新的主体电脑。

回到我们写代码,我们之前的写法是一行一行写得代码,一点点去实现。

现在呢,面向对象的写法就是我们先去实现一点点的零部件,然后实现一个总的功能,乃至一个大整体。

游戏引擎: 游戏引擎是指包含游戏必备的一些功能框架以及工具的集合,是决定一款游戏质量高低的核心因素。这里就不列举市面上的一些引擎了。

有了游戏引擎之后,游戏实现变得简单,只需要调用API就可以很快实现,很多功能已经做成了模块等待你去使用。

(二)、Cocos发展史

2008年2月,Python版Cocos2D诞生

2008年6月,Objective-C版Cocos2D for iphone诞生,将Cocos推上高峰。

之后出现了各种语言的cocos版本,如:

Cocos2D-x,Cocos2D-js,Cocos2d-android,Cocos2d-net等等。

在这些版本中,最有历史意义的就是Cocos2d-x,可以轻易做出跨平台版本。之后,从Cocos2D-x诞生出两个分支,一个是给wp系统用的Cocos2D-xna,还一个是Cocos2D-HTML5。

CocosCreator的诞生是为了将Cocos2D-x的纯代码编辑,分解成可视化,脚本化等特点,让更多新人轻松入手。

(三)、编程语言

环境:

搜索Ts在线编辑器:[TypeScript][https://www.typescriptlang.org/play/],前期我们的学习都是在这里验证。这个网页可以轻松转换Ts和Js。

语言区别:

Ts和Js都可以支持,并且Ts是Js的超集,Ts包含了Js的所有功能,同时Ts包含了新特性。

Ts新特性

  1. Js数据类型是弱类型(即可以随意赋值),Ts数据类型添加了强类型(更明确的类型)。

  2. Ts具有面向对象性。

开始学习:

1.输出语句:

// 在控制台上输出HelloWorld!
document.write("HelloWorld!");

2.变量:

用let 或者var 关键字来声明变量

// 我们想要修改输出内容,就要使用变量
// 变量相当于我们开辟了一块内存去存储内容
// 在我们调用的时候就是取得这个地址存储的值
let personName="我是一个善变的变量";
document.write(personName);

标识符命名规则:

标识符是使用字母,数字,下划线组成的。

标识符首位只能使用字母或者下划线。

我们在声明变量的时候,最好使用[驼峰命名法(Camel-Case)][https://https://baike.baidu.com/item/骆驼命名法 ]。

3.常量:

使用const常量关键字来声明常量,常量的内容只能在声明的时候赋值。

const tmp="我是一个不会变的常量"

限定类型:

在声明的时候,变量名后添加:加数据类型,可以限制这个变量的类型。

var personName:string="我被限制成了字符串";

4.数据类型

Ts可以在你没有限定变量类型的时候,会自动与推断它的类型。

特殊值:

undefined:变量未赋值的情况下,它的值就是这个

null:空值,就是空值,是我们希望它为空,主动赋值给它为空。

基本类型:
  • number:数值型,用来存储数字的类型。

  • string:字符串类型,用来存储一段话或者一行字母的类型。字符串只能为一行的,如果我们想要换行(不能使用\n)要使用模板。

    我们使用`来换行。

    ${变量}来写有格式的内容

document.write("`我被换行了`");

let tmp=3;
document.write("我的值是tmp的值是${tmp}");
  • boolean:布尔值,表示是否。

  • any:任何类型,它可以是任何类型。

数组类型:

在声明变量的时候,变量名后添加[]来声明数组变量。

let tmp:number []=[1,2,3,4,5,6];

数组变量的类型可以是任何类型。

值得注意的是,索引是从0开始计数的。

let tmp:number []=[1,2,3,4,5,6];
document.write("这个数字是${tmp[2]}");
// 输出内容应该为3
联合类型:

在限制变量的时候使用|来再添加一种类型,这样可以让一个变量支持两种类型。

let tmp:number|string;
// 现在这个tmp支持数值和字符串类型。
// 可以在使用中当做对应的类型。
// 如果使用时是未联合的类型,那就会报错
枚举类型:

使用枚举来定义属于我们自己的类型。

我们使用关键字enum定义枚举

enum Color
{
	red,
	green,
	yellow
}
类型验证:

我们可以用typeof来确定对应的类型。

document.write(typeof tmp);
// 输出打印的就是tmp的类型
类型别名:
// 这样可以把常用的类型再起个别名,但是时机还是原来的类型名
type NewNumber=number;

5.运算符

三目运算符:
// (判断条件) ?{语句1}:{语句2}
// 判断条件如果为真,执行语句1
// 判断条件如果为假,执行语句2
二目运算符:

算数运算符

算术运算符计算出对应的结果,跟我们生活中的算数相等。

求余计算得出来的是先除法运算,然年后不计算小数位,直接返回余数。

// +  -  * /  % 
// 加 减 乘 除 取余 

比较运算符

比较运算符返回布尔值

// > 	< 	== 	 <= 	>=
// 大于 小于 等于 小于等于 大于等于
// 这些都是值的比较,不比较类型

// ===
// 这个比== 更严苛,值相同,还得类型相等。

// !=
// 不等于,这个也是不比较类型的

// !==
// 这个比 != 更严苛,值不相同,还得类型不相等。

逻辑运算符

// && ||
// 与 或
// 与 一假必假
// 或 一真必真

// !
// 非
// 把后面的布尔值取反

赋值运算符

// += -= *= /=
// 先跟运算符右边的值计算,再赋值给左边的变量
单目运算符:
// ++   -- 
// 自加 自减

注意自加和自减要注意位置,放在变量前面,先计算后输出,放在变量后面,先输出再计算。

let a=3;
let b=3;
let c=3;
let d=3;

document.write(a++);
// 输出3

document.write(++b);
// 输出4

document.write(c--);
// 输出3

document.write(--d);
// 输出2

6.条件控制语句

if和else :
// if(条件){执行语句}
// 如果条件为真,那就执行后面的执行语句
// 否则不执行

// if(条件){执行语句1}else{执行语句2}
// 如果条件为真,那就执行执行语句1
// 否则执行执行语句2

// if(条件1){执行语句1}else if(条件2){执行语句2}
// 如果条件1为真,那就执行执行语句1
// 否则执行判断条件2,如果为真执行语句2
// 否则不执行
switch :
enum Color
{
	red,
	green,
	yellow
}
Color color=Color.red;
switch(color)
{
    case(Color.red):
        break;
        // 当color等于Color.redw,就执行这里
	case(Color.green):
        break;
        // 当color等于Color.green,就执行这里
	case(Color.yellow):
        // 当color等于Color.yellow,就执行这里
        break;
    default:
        // 其他状态,指上面的也不通过条件,就执行这里
        break;
}

7.循环控制语句

while:
// While(条件){执行语句}
// 当条件为真,那就执行执行语句,执行完毕后,再判断条件。
// 以此进行循环,注意死循环情况,要能跳出。
for :
// for(初始化;条件;控制跳出)
// {
//		执行语句
// }
// 初始化只在最初进入循环进行一次
// 然后每次循环前都判断条件,是否能进行
// 控制跳出则是在每次执行循环后执行
// Example:
let max=5;
for(let i=0;i<max;i++)
{
	document.write(i);
}

8.跳出循环

break:
// break 结束最近循环,
contiue:
// 跳过当前这一次的循环,直接进入下一次的循环

9.函数

// 关键字 函数名 参数列表
// 函数相当于流水线,我们输入参数,得到返回值
// Example 这个例子没有返回值
function func(char :string)
{	
let arr:string[]={"a","b","c"};
    for(let i=0;i<5;i++)
    {
        if(char==arr[i])
        {
        document.write("当前是第"+i+"个字符");
        }
    }
}

// Example 这个例子有返回值
function  add(num1:number,num2:number):number// 这里控制返回类型
{
    // return后会直接返回函数,后面的不会执行
 	return num;   
}

// Example 特殊的写法,这个add既可以当变量赋值,也可以当方法名
let add2=fubction()
{
    
}

//Example
let add3=()=>{}
// 调用语句
func('a');
let test=add(3,4);
add2();

10.面向对象

类:相当于模具,是一个抽象的概念。这是由程序员自己定义的。

成员属性:类中的睡醒,需要从类中去访问。抽象解释,这个类的属性,比如人的身性别。

成员方法:类中的方法,需要从类中去访问。抽象解释,这个类的功能,比如人会说话。

对象:实体,具体的实例,需要使用关键字new来声明。

声明类关键字:Class

面向对象的三种特性:继承、多态、封装

继承不能写成**:,要使用extends**关键字。

类中的可访问属性和方法,在类外访问都要用**.**的形式去访问。

类的构造方法

使用关键字constructor

constructor(){}这是无参的构造方法。

也可以根据需求来添加参数。

构造方法不能被调用,默认是在类在被实例化时自动调用。

静态属性,静态方法

静态属性虽然可以可以写在类里,但是实际上并不属于对象,静态的属性或者方法,都是在程序刚运行时就开始静态初始化了。只能通过类名.的形式来调用。

只要添加static,那么他就是静态的。

class Person
{
    // 静态属性
    static des:string="这是一个person类";
    // 默认值,也叫缺省值
	name :string="默认";
	age:number=0;
	// 无参构造方法
	constructor()
        {

        }
	// 有参构造方法

constructor(name :string, age :number)
        {
			this.name=name;
            this.age=age;
        }
   say()
        {
		document.write(this.name);
        }
}
// 实例化方法
let a=new Person()
{
	a.name="我的名字";
    a.age=20;
    a.say();
}
继承
// 继承
class Person
    {
        name:string="";
        say()
        {
            docyment.write("我是人类,叫做"+this.name);
        }
    }
// 学生类继承了人类
class Student extends Person
    {
        // 这个学生继承了人类的所有属性和方法
        // 现在这里创建的都是学生独有的属性
        num:number=0;
        score:number=0;
        // 如果我们写得新方法跟父类重名,则会覆盖掉父类
        // 如果我们不想覆盖,想对方法进行扩充
        say()
        {
            // 这里super关键字代表父类
            super.say();
             docyment.write("我是学生,叫做"+this.name);
        }
    }
let a=new Student();
a.name-"aaa";
// 
a.say();
抽象类

继承自抽象类的子类,必须重写父类的抽象方法。

抽象类关键字:abstract

// 抽象类本身不能被实例化为对象,只能用作继承。
abstract class Person
{
    name :string=”“;
// 抽象类中可以存在正常方法
run()
{}
// 抽象方法在声明时不能实现
abstract say();
}

class Student extents Person
{
    // 重写抽象方法
	say();    
}


父类指针可以指向子类对象

这是面向对象非常重要的一点

let a:Person=new Student();
a.say();

11.接口

因为TS只能单继承自一个类,所以我们要实现接口。多对多会导致出错,接口就是为此产生的。

接口时用来实现的,继承接口关键字implements

// 人类
class Person{
	name:string;
}
// 狼接口
interface IWolf
{
	attack();
}

interface IDog
{

}
// 狼人
class wolfMan extends Person implements IWolf,IDog  
{
    attac()
    {}
    eat()
    {}
}

12.属性寄存器

我们不希望用户直接调用我们的字段,我们为了安全性,以及更多的可操作性。我们使用属性寄存器

// 属性寄存器
class Person
{
	_hp:number=100;
	// 取值
	get hp()
	{
		return this._hp;
	}
	// 赋值
    set hp(value)
    {
        if(value<0)
            {
                this._hp=0;
            }
        else

            {
				this._hp=value;
            }
    }
}

let a=new Person();
a.hp -=180;
document.write(a.hp+"");

13.命名空间

同名的类和方法,或者多人合作,在编译时的解决方案。

通过命名空间,来统一管理。相当于在我们写得代码先加上一个标记

关键字namespace

namespace aa{
	class Person
	{
		name:string;
	}
}
namespace bb
{
	class Person{}
}

我们的类想要在命名空间外使用,就需要使用export关键字。

namespace aa{
	export class Person
	{
		name:string;
	}
}
namespace bb
{
	export class Person{
	name:string;
	}
}
let person=new aa.Person();
let person2=new bb.Person();

14.泛型

我们在写代码时可能这个方法要的参数类型,我们并不确定,但是在方法里面的代码是需要确定类型的。

/*
// 这种是我们之前的写法
function add(num:any):any{
    if(typeof num =="number")
        {
			num++;
        }
    return num;
}
*/
// 现在使用泛型
function add<T>(num:T):T
{
	      if(typeof num =="number")
        {
			num++;
        }
    return num;
}
document.write(add<string>+"");

两种方法的比较,第一种虽然也是实现了需求,但是很有可能造成传进去的参数和传出来的值类型不相同,这太随意了。而泛型则确定了传进去的是我们自定义的类型,传出来的也是我们要用的类型。尽量使用泛型来满足这一类需求。

15.集合

元组

元组不限定类型,我们可以使用它来传回一串数据。

let hero:[string,number]=["超人",100];
hero[0]=”蝙蝠侠“;
document.write(hero[0]);
// 打印输出蝙蝠侠
数组
// 数组
let array1:number[]=[1,2,3];
let array2:Array<number> =new Array<number>();
// 长度
 document.write(array1.length+"");
// 在数组后面追加元素
array1.push(4);
arrat1.unshift(0);
// 删除最后面的元素
array1.pop();
// 从第几位开始删除参数到第几位
array1.splice(0,2);
// 删除最前面的
array1.shift();
// 合并两个数组
array1=array1.concat(array2);
// 查找元素位置
let index=array1.indexof(3);
// 数字排序
array1.sort();// 正序 升序
array.reverse(); // 倒序 降序

字典

我们自定义键的类型,也能自定义值

let dic:{[key:string]:string}={
    "name":"王小虎",
    "name2":"李逍遥"
};
// 如果我们给的键是空的,那么自动填充。
dic["name3"]="灵儿";

16.回调

// 实现回调
function func(value:function)
{
    // ...
    value();
}
function test()
{
    document.write("test");
}
func(test);

func(function(){
    document.write("这是匿名函数");
})

func(()=>{ document.write("这也是匿名函数");});

17.正则表达式

测试工具

网上搜索在线正则表达式

正则表达式

正则表达式使用单个字符串来描述、匹配符合某个句法规则的字符串。在很多文本编辑器中,正则表达式通常用来检索、替换、那些符合某个模式的文本。

原义文本字符

正常的文本字符

元字符

具有特殊意义的专用字符,是代替正常文本字符的字符。

. 匹配除换行符以外的任意字符

\w 匹配字母或者数字或者下划线或汉字

\s 匹配任意的空白符

\d 匹配数字

\b 匹配单词的开始或结束

^ 匹配字符串的开始

$ 匹配字符串的结束

[a-z] 限定小写字符a到z

[A-Z]限定大写字符A到Z

[0-9]限定数字0-9

如果多个限制条件直接

[a-zA-Z]大小写都支持

限定符

限定匹配的数量或特殊条件

{n} 重复n次

{n,} 重复大于等于n次

{n,m} 重复n次到m次

***** 重复大于等于0次

+ 重复大于等于1次

? 重复0次或1次

例子

常用功能建议百度,因为网上版本可能写得比你好

  • 全数字:[0-9]$
  • 电话号: \d{3}-\d{8}-\d{7}
  • QQ号: [1-9] [0-9]{4,}
  • 账号(字母开头,5-16位,允许字母数字下划线)
  • [a-zA-Z] [a-zA-Z0-9]{4,15}$
// 在代码中使用正则表达式
let reg=/\d{4}-\d{7}g;
let str="03450-1234567";
let res=reg.exec(str);
document.write(res.length+"");
// 匹配到的内容
res.forEach(function(value,index))
{
document.write(value+""+index);            
            }

18.访问修饰符

public

公开的

  • 这种是完全公开的无论是在外界或者子类里面
private

私有的

  • 这种只能被类自身调用,包括子类都调用不了
Protect

受到保护的

  • 这种只能被类自身和子类可以调用,在外界不能调用

使用方法,在类中声明属性或者方法时,在方法名前添加访问修饰符。

19.单例模式

单例模式,也叫单子模式,是一种常用的软件设计模式,属于创建型模式的一种。在应用这个模式时,单例对象的必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

话不多说,单例模式大家应该很熟悉了,所以我们直接写单例

// 第一种写法 饥汉法
class SoundManager{
	static Instance =new SoundManager; 
	private static instance:SoundManager;
	private constructor ()
	{}
}
// 我们直接调用属性就好了
SoundManger.Instance;
// 第二种写法
// 懒汉
class SoundManager{
	private static instance:SoundManager;
	static Instance()
	{
	
        if(!SoundManager.instance)
        {
            SoundManager.instance=new SoundManager();
        }
        return instance;
	}
}
// 我们调用方法
SoundManager.Instance();

20.代理模式

代理模式: 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

实际场景

实体创建比较费时:在等待期间给出提示;
本体创建出来占用内存过大: 等到用到这个实体的时候再去创建。
系统的权限控制: 用来过滤请求。

代理模式的结构

  • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
// Sample Example

// 抽象角色
interface ICalc
    {
        calc(num1,num2):number;
    }
// 代理
class Npc1 implements ICalc
    {
        calc(num1,num2){
            return num1 +num2; 
        }
    }
// 代理
class Npc2 implements ICalc
    {
        clac(num1,num2)
        {
            return num1 -num2;
        }
    }
// 真实角色
class Person
    {
        delegate:Icale;
        // 计算数字
        GetNum(num1,num2){
            // 拿到num1和num2计算后的结果
            let num=this.delegate.cale(num1,num2);
            document.write(num+"");
        }
    }
let person=new Person();
// 设定一个代理
person.delegate=new Npc2();
person.GetNum(3,4);

代理模式和装饰者模式的区别

代理模式和装饰者模式都是对真实对象进行修饰。
代理模式一般不会添加额外的方法,最多会加一些权限校验的方法。而装饰者模式就是为了对真实对象扩展而存在的。

代理模式的利弊

利:代理模式可以推迟大内存对象的创建到其他元素加载完毕之后,这往往能给用户带来一种速度大幅提升的感觉。
在较长时间的操作增加“正在加载等提示”。将权限系统的权限判断和实际操作分离开。
弊:将大对象推迟创建后,用户在第一次使用时会感觉很慢而大吃一惊。在不恰当的场合使用会增加无谓的复杂性,还不如直接访问本体轻松。

21.观察者模式

很多对象去监听同一个物体,当那个物体发生改变,需要通知所有的对象。

观察者模式

观察者模式: 观察者模式是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

实际场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

观察者模式的结构

  • Client: 客户端
  • Subject: 通知者
  • Observer: 观察者
// Example
interface IObserver
    {
        nameChanged(newName);
    }
class Person
{
	private _name:string;
    
    // 所有的观察者
    observers:Array<IOserver>=new Array<Iobserver>();
    set name(value)
    {
        this._name=value;
        // 发生变化
        // 遍历所有的观察者,给所有的观察者发消息
        for(let observer of this.observers)
            {
                observer.nameChange(this._name);
            }
    }
    get name()
    {
		return this._name;        
    }
}
class Test implements IOserver{
    nameChanged(newName)
    {
        document.write("监听到变化,名称变为"+newName);
    }
}
let person=new Person;
person.name="嘎嘎";
Test test=new Test();


22.工厂模式

简单工厂模式

简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。

定义

定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。

适用场景:
  其实由定义也大概能推测出其使用场景,首先由于只有一个工厂类,所以工厂类中创建的对象不能太多,否则工厂类的业务逻辑就太复杂了,其次由于工厂类封装了对象的创建过程,所以客户端应该不关心对象的创建。总结一下适用场景:
  (1)需要创建的对象较少。
  (2)客户端不关心对象的创建过程。

// Example

enmu CarType{
	Bmw,
	Audi,
	Benz
}
// 父类
class Car
{
	name:string;
    // 工厂方法
    static Create(carType:CarType):Car
    {
        let car:Car;
        switch(carType)
        {
            case   CarType.Bmw:
                car=new Bmw();
                break;
                
   			case carType:CarType.Audi:
            car=new Audi();
                  break;
		
            case   CarType.Benz:
                car=new  Benz();
                break;
      
    	}           
	}
}
class Bmw extends Car{}
class Benz extends Car{}
class Audi extends Car{}
let car =Car.Creat(carType.Bmw);

23.链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。 每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。

简单来说,就是在物理存储上并不是连续,有顺序的,我们通过逻辑将其串联起来。

遍历起来,数组最快。修改起来,链表最快。

// 范例
class  Person
    {
        name :string;
        next:Person;
        constructor(name)
        {
			this.name=name;            
        }
    }
let person=new Person("李逍遥");
person.next=new Person("王小虎");
person.next.next=new Person("赵灵儿");

// 删除
person.next=person.next.next;
// 添加
let tmpPerson=new Person("赵四");
tmpPerson.next=new Person("柳如烟");
tmpPerson.next=person.next;


while(person)
    {
        dpcument.write(person.name);
        // 让节点后移
        person=person.next;
    }

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值