|
内容来自《Unity 3D/2D 手机游戏开发:从学习到产品(第4版)》正文和附录A C#语言(比较简陋)、菜鸟教程的C#教程(稍微详细一些)。
(抄书 ing。。。主要记录一些不知道和不熟悉的地方)
Unity的底层使用C++开发,Unity的使用者只能使用脚本进行游戏开发,Unity脚本主要使用C#语言。
C#是“面向对象”编程语言。 Unity 脚本基础
脚本是组件
在Unity中,最基础的游戏单位叫做Game Object(游戏体)。每个Game Object上可以加载不同的Component(组件),包括:Transform(默认),刚体,碰撞体,脚本,音频 ......(很多很多)。
- Unity 脚本中三个重要的类:
- MonoBehaviour类:所有Unity脚本的基类,提供大部分Unity功能。如果脚本不是继承自MonoBehaviour,则无法将这个脚本作为组件运行。
- Transform类:每个Game Object都包含的默认组件,功能:平移、旋转、缩放(这三个在 Inspector 面板可以设置),以及层级面板(Hierarchy)中的父/子关系【子对象随父对象移动】。
- Rigid Body(2D)类:一种组件,提供所有物理功能。
- 脚本文件名必须==脚本里的类名,否则将脚本拖到Game Object上时、运行游戏时会报错:The referenced script on this Behaviour ...... is missing!
脚本拖到Game Object上时报的错
这个类名必须和文件名一样
脚本里的常用事件函数
(这里只是简单介绍一下有哪些函数,具体的调用顺序需要进一步学习)
void Awake(); // 实例化后最先执行的函数,只执行一次
void OnEnable(); // 脚本可用后,执行一次;因为可以关闭脚本,所以可以反复执行,在Awake之后执行
void Start(); // 进入Update函数之前执行,只执行一次,在OnEnable之后执行
void Update(); // 程序主循环,每帧执行
void LateUpdate(); // 在Update之后执行
void FixedUpdate(); // 这个函数的执行频率可以自定义,通过 Unity【Project】->【Time】自定义 Fixed Timestep。
脚本序列化
听起来很陌生,其实意思就是:
- 在脚本里写的 public 变量可以显示在 Game Object 的 Inspector 里,这样就可以在 Inspector 中设置 public 变量的初始值了。
- 默认只有继承自 MonoBehaviour 的类才能序列化,普通类需要添加 System.Serializable 属性。
例子1:
脚本里的 public 变量
脚本挂到 Game Object 上之后,Inspector 中的脚本组件。为了显示方便,Unity 会自动去掉前缀 m_
例子2:
脚本里的普通类里的 public 变量
脚本挂到 Game Object 上之后,Inspector 中的脚本组件
读取资源、实例化
C# 基础
在C#中,万物皆是类,不存在不属于一个类的函数或变量。
.Net开发框架:微软为Windows操作系统提供的 API (应用程序编程接口),能实现跨平台开发。
Unity的C#和微软.Net家族中的C#差不多。Unity内的C#运行于Mono虚拟机。
Mono虚拟机:一个开源软件平台,提供了与.Net差不多的功能,所以在Unity内使用C#不但能调用Unity引擎本身的功能,还能调用.Net平台中提供的大部分功能;有一些针对Windows平台的专用C#类库,可能无法在Unity中使用。 .NET 类库简介
除了 C# 语言本身,还需要其他项才能构建应用程序:1).NET 运行时,用于最终在计算机上执行代码时托管并管理代码;2)可在应用程序中使用的称为 .NET 类库的功能集合。
.Net 类库是包含成千上万个方法的数以千计的类的集合。 这些方法由 Microsoft 创建,并可在应用程序中使用。
类只是一种用于包含方法的容器。 开发者通常将相关方法保留在一个类中。 例如,可从命令行窗口发送或接收信息的任何方法都会被收集到 .NET 类库的 System.Console 类中。
C# 数据类型实际上也是 .NET 类库的组成部分。 为简化我们的工作,C# 掩盖了我们使用的数据类型的真实标识。 但在幕后,数据类型的实现方式与 .NET 类库中每个其他的类都一样。 这也意味着一些有用的方法是内置的,并可用于变量。
可将命名空间视为类型的“姓氏”。 类包含实现类型的代码。 类被组织成不同的命名空间,以防发生命名冲突。 毕竟,拥有上千个类时,可能需要重复使用类名。 命名空间有助于确保任何两个类中不具有相同的全名。
--------------------------
在如此多的类和方法中,如何找到应用程序所需的内容呢?
首先,找到类的一个小子集,你可能会在 C# 软件开发过程中使用该子集。 根据所处理的项目,你会更熟悉 .NET 类库的某些部分,却不太熟悉其他部分。 任何人都不了解所有部分,即使是 Microsoft 工作人员也不了解。
其次,根据需要,使用偏爱的搜索引擎查找博客文章、文章或论坛,其他用户需要执行类似的操作。 第三方源将为你提供线索,甚至提供一些可以尝试的示例代码。
接下来,你可能会花时间来阅读 Microsoft 自己关于特定方法的文档,了解其作用、工作原理、其限制、在什么情况下会引发异常(错误),以及如何缓解这些问题。 文档将成为 .NET 类库的事实来源。 文档团队与 .NET 类库的软件开发者密切合作,确保其准确性。
最后,开始通过小代码项目进行试验,加深你对类和方法工作原理的理解。
在进入不熟悉的领域时,所有软件开发者都遵循类似的过程。 发现的过程虽然充满挑战,但却是令人愉快的。
-----------------------------
若要调用 .NET 类库中类的方法,请采用 ClassName.MethodName() 格式,其中 . 符号是成员访问运算符,用于访问在类中定义的方法,而 () 符号是方法调用运算符。
调用无状态方法时,无需先创建其类的新实例。
在调用有状态方法时,需要创建类的实例,并访问对象的方法。
使用 new 运算符创建类的新实例。
类的实例称为对象。 控制台程序
- 在 Visual Studio (2019)创建 Console Application(控制台程序)
【问题:这俩有啥区别?】
using System; // using:在程序中包含 System 命名空间(类库)
namespace _001 // namespace声明:用于防止重名,默认的名称和工程的名称有关。一个 namespace 里包含了一系列类。
{
class Program // class声明:类一般包含多个方法(成员函数)。方法定义了类的行为。
{
static void Main(string[] args) // 定义了 Main 方法。Main 方法是所有 C# 程序的入口点。
{
// Console(控制台;黑色窗口)是 System 命名空间中的一个类,
// WriteLine()、ReadKey()是Console类里的方法。
Console.WriteLine("Hello, world.");
Console.ReadKey(); // 如果没有的话,控制台闪一下就没了
}
}
}
* 预处理
使编译器编译或不编译代码片段。C#的预处理不支持宏(C++预处理支持宏)。
* 类型
- 值类型【值类型的实例在栈上静态分配】:内置类型、struct、enum。所有的值类型隐性派生于 System.ValueType。
- 引用类型【引用类型的对象在堆中动态分配】:class、delegate(委托)。
- “引用类型”的值是地址;“值类型”的值是数据本身。
实例化类的对象:在面向对象编程中,通常把“用类创建对象的过程”称为实例化(instantiate)。C++和C#中使用 new 关键字进行实例化。
“引用类型”在没有使用 new 分配内存时,值为 null;使用 new 为其分配内存后,值为“保存对象数据的内存地址”(指针)。 类型转换
整数:现在计算机内存都比较大,一般都可以选择 int 类型。
浮点数:C#默认浮点数为 double 类型,但在游戏中一般用 float 就可以了。float类型的赋值后面要跟一个f。 隐式、显式类型转换:短的可以隐式转换为长的;长的不能隐式转换为短的,需要显式转换。
float somevar = 0.1f; // float类型的赋值,后面要跟一个f
short x = 10;
// 隐式类型转换
// 较短字节的类型可以隐式转换为较长字节的类型,无信息丢失
int y = x; // short(2 bytes) -> int(4 bytes)
// 长的不能隐式转换为短的
//x = y; // error CS0266: Cannot implicitly convert type 'int' to 'short'. An explicit conversion exists (are you missing a cast?)
// 显式类型转换
x = (short)y;
* 枚举
一种独特的“值类型”。
声明枚举变量:
enum <enum_name>
{
enumeration list
/* 枚举列表中的每个标识符代表一个整数值,一个比它前面的符号大的整数值。
默认情况下,第一个枚举符号的值是 0。*/
};
例子:
using System;
namespace _001
{
class Program
{
// 声明enum
enum FRUIT
{
Apple = 0,
Banana, // 值为1
Cherry, // 值为2
Blueberry = 7,
Pear, // 值为8
Orange, // 值为9
}
static void Main(string[] args) // Main方法,程序入口
{
FRUIT fruit = FRUIT.Apple;
Console.WriteLine(fruit);//输出:Apple
Console.WriteLine((int)fruit);//输出:0
// 输出:{0} 参数列表索引为0的值,{1} 参数列表索引为1的值
Console.WriteLine(&#34;{0} = {1}&#34;, FRUIT.Banana, (int)FRUIT.Banana);//输出:Banana = 1
Console.WriteLine(&#34;{0} = {1}&#34;, FRUIT.Cherry, (int)FRUIT.Cherry);//输出:Cherry = 2
/*
Exception thrown: &#39;System.FormatException&#39; in mscorlib.dll
An unhandled exception of type &#39;System.FormatException&#39; occurred in mscorlib.dll
索引(从零开始)必须大于或等于零,且小于参数列表的大小。
*/
//Console.WriteLine(&#34;{0} = {2}&#34;, FRUIT.Banana, (int)FRUIT.Banana));
Console.WriteLine(&#34;{0} = {1}&#34;, FRUIT.Blueberry, (int)FRUIT.Blueberry);//输出:Blueberry = 7
Console.WriteLine(&#34;{0} = {1}&#34;, FRUIT.Pear, (int)FRUIT.Pear);//输出:Pear = 8
Console.WriteLine(&#34;{0} = {1}&#34;, FRUIT.Orange, (int)FRUIT.Orange);//输出:Orange = 9
Console.ReadKey();
}
}
}
C# 面向对象【补充】
(基本的面向对象知识记在“(C#)设计模式 笔记 - 面向对象基础”里面了)
! 静态构造函数
(来自《剑指Offer(第2版)》)
- 静态构造函数特点:在“类型第一次被使用时”,由“运行时”自动调用,而且保证只调用一次。
- 静态构造函数:先初始化类的“静态成员变量”,再执行函数体内的语句。
- 普通构造函数:先初始化类的“普通成员变量”,再执行函数体内的语句。
例子:
// Program.cs
using System;
namespace _001
{
class A
{
public A(string text) // 普通构造函数
{
Console.WriteLine(text);
}
}
class B
{
static A a1 = new A(&#34;a1&#34;); // 静态成员变量
A a2 = new A(&#34;a2&#34;); // 普通成员变量(私有)
static B() // 静态构造函数
{
a1 = new A(&#34;a3&#34;);
}
public B() // 普通构造函数
{
a2 = new A(&#34;a4&#34;);
}
}
class Program
{
static void Main(string[] args)
{
B b1 = new B();
B b2 = new B();
Console.ReadKey();
}
}
}
这个程序执行时发生了什么:
B b1 = new B();
第一次使用 class B,先调用静态构造函数 static B()。
静态构造函数:先初始化类的“静态成员变量”,static A a1 = new A(&#34;a1&#34;);,再执行函数体内的语句,a1 = new A(&#34;a3&#34;);。
执行该语句B b1 =new B();,执行普通构造函数 public B()。
普通构造函数:先初始化类的“成员变量”,A a2 = new A(&#34;a2&#34;);,再执行函数体内的语句,a2 = new A(&#34;a4&#34;);。 B b2 = new B();
执行该语句B b2 =new B();,执行普通构造函数 public B()。
普通构造函数:先初始化类的“成员变量”,A a2 = new A(&#34;a2&#34;);,再执行函数体内的语句,a2 = new A(&#34;a4&#34;);。 因此该程序的输出为:
a1
a3
a2
a4
a2
a4
属性、访问器
例子:
using System;
namespace _001
{
public class Player
{
string m_name = &#34;&#34;; // 不加访问修饰符,默认为private
public string Name // 声明属性
{
set { m_name = value; } // set访问器
get { return m_name; } // get访问器
}
int m_life = 100;
public int Life
{
get { return m_life; }
}
}
class Program
{
static void Main(string[] args)
{
Player player = new Player(); // 默认构造函数(?)
// error CS0122: &#39;Player.m_name&#39; is inaccessible due to its protection level
//player.m_name = &#34;Player1&#34;;
player.Name = &#34;Player1&#34;;
Console.WriteLine(&#34;{0}&#34;, player);//输出:_001.Player
Console.WriteLine(&#34;{0}: {1}&#34;, player.Name, player.Life);//输出:Player1: 100
Console.ReadKey();
}
}
} |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|