Unity 3D游戏编程自学#2——C#面向对象

本文深入解析C#面向对象编程,涵盖类与对象、字段与属性、方法、继承、多态、封装等核心概念,配以实例讲解,助您掌握OOP精髓。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.类与对象

类,简单来说就是高级版的结构体。类是用于描述一类事物的,而对象是某一种具体的类。比如说有一个类叫做“玩家”,类里面描述的是“玩家”的具体信息,那么小明就是属于“玩家”这类的一个具体的对象。

创建一个类:

举个例子,我们创建了一个叫Player的类:

之后我们在Main函数中我们就可以调用这个类了:

Player xiaoming = new Player();

接下来介绍类中的三大成员:字段、属性、方法

 

2.字段和属性

描述类的变量都不叫变量,叫字段。

字段的取值、赋值与结构体一样,字段命名前都要加public。

如果不想让外部程序可以直接访问类中的字段,可以不用public,改为private。(public和private叫做访问修饰符,也称为权限修饰符)

这样一来,在Main函数中就无法直接对该字段进行取值、赋值。(但在类中该字段可以被随意访问)

每个Private字段都需要一个唯一对应的属性来进行操作:

属性语法:

public 数据类型 属性名

{
    get { return 字段名; }
    set { 字段名 = value; }
}

get函数用于取得对应字段的值,set用于赋值。

简写版,即自动属性(与上图功能一样,上图的叫普通属性):

public 数据类型 属性名 { get; set; }

在属性内部可以通过代码防止字段被恶意赋值。(比如在set里面加入if语句等)

例子:

属性名一般与字段名一样(不过属性名开头字母大写,规则同帕斯卡命名法)。如果要对字段进行特殊处理,则可在set函数中进行编写。

在Main函数中调用:

xiaoming.Level = 18;
Console.WriteLine(xiaoming.Level);

 

3.方法

类中的函数叫做方法。

方法有三种:普通方法、构造方法以及析构方法。

普通方法:

与正常函数的使用一样,不过前面要加访问修饰符(可private也可public)

构造方法:

即C++中的构造函数,一般用于初始化类中字段。格式:

public 类名()
{

}

注意:构造方法的名字一定与类名一模一样,而且不能是private型的。

构造方法无返回值,也不用写void。

构造方法可重载,若没有写构造方法,系统会自动生成一个。

例子:

赋值时需要注意:要给类中的字段赋值,则必须在前面加上this修饰。

其实不加this也可以,但加this,一方面是为了防止与方法中的参数或者某些局部变量区分,另一方面也是为了减少bug。

调用的时候:

Player xiaoming = new Player(18);

若写成重载,则可以有多种初始化形式。

对象初始化器:对于有多个字段的类,可能需要写多个参数不同的构造方法(有时这几个字段需要初始化,有时那几个又要)。为减少代码量,可以使用对象初始化器(前提:相关字段的属性编写完成)

例子:

假设Person极多字段,现在我只想初始化name和age,这时我不需要再写一个仅有name和age两个参数的构造方法,仅需要完成这两个字段的属性,然后:

Person p1 = new Person() { Name = "Z", Age = 12 };

大括号内的属性数不限。

注意:要使用对象初始化器,类中至少要有一个无参的构造方法(什么构造方法都没有其实也可以)。

析构方法

即析构函数,用于销毁无用对象,不写系统会自动生成,对象销毁时系统会自动调用(无论是自己写的还是系统生成的)。

格式:

 ~类名()
{

}

析构方法不能有任何参数。

 

4.堆栈关系

对象是一种引用类型,若一个对象由另一个对象赋值而来,则他们的值会同步变化。

Player p1 = new Player(18);
Player p2 = p1;
p2.Level = 10;

具体一点,程序执行时,生成的p1放在了栈空间(这一步叫做对象的实例化,通过实例化关键字new实现),但p1的具体数值被放在了堆空间,因此栈空间的p1指向了堆空间中存储自己数据的地方。当把p1赋值给p2时,p2指向了堆空间中相同的地方,因此无论改变哪个,两者的值都会变。

 

5.命名空间

用于对代码进行分类管理,一般是类的集合。

定义:namespace 空间名称 { 类 }

调用:using 空间名称

 

面向对象开发三大特点:继承、多态、封装

6.继承

为了方便以后理清子类父类之间的关系,可用Visual Studio自带的视图功能:

Visual Studio 2017要另外装Visual Studio extension development中的Class Designer才能拥有此功能。

class 子类名:父类名
{

}

子类会继承父类所有的成员(字段、属性和方法,无论是private还是public,这些都能继承到,除了构造方法),但所有父类中的private成员子类都是无法访问的。

一般类中的字段写成private,属性和方法写为public。

在子类中,若要访问父类的public成员:

base.字段名

base.属性名

base.方法名()    若有参数还要带上参数

构造方法随不能继承,但子类也可调用父类的构造方法:在子类构造方法的后面直接加上: base(参数)

例子:

public Son(int level) : base(level)
{
}

base中不用写数据类型(调用父类的普通方法时也一样),参数名必须和Son构造方法的参数名一致,说明是把子类构造方法的参数传给父类。

虽然子类在使用父类的public成员时需要加base,但在Main函数函数中子类的对象可以直接调用(子类对象名.父类成员)。

若子类对象之间直接赋值,仍存在引用传递的现象,无论这个字段是子类的还是父类的。

 

7.多态

不通过增加子类的成员,使继承同一父类的多个子类有各自不同的、有别于父类的特性。

有三种方法实现多态:虚方法、抽象类、接口。

虚方法:

父类的方法中加入关键字virtual,子类的方法加入关键字override:

例子:父类名为User,有方法“技能”

public virtual void JiNeng()
{
    Console.Write("玩家使用了技能:");
}

子类Mage:

实例化后输出为:玩家使用了技能:火焰冲击

在使用虚方法时,一般会在父类的方法中实现所有子类共有的东西(比如初始化),而在子类中加上个性化的代码。

若不需要父类中的初始化,base.JiNengj()是可以删去的。

抽象类:

若某父类普通方法无需初始化,或者只能交给子类完成,仅在父类实现一个定义作用(即父类方法无任何操作),则可以把该方法写成抽象方法。

abstract class User
{
    public abstract void JiNeng();
}

抽象方法只能存在于抽象类中,但抽象类可以没有抽象方法。

抽象类无法实例化。

一旦父类的普通方法写成了抽象方法,则子类一定要重写该抽象方法,重写语法与虚方法一样。(此特性可以防止子类功能不全)

在Visual Studio中,有一个功能可以一键补全子类重写的方法:

对子类名右键第一项——Quick Actions and Refactorings——Implement Abstract Class。

之后的接口也有类似的便捷功能。

接口(interface):

一种特殊的类,有属性,但没有字段,所有的方法都是抽象方法,所以也无法被实例化。

接口的命名:与类名命名规则一样,前面多加一个大写的“I”。

接口的创建与创建类的路径一样,但有区别:

接口从类中分了出来。

 接口的语法比较特殊,其方法必须都是public且抽象,所以接口格式:

接口中所有的方法都不能有方法体。

一个类无论有没有继承父类,都可以同时继承一到多个接口,格式:

class Son:Father,接口1,接口2
{
    public void 接口中的方法()
    {
        ...
    }
}

在类中使用接口中的方法,无需加abstract。(注意:与抽象类一样,类一旦继承某接口,就一定要重写接口中的所有方法,)

一个接口也可以继承多个接口,格式与上述例子类似。

 

接口中的属性:用于规定子类中的字段的读写权限。

举例:

则继承IWork的子类必须要有一个名为attack的int型字段,而且还要重写其普通属性。此外,因为IWork中规定了这个字段是可读(get)也可写的(set),因此子类的这个属性必须要有这两个功能:

若接口的属性只有get,那么子类的对应属性也要有get,set则可有可无。则对set同样适用。

 

要实现多态,这3种具体选择哪一种实现?

如果子类方法的共性多,需要统一初始化,则可以使用虚方法。

如果仅仅让父类定义一系列规范,则可以使用抽象类。

若要直接拓展类的功能,则可以使用接口。

 

8.封装

五种访问修饰符:public、private、internal、protected、protect internal

1.public:当前类、实例对象、子类、子类的子类……均可访问。

2.private:仅当前类可以访问。

3.protected:仅当前类、子类、子类的子类……可以访问。

4.internal:仅能在当前程序集(项目)中访问,也就是说,如果在同一项目下,internal与public一样。

5.protected internal:在同一项目下与public一样。

类仅有public和internal两种,默认为internal。

类的成员五种都有,默认为private。

 

9.Static(静态)

static关键字,可修饰类及其所有成员(被static修饰的都叫做“静态XX”,如静态字段、静态属性、静态方法、静态类)

静态成员

静态成员的语法与普通成员一样,仅在访问修饰符后面、数据类型前面加上关键字static。

class Person
{
    private static string name;
    public static string Name { get; set; }
    public static void SayHello()
    {
        Console.WriteLine("Hello,{0}", name);
    }
}

静态成员不属于任何实例化对象,它们在类实例化之前就已经存在,若要调用它们,格式:类名.静态成员名:

Person.Name="Zerg";

因此,无论实例化多少个对象,静态成员在程序中的数量与类的数量相当。

静态字段无法用this修饰。

注意:在静态方法中无法调用非静态的方法!但非静态方法可以调用静态方法。

Main函数是静态的,这也是为何我们之前写的函数要加上static关键字。

静态构造方法

用于初始化静态成员,一个类中仅能有一个静态构造方法,且该静态构造方法无任何访问修饰符及参数。例:

static Person()
{

}

静态构造方法会在实例化第一个对象、第一次调用静态成员前完成。

静态类

在class之前加上static即可。

静态类中不能有非静态成员,也不能实例化。

单例设计模式

在设计某个类时,要保证程序运行时最多只有一个实例对象存在(例如Windows的任务管理器,只能开一个,如果开多了,内容还一样,则浪费资源,内容不一样,则会让系统陷入混乱),此时就需要用到单例设计模式。以下是流程:

这样一来,在Main函数中:

我们无法直接实例化出t1,只能调用类中的方法创建,但因为类中的if语句,创建出的t3和t2是完全一样的,这就保证了有且只有一个实例对象存在。

 

10.特殊的类(实际上很少使用)

嵌套类

在类中再定义一个类:

实例化方法:

TaskManager.CPU my_CPU = new TaskManager.CPU();

如图例子中,CPU叫做嵌套类,TaskManager叫做外部类。

匿名类

创建方式:(属性数量不限)

var 匿名类名 = new { 属性1 = 值1 ,属性2 = 值2, 属性3 = 值3};

调用方法:类名.属性名:

var player = new { Name = "Zerg" ,Level=50};
Console.WriteLine(player.Name);

匿名类的值定义后就不能修改!

密封类

不能有子类的类叫做密封类

创建密封类,只需要在class前加上关键字sealed即可,这个类就无法被任何类继承。

Object类

最原始的类,所有的类都是或直接或间接继承自Object类。

Object类本身自带了一些方法,对于一个无任何成员的类,它因为继承自Object而先天带上了4个方法:

这四个中除了GetType都是虚方法,可以进行重写,例如对ToString进行重写:(原来的ToString以字符串形式返回该对象的类及这个类所处的命名空间)

 

 

11.装箱与拆箱

注意区别:开头大写的Object是类,而开头小写的Object是类型(int、double这些都是类型)。

装箱:将值类型转换为引用类型。

拆箱:将引用类型转换为值类型。

装箱和拆箱本质上是数据存储在栈空间和堆空间的变更,若频繁装箱、拆箱会降低代码运行效率。

 

12.预编译指令

又称预处理指令,在程序编译运行前存在,这些指令在程序在编译时会自动忽略。(和注释一样)

这里介绍一下其中一种预编译指令:区域指令。

#region 该段区域指令名

#endregion

用法举例:

假如我的类中字段太多太杂,可以在字段前后使用区域指令,然后左边会出现一个“减号”的按钮,按一下:

可以把这一区域的代码折叠,方便用户审查代码,理想效果:

 

本文部分内容来自擅码网(http://www.mkcode.net)Unity 3D课程,经本人学习、整理得来,若有错漏,欢迎指正!

第 1 章 游戏开发和托管代码 .... 3 1.1 什么是.NET? ...... 3 1.2 什么是托管代码 ...... 5 1.3 使用 Microsoft Visual Studio .NET 2003 IDE 编写代码 ...... 5 1.3.1 C#代码 ..... 6 1.3.2 VB .NET 代码 ..... 8 1.4 在命令行中编译.NET 代码 ...... 9 1.5 游戏开发简述 ...... 10 1.6 开发人员 ...... 10 1.7 游戏开发过程 ...... 11 1.8 工具 ...... 12 1.9 小结 ...... 16 第 II 部分 图形和游戏 1 的介绍 第 2 章 策划第一个游戏 .... 19 2.1 提出游戏构想 ...... 19 2.2 理解一个 3D 游戏的需求 ...... 21 2.3 游戏规范 ...... 24 2.4 小结 ...... 26 第 3 章 理解示例框架 .... 27 3.1 创建项目 ...... 27 3.2 枚举所有设备选项 ...... 32 3.3 小结 ...... 39 第 4 章 在屏幕上显示 .... 40 4.1 创建设备 ...... 40 4.2 开始绘图 ...... 47 4.3 加载并绘制网格 ...... 48 4.4 在场景中添加照相机 ...... 51 4.5 小结 ...... 54 第 5 章 完成代码 .... 55 5.1 理解高分辨率计时器 ...... 55 5.2 处理丢失的设备 ...... 60 5.3 添加帧速率输出 ...... 63 5.4 设计 UI 界面 ...... 65 5.5 设计按钮 ...... 72 5.6 小结 ...... 75 第 6 章 实现用户界面 .... 76 6.1 设计主菜单 ...... 76 6.2 插入到游戏引擎中 ...... 81 6.3 选择人物(Loopy) .. 84 6.4 利用新界面更新游戏引擎 ...... 91 6.5 小结 ...... 95 第 7 章 实现玩家和块 .... 96 7.1 编写 Player 对象 ...... 96 7.2 设计块 ...... 104 7.3 小结 ...... 110 第 8 章 实现级别对象 .... 111 8.1 实现级别 ...... 111 8.2 控制玩家的移动 ...... 116 8.3 处理级别的更新 ...... 119 8.4 小结 ...... 123 第 9 章 综合应用 .... 124 9.1 包含玩家 ...... 124 9.2 挂钩级别 ...... 128 9.3 实现退出界面 ...... 132 9.4 结束工作 ...... 135 9.5 小结 ...... 140 第 III 部分 基本的数学规则 第 10 章 3D 数学快速入门 .... 145 10.1 2D 与 3D .... 145 10.2 使用 3D 点 ...... 147 10.3 操作 3D 对象 ...... 148 10.3.1 平移(移动)对象 ..... 149 10.3.2 缩放 ..... 149 10.3.3 旋转 ..... 150 10.3.4 坐标系 ..... 150 10.4 数学结构 ...... 151 10.5 向量 ...... 151 10.6 矩阵 ...... 154 10.7 小结 ...... 157 第 IV 部分 间接图形、对等网、游戏 2 第 11 章 开始创建游戏 .... 161 11.1 Tankers—— 下一个游戏构想 ...... 161 11.2 创建 Tankers 项目 ...... 163 11.3 项目的图形绘制 ...... 169 11.4 为纹理构建对象池 ...... 171 11.5 小结 ...... 173 第 12 章 开发更先进的用户界面 .... 174 12.1 使用 Blockers 的基类(Base 类) .. 174 12.2 添加新的基类 ...... 179 12.3 实现主界面 ...... 182 12.4 利用用户界面绘制 3D 模型 ...... 189 12.5 小结 ...... 193 第 13 章 绘制真实的坦克 .... 194 13.1 理解网格层次结构 ...... 194 13.2 加载坦克层次结构 ...... 197 13.3 绘制网格层次 ...... 199 13.4 操作坦克 ...... 201 13.5 坦克的属性 ...... 204 13.6 创建照相机类 ...... 207 13.7 小结 ...... 210 第 14 章 天空?级别?玩家! .... 211 14.1 没有天空的世界将是黑色的世界 ...... 211 14.2 有了一个天空,但坦克不能驱动到那里 ...... 214 14.3 控制坦克 ...... 216 14.4 IMoveableObject 接口 ...... 225 14.5 基本碰撞检测 ...... 231 14.6 小结 ...... 234 第 15 章 准备,瞄准,开火! .... 235 15.1 实现 Ammunition 类 ...... 235 15.2 Bullets 集合 ...... 242 15.3 完成玩家 ...... 243 15.4 添加声音 ...... 245 15.5 小结 ...... 248 第 16 章 避免单人游戏的枯燥 .... 249 16.1 使用 DirectPlay ... 249 16.2 创建会话 ...... 255 16.3 加入会话 ...... 257 16.4 事件处理程序 ...... 258 16.5 发送及接收数据 ...... 260 16.6 小结 ...... 265 第 17 章 完成 Tankers 游戏 .... 266 17.1 插入到游戏引擎中 ...... 266 17.2 绘制游戏 ...... 272 17.3 小结 ...... 275 第 V 部分 高级绘图、客户/服务器网络和游戏 3 第 18 章 添加特殊效果 .... 279 18.1 实现基本粒子系统 ...... 279 18.2 绘制粒子系统 ...... 287 18.3 将各部分连接到一起 ...... 290 18.4 小结 ...... 292 第 19 章 构建自己的游戏 .... 293 19.1 阐明思想 ...... 293 19.2 创建自己的项目 ...... 294 19.3 设计用户界面 ...... 300 19.4 小结 ...... 305 第 20 章 可编程流水线 .... 306 20.1 定义可编程流水线 ...... 306 20.2 使用 HLSL ... 307 20.3 编写 Vertex Shader .. 309 20.4 使用着色增加逼真度 ...... 314 20.5 添加 Pixel shader .. 315 20.6 小结 ...... 317 第 21 章 控制细节的级别 .... 318 21.1 简化网格 ...... 318 21.2 使用简化的网格 ...... 322 21.3 使用渐进网格控制细节的级别 ...... 323 21.4 小结 ...... 324 第 22 章 使用绘图目标创建特效 .... 325 22.1 绘制跑道和多辆卡丁车 ...... 325 22.2 创建绘图目标和表面 ...... 331 22.3 将场景绘制到绘图目标 ...... 333 22.4 演示后视镜 ...... 334 22.5 小结 ...... 335 第 23 章 理解高级渲染语言 .... 336 23.1 理解老的 shader 模型的限制 ...... 336 23.2 添加卡丁车镜面高亮 ...... 337 23.3 逐 pixel 镜面高亮 ...... 340 23.4 小结 ...... 343 第 24 章 关于性能的注意事项 .... 344 24.1 事件模型和 Managed DirectX .... 344 24.2 生成本机程序集 ...... 345 24.3 Boxing 恶梦 ...... 346 24.4 Managed DirectX 的速度 ...... 348 24.5 理解方法的开销 ...... 349 24.6 小结 ...... 350 第 VI 部分 附 录 附录 A 开发级别创建器 .... 353
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值