[TOC]
## 类 class
类是一个比较特殊的概念,我自己也是花了很长时间才理解透彻。
记得MEA群里有个编程大神是这样给我解释的,类可以理解成蓝图,或者说模板。说实话当时仍然没有太理解,直到后来自己把它从头到尾探索了一遍。
在面向对象的编程语言中,几乎都有类这个概念。类也是面向对象这种思路的具体体现。
~~~
void Main(){
}
class Student
{
}
~~~
这里的Student就是一个类,class是它的定义,Student是它的类名。类在定义的时候是不需要()的,只需要紧跟一个花括号。
### 一、类的成员变量(类的属性)
类是对现实中实际物体的模拟,设想游戏中的怪物。我们给它设计一个名字、生命值、位置坐标、移动速度几个属性。并且所有的怪物都有这些属性,这时我们就可以使用一个类来封装这个怪物
~~~
class Monster
{
public string Name; //名字
public int Health; //生命值
public Vector2I Position; //位置坐标
public Vector2I Velocity; //移动速度
}
~~~
这些都是这个类的成员变量,意味着这个类有这些属性。
>[danger] 这个概念一定要先背下来,类可以有成员变量,这些都是变量,完全符合前文对变量的解释和用法。
>
> 这里在变量类型前面还增加了一个public 关键字,它是必须的,它表示这个成员变量,在类的外面可以访问到。如果改为private表示私有变量,只允许在类里访问。
>
>类中可以看做一个新的编程区域,在类的内部,也有公共变量和局部变量。
>
>类中的变量都被保护在类之中,与外部是两个不同的编程空间。
>
>因此当类中存在一个变量与类外面的公共变量重名,在类里你使用的时候,使用的是类里面这个变量而不是外部的公共变量。
### 二、类的成员函数(类的方法)
其实上面我们已经创造好了一个怪物的模板,所有的怪物都有名字、生命值、坐标、速度这几个属性。接下来我们还可以定义一些所有怪物都通用的方法,或者说函数。比如移动
~~~
class Monster
{
public string Name; //名字
public int Health; //生命值
public Vector2I Position; //位置坐标
public Vector2I Velocity; //移动速度
public void Move(){
this.Position = this.Position + this.Velocity;
}
public bool isDie(){
return (this.Health <= 0);
}
}
~~~
我们给怪物增加了两个函数,实际上把函数说成方法,也是在类的基础上。因为类是某种实体的抽象,而函数就是对这个实体进行操作的方法。
上面我们定义了两个方法
Move()方法,它通过怪物当前的速度来改变它的位置,这就实现了移动。
isDie()方法,它是一个bool类型的方法,可以返回一个true或false结果,用来判断这个怪物是否死亡。我们规定生命值<=0就算死亡。
为什么要把这些类中呢?往下看
### 三、类的实例化
~~~
void Main(){
//实例化两个Monster类,并赋值给变量M_A和M_B
Monster M_A = new Monster();
Monster M_B = new Monster();
//这时我们可以理解为,M_A和M_B是使用Monster模板创造出来的两个怪物,它们都有名字、生命值这些属性,并且它们的属性是不互相影响的
M_A.Name = "普通怪物";
M_B.Name = "精英怪物";
M_B.Health = 1000; //让B作为精英怪物,血量改为1000。此时M_A的血量依然是默认的100,它们互不影响。
Monster Winer = Fight(M_A, M_B);
Echo(Winer.Name); //此时会显示 “精英怪物”
}
//定义一个函数,返回Monster类型变量,传入两个Monster类型变量。判断谁的血量高就返回谁
Monster Fight(Monster A, Monster B){
if(A.Health > B.Health){
return A;
}else{
return B;
}
}
class Monster
{
public string Name; //名字,声明同时不赋值,默认为""空字符串。
public int Health = 100; //生命值,默认为100
public Vector2I Position; //位置坐标
public Vector2I Velocity; //移动速度
public void Move(){
this.Position = this.Position + this.Velocity;
}
public bool isDie(){
return (this.Health <= 0);
}
}
~~~
在主函数中,我们可以使用new关键字来实例化一个类,这是个固定的写法。当你实例化了一个类,它就会获得独立的一个内存空间,它的所有成员变量都会得到储存。当你需要访问它的某个成员变量时,使用M_A.Name来访问。访问它的成员方法,也是类似,区别在于需要加个括号。
>[danger] 注意这几点
>1. 使用new关键字来实例化一个类,并赋值给这个变量。这个变量在实例化的同时就自动产生了一些成员属性,这是由你自己来定义的。
>2. 使用Monster.Name来访问它的成员变量。使用Monster.Move()来访问它的成员函数。
>3. 同一个类可以实例化出多个变量,它们之间是相互独立互不干扰的。
>4. 在类中使用this关键字来指代它自己。例如Move()函数,M_A.Move()会让M_A这个怪物移动,但不会让M_B移动。因为在对M_A使用Move()的时候,里面的this代表了M_A这个实体,和其他的M_B、M_C都没有半点关系。
实际上这和之前说的变量与函数是完全相似的。
### 四、构造函数
上面的例子中,我们都没有写类的构造函数。构造函数听起来很复杂,实际上就是一个普通的函数,它在类被实例化的时候会执行一次。同时,构造函数允许你将一些初始值传入类中,从而更灵活的实例化类。
~~~
class Monster{
public string Name; //名字,声明同时不赋值,默认为""空字符串。
public int Health; //生命值,默认为100
public int Attack; //攻击力数值
public Vector2I Position; //位置坐标
public Vector2I Velocity; //移动速度
//这就是构造函数
public Monster(int health = 100){
this.Health = health; //生命值等于传入的参数,如果不传入就默认为100
this.Attack = this.Health - 5; //攻击力等于生命值-5
}
}
~~~
这就是构造函数的写法,函数名必须和类名完全一致,它相当于一个函数,当你
~~~
Monster M_A = new Monster(10);
~~~
的时候,这个函数会被执行,并且接收一个int型参数,这个怪物的生命值就可以在实例化的时候控制。
~~~
void Main(){
Monster M_A = new Monster(50); //M_A的生命值为50,攻击力为45
Monster M_B = new Monster(); //M_B的生命值为10,攻击力为5
}
~~~
### 五、类的继承
实际上我考虑了很久才决定写这一小段内容。
举了这么多例子,我想让你明白类是有多方便。类这个东西,就是用来封装属性和方法的。实际上现实中的物体,都可以分解成属性和方法。
当你像上面的例子中,用一个类来封装一个怪物对象时,要求所有的怪物都有这些成员属性和成员方法。即,所有的怪物都必须有名字,可如果有些属性是一部分怪物才有的,怎么办?
下面我们来解决这个问题,下面的内容,简单了解一下即可。
类是可以继承的,来,看看它有多神奇。
当一个类继承了另一个类,它会继承它的所有成员属性,成员函数,甚至是构造函数。
~~~
//我们来设想一个平面游戏场景
//怪物基类
public class Monster
{
public string Name; //怪物的名字
public Vector2I Position; 怪物的坐标,Vector2I也是一个类,包含了int X和int Y两个成员变量。
public Vector2I Velocity; 怪物的速度矢量。
public int Health;//怪物的血量
//构造函数
public Monster(){
this.Name = "怪物";
this.Position = new Vector2I(0,0); //初始坐标 0 0
this.Velocity = new Vector2I(0,0); //初始速度0 0
this.Health = 100;//血量默认100
}
public void Move(){
this.Position = this.Position + this.Velocity;
}
public bool isDie(){
return (this.Health <= 0);
}
}
/*
当一个class在声明的时候在类名后面使用 : 并紧跟另一个类的类名,即表示这个类继承了另一个类。
这个类称为子类,它继承的类叫做它的父类。继承是可以无限制继承下去的,所以可能还会有爷爷、太爷爷类
*/
//火焰怪,它可以攻击别人
public class FireMonster : Monster
{
public int AttackPower = 2; //攻击力 2
public FireMonster(){
this.Position = new Vector2I(-10, 0); //火焰怪初始位置在-10,0
this.Velocity = new Vector2I(2, 0); //火焰怪初始速度是2,,0
}
//子类可以创造新的成员变量或新的成员函数
//这个函数传入一个Monster类变量,并按照火焰怪的攻击力减少它的生命值后返回出去
public Monster Attack(Monster BeAttackMonster){
BeAttackMonster.Health -= this.AttackPower;
return BeAttackMonster;
}
}
//僵尸怪,生命值为-50才判定为死亡
public class ZombieMonster : Monster
{
public ZombieMonster(){
this.Position = new Vector2I(10, 0); //僵尸怪初始位置是 10,0
}
//子类可以重载从父类里继承的函数。重载以后,在调用子类实例化的变量的这个函数时,会运行新的函数里的内容,而不是继承下来的父类的内容。
public override bool isDie(){
return (this.Health <= -50);
}
}
/*
下面我们来写主函数,创建几个怪物并让他们互相攻击
*/
int tick;
void Main(){
tick += 1; //用这个来记录当前是第几个循环
ZombieMoster Z_M = new ZombieMoster();
FireMonster F_M = new FireMonster();
//让两个怪物自由移动
Z_M.Move();
F_M.Move();
//这个方法可以判断两个Vector2I类型坐标是否重合
if((Z_M.Position - F_M.Position).Length() == 0){
F_M.Velocity = new Vector2I(0, 0); //速度设为0,停止运动。可以遇见,在下一帧里,由于两个怪速度都为0,僵尸怪和火焰怪的位置还是会重合的
Z_M = F_M.Attack(Z_M); //把僵尸怪拿给火焰怪打一下,再重新赋值给僵尸怪。
if(Z_M.isDie()){
Echo("游戏结束,僵尸怪挂了");
}
}
Echo(tick.ToString());
Echo(Z_M.Health.ToString());
//可以预见在tick=10的时候,火焰怪预见了僵尸怪,并停了下来,然后一直攻击僵尸怪,直到僵尸怪死亡。
}
~~~
- 序言
- 写在前面的话
- 太空工程师
- MEA小组
- 一、入门
- 1.1 基础概念
- 1.2 编程工具
- 1.3 变量
- 1.4 函数 Function
- 1.5 基本语法
- 1.5.1 运算符
- 1.5.2 if
- 1.5.3 for
- 1.5.4 其他语法
- 1.3 类 Class
- 二、编程块
- 2.1 方块的概念
- 2.2 List<T>结构
- 2.3 获取方块
- 2.4 方块的使用
- 三、Ship 类
- 3.1 简介
- Ship v0.5
- 代码
- 手册(待更新)
- 例子(待更新)
- Ship v1.0
- 代码
- 例子
- 文档
- 实例化
- 内置变量
- 内置方法
- Target类
- 四、运动控制算法在SE中的应用
- 4.1 运动控制介绍
- 4.2 过程控制
- 4.3 震荡和动态误差
- 4.4 误差累加方案
- 4.5 PID算法
- 4.6 对PID算法的一点点简化
- 4.7 一阶惯性系统的PID算法优化的研究
- 五、MEA方块类
- 5.0 核心代码目录
- v1.0核心代码
- v1.1 核心代码
- v2.0 核心代码
- 5.1 类的概念
- 5.2 MEA的方块类(Block)
- 5.3 方块类文档
- 5.4 方块类2.0 全教程
- 5.4.1 安装和使用
- 5.4.2 方块类(Block)
- 5.4.3 显示屏类(DisplayScreen)
- 5.4.4 LCD类(LCD)
- 5.4.5 主控座椅类(Cockpit)
- 六、疯猴的编程笔记
- 第一个程序
- 获取和控制其他块
- 物流与生产
- 界面与通信
- 运动与姿态
- 侦测与导航
- 七、SteamZhou的笔记
- 有趣而花里胡哨的IDEA
- 八、质子对撞炮的笔记
- 属性 Property
- 接口 interface