# Unity学习 **Repository Path**: the_beginning_of_man/unity-learning ## Basic Information - **Project Name**: Unity学习 - **Description**: 主要用来学习Unity游戏开发,带点C# - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2022-10-10 - **Last Updated**: 2023-11-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Unity学习 ## 介绍 主要用来学习Unity游戏开发,带点C# ## C#学习 ### 10月10日笔记 ### C#类 #### using + namespace (命名空间) 访问修饰符:指定了及其成员的访问规则; * 类的默认访问修饰符是:internal; * 成员的默认访问修饰符是:private; 1. public:公有的,能在任意程序集类中和类外访问 2. protected:受保护的,能在任意程序集的类中和子类中访问 3. private:私有的,只能在类中访问 4. internal:只能在当前程序集中的类中和类外访问 5. protected internal: == protected or internal :既可以在任意程序集中的类中和子类中访问,也可以在当前程序集中的类中和类外访问 #### C#中类的继承为: 单继承类 程序集的数据可以分为:类型元数据,程序元数据,IL代码,资源; 元数据:一般指的描述自身的数据 类型元数据:记录了程序集引用了哪些类,用户自定义了哪些类,字段,属性,数据类型等一系列信息 程序元数据:包含了程序集的版本信息,安全信息,签名等 IL代码:MSIL,C#->IL代码-》保存到程序集中,在被CLR加载-》由JIT编译器-》调用BCL->转换成机器代码运行在CPU上 资源:图片,音频,视频等 ##### 垃圾回收机制(GC)代龄 ## 类的三大特性:封装,继承,多态 * 类:是一个蓝图,将常用的、公有的属性与方法剥离出来 * 封装:将数据与方法分开,防止对实现细节的访问。 ### 封装 #### 值类型与引用类型 1. 在C#中值类型变量是直接存储数据(int,byte,short,long,float,double,char,bool,struct),值类型变量声明后,不管是否赋值,编译器都会为其分配内存 2. 引用类型的变量持有数据的引用,数据实际是存储在堆区(class,string) 当声明一个类时,只在栈中分配一小块内存用于存储地址,而此时并没有为其分配堆区的内存,当使用new创建对象时,才分配内存空间,并且将堆上的内存地址保存到在栈区中分配的小块地址中 ##### 总结 1. 值类型的数据存储在栈区,引用类型的数据是存储在堆区 2. 值类型存取速度快,引用类型存取速度慢 3. 值类型表示的是实际的数据,引用类型表示指向存储在堆区的地址 4. 值类型继承自System.ValueType,引用类型继承自System.Object 5. 值类型的内存是自动释放,引用类型的内存是通过GC释放 * 字段(变量)与属性 * 属性是类,结构体以及接口的命名成员,类或结构中成员变量或方法称为域,属性是域的扩展。定义属性后,派生类中直接调用属性修改字段的值 例子: ``` class A { private int a; //a的属性定义如下: public int A { get {return a;} set {a=value;} } } ``` * 继承,单继承类,多继承接口 #### 静态成员 1. 存储在静态成员中的信息只有在应用程序退出时才会从内存中释放 2. 静态成员不能通过对象访问,只能通过类名进行访问,访问方式:类名.静态成员名 3. 静态成员在内存中只存储一份 ### 10月11日笔记 ### 多态:同一行为的具有多个不同表现形式的能力 多态:同一行为的具有多个不同表现形式的能力 * 静态多态:函数响应是发生在编译时,函数重载,运算符重载 * 动态多态:函数响应是发生在运行时 #### 静态多态 静态多态的实现: * 函数重载:在同一个范围内对相同的函数名有多个定义,参数列表找那个的参数类型不一致,参数个数不一致,不能重载只有返回值类型不同的函数声明 * 运算符重载:可以重新定义C#中内置的运算符,`operator` ##### 可以被重载的运算符 1. 单目运算符:+ - !~ ++ -- 2. 双目运算符;+ - * / % 3. 比较运算符: > < >= <= == != ##### 不能被重载的运算符 1. 条件运算符:&& || 2. 赋值运算符;+=,*=,/=,%=,-=,= 3. new sizeof typeof ?: . -> #### 动态多态 动态多态的实现: * 抽象 abstract 抽象类+虚函数 * 抽象类也是不能去创建对象的 #### new和override * override:重写,用于重写基类中的虚方法,在派生类中提供新的方法 * overload:重载,提供的一种机制 * new:覆盖:在子类中用new关键字修饰定义与父类中的同名方法时,就叫覆盖,覆盖是不会改变父类方法的功能 * 1.在堆区分配内存, * 2.调用构造函数进行初始化 1. 不管是重写还是覆盖都不会影响父类的功能 2. 当用子类创建父类的时候,譬如 `Father fa = new Son()`;重写会改变父类的功能,即调用了子类的方法,而覆盖不会,仍然调用父类的方法 3. 虚方法,实方法都可以被覆盖,抽象方法不可以被覆盖 4. 抽象方法,虚方法都可以被重写,实方法不可以被重写 5. 覆盖主要用于对以前无法修改的类进行继承的时候 #### foreach循环 总结: 1. foreach语法简洁;for循环需要给初始值,结束值,步长,而foreach不需要,他是自动遍历集合体中的所有值 2. foreach效率比for高,因为for循环对数组访问的时候,对索引进行有效值检查,而foreach不需要 3. foreach不需要关心数组索引的起始值 4. 遍历多维数组只需要一行代码 5. foreach是只读类型,只能进行对数组的遍历,不能对数组中的每一项进行修改 6. foreach循环遍历的时候会造成额外的gc开销 7. 数组中的每一项必须要与其他项相同才能进行遍历,foreach即便数组的每一项不同,都能进行遍历 ``` string[] str = new string[] { "a", "b", "c" }; foreach (string item in str) { Console.WriteLine("str:" + item); } ``` #### out与ref 相同点:都是址传递,执行方法后,原来的值发生改变 不同点: 1. 使用ref时,传入的参数必须要初始化,而使用out时是不需要初始化的 2. ref可以把参数的值传递进函数中,而out是不能把参数的值传递进函数中,即便参数已经赋值也会被清空,退出函数时,所有被out修饰的参数都必须赋值 #### base 1. 用于在子类中实现对父类中公有或者受保护的成员进行访问,但是只局限于构造函数,实例方法中 ### 10月12日笔记 #### 静态类 static class 1. 静态类中的所有成员必须是静态的(字段,属性,方法),常量 2. 静态构造函数是不允许添加访问修饰符 3. 静态类是不能被继承的 4. 静态构造函数可以存在与普通类或静态类中 5. 静态方法也可以存在普通类中 6. 静态类是不能创建实例的 7. 静态构造函数会在首次访问类中的静态成员时被调用 ``` static class StaticTest { public static int value; public static int Test { get; set; } public const int test = 1; static StaticTest() { Console.WriteLine("静态类中的构造函数被调用"); } public static void Func() { Console.WriteLine("静态类中静态方法被调用"); } } ``` #### 抽象类 abstract class 抽象类以及抽象方法需要采用abstract进行显示声明 1. 抽象类是不能被直接实例化,可以在派生类中实现实例化 2. 抽象类中抽象方法只声明不实现,但是在子类中必须全部实现 3. 抽象类中可以包含非抽象方法,抽象方法只能写在抽象类中 4. 抽象方法是不能使用private访问修饰符 5. 抽象类中包含抽象方法和普通方法,但是抽象方法只能放在抽象类中 ``` abstract class Food { private int value; public abstract void Eat(); public void Test() { Console.WriteLine("抽象类中的普通函数被调用"); } } ``` #### 密封类 sealed class 1. 密封类不能作为基类被继承 2. 在密封类中不声明受保护的成员或虚成员,因为受保护的成员从派生类进行访问,而虚成员在子类中进行重写 3. 由于密封类的不可继承性,因此密封类不能声明为抽象的,即`sealed`修饰符不能与`abstract`进行同时使用 4. 密封类除了不能被继承外,与非密封类的用法基本一致 5. 密封方法只能用于对基类的虚方法进行修饰,并且提供具体的实现,`sealed`修饰符与`override`修饰符同时使用 #### 接口 单继承类,多继承接口 接口:定义了所有类继承接口时需要遵循语法规则,简而言之就是在接口中声明要做什么,需要在子类去实现具体怎么做的 interface 默认权限是public 1. 接口里面包含属性,方法,事件成员,不能定义字段 2. 接口只能包含成员的声明,不能有定义,因为成员的定义是派生类的责任 3. 继承了接口就必须要实现接口中所有的成员 4. 接口中的成员是不需要修饰符修饰,而子类中实现的成员需要用public修饰 5. 继承接口后,要实现的方法名必须与接口定义的方法名保持一致 6. 如果一个接口继承了其他接口,那么子类就需要实现所有接口的成员 7. 接口具有专一性 * 接口与抽象类的区别? * 相同点: 1. 都可以被继承 2. 都不能直接被实例化 3. 自身不提供代码的实现 4. 派生类做必须实现未实现的方法 * 不同点: 1. 抽象类中可以定义字段,属性,方法实现(非抽象方法),接口只能定义属性,事件,方法声明,不能包含字段 2. 接口可以多继承,抽象类只能单继承 3. 抽象类中实现的方法默认是虚的,接口的类中实现的接口方法默认是非虚的,但是可以显示声明为虚的 * 接口污染:如果一个类只是要实现这个接口中的某一个功能时,而不得不去实现接口中其他的方法 * 接口的重复继承:同一个对象的类继承的不同接口的同名方法,将对象赋值给不同接口的变量来访问不同接口的同名方法; ### 10月13日笔记 * 单例模式:构造函数私有 ``` class Singleton { private Singleton() { } private static Singleton instance; public static Singleton Instance { get { if (null == instance) { instance = new Singleton(); } return instance; } } } ``` ### 字符串 string 引用类型 System * string:关键字,声明一个字符串变量 * String:类名 * 字符串变量:StringBuilder using System.Text 总结: 1. 少量的字符串操作,字符串不经常发生改变时,优先使用string,分配在栈区,创建后大小不可修改 2. stringbuilder创建后是在堆区,大小是可以自由修改 ### 委托 委托是存有对某个方法的引用,是引用类型,相当于C++中的函数指针 * 委托常用于事件与回调方法 * delegate 委托返回值类型 委托名(参数列表) 创建委托: 声明委托类型: `delegate ` 访问修饰符关键字 委托返回值类型 委托名 (参数表); 访问修饰符关键字 委托返回值类型 委托类型名 (参数表); 例子:`public delegate int MyDelegate (string s);` 实例化委托: 委托类型名 委托变量名=new 委托名(参数列表); 例子: ``` public delegate void DelegateName(int a,int b);//声明了一个DelegateName的委托类型 DelegateName delegateVar =new DelegateName(obj.Func);//使用DelegateName类型定义了一个对象delegateVar,用new分配空间并初始化,委托类型DelegateName后面()中传入的方法obj.Func不带参数且无(); DelegateName delegateVar0 =new DelegateName(obj.Func);//同上 delegateVar+=delegate0;//多播委托,将委托变量delegate0中的方法压入委托变量delegateVar中 ``` 委托的调用(委托中函数的调用): 委托对象名(参数列表); 例子: ``` delegateVar(3,4); ``` #### 系统自带委托类型 使用系统自带的委托类型,不用显示声明委托类型,直接创建委托变量。 系统自带的委托类型:Func、Action、Predicate * Func:前面是参数类型表,后面是返回值类型,=后面接的函数必须有返回值; * Action:只有参数类型表,没有返回值类型,无返回值,=后面接的函数返回值类型必须为void; * Predicate:返回值类型是bool; Func<参数类型,参数类型...,返回值类型> 委托变量名= Action<参数类型,参数类型> 委托变量名= 系统自带委托类型创建委托: ``` Func DelegateName = new Func(obj.func); //简写 Func DelegateName = obj.func; ``` ### 字符串输出到文件中 ``` public void WriteToFile(string str) { FileStream fs = new FileStream("d:\\message.txt", FileMode.Append, FileAccess.Write);//初始化文件属性:文件路径、文件写入方式(尾部添加,首部添加,插入添加)、文件是进行读或进行写 StreamWriter sw = new StreamWriter(fs);//定义数据流写的实例 sw.WriteLine(str);//进行写入 sw.Flush();//将内存中的数据写入文件,并清除内存中的数据 sw.Close();//关闭文件写操作 fs.Close();//关闭文件 } ``` ### 10月14日笔记 #### 匿名函数 匿名函数:提供一种传递代码块作为委托参数的技术 匿名函数没有函数名,只有主体方法; 匿名函数只能作为赋值语句右值,给委托赋值,不能单独存在,只能通过委托来调用; 匿名函数例子 ``` //先定义一个委托类型 delegate void NumberChanger(int n); //定义一个委托变量,用匿名函数初始化 NumberChanger nc = delegate(int x) { Console.WriteLine("Anonymous Method: {0}", x); }; ``` Lambda表达式:匿名函数的简化,省略关键字`delegate`,只写参数列表,用`=>`指向函数体 例子: ``` NumberChanger nc = (int x)=> { Console.WriteLine("Anonymous Method: {0}", x); }; ``` #### 事件 事件:用户操作 事件是基于委托的,事件是一种具有特殊签名的委托 事件是类或者对象向其他类或对象通知发生的事情的一种特殊签名委托。 * 事件在类中声明并生成 * 包含事件的类为发布器类 * 类中的方法被压入委托中,则此类为订阅器类 事件的声明: ``` //先声明一个委托类型 BoilerLogHandler public delegate void BoilerLogHandler(string status); // 基于上面的委托定义事件 BoilerEventLog public event BoilerLogHandler BoilerEventLog; ``` 事件的调用:事件名(参数列表); ``` BoilerEventLog(str); ``` ### 观察者模式 将事件的调用封装到发布器类的方法当中,当发布器类中的方法被调用,事件也会被调用 例子: ``` class Cat //包含事件的声明为发布器类 { public event Action CatCall;//使用系统自带的委托类型`Action`创建事件 CatCall public void OnCatCall() { Console.WriteLine("猫叫了一声!"); if (CatCall != null) { CatCall();//将事件的调用放到`Cat`的方法中 } } } class Mouse //类中的方法被压入委托中为订阅器类 { public void OnMouseRun() { Console.WriteLine("老鼠跑!"); } } class People { public void WakeUp() { Console.WriteLine("主人醒!"); } } cat.CatCall += new Action(mouse.OnMouseRun);//将方法压入委托中 cat.CatCall += new Action(people.WakeUp);//将方法压入委托中 cat.OnCatCall();//调用发布器类中,包含事件调用的函数 ``` ### 10月17日笔记 ### C#数据结构 #### 数组 数组:是一个存储相同类型元素且大小固定的顺序集合,内存是连续的; ##### 普通数组的定义: * `int[] array={1,2,3};` * `int[] array=new int[]{1,2,3,4};` * `int[] array=new int[4] {1,2,3,4};` * 在声明数组时必须指定数组大小,防止数据溢出与内存浪费 * 在数组中间插入数据效率低 ##### `ArrayList` * 声明:`ArrayList al = new ArrayList();` * 能容纳不同类型的数据,节点保存的数据,类型可以不一样,能遍历 * 在数据检索以及存储时会存在装箱与拆箱的操作,带来性能损耗 * 类型不安全 ##### 装箱与拆箱 arraylist之所以能存储任意类型的数据,因为将所有的数组作为object进行存储 * 装箱与拆箱从值类型转换到object类型 * 装箱:,值类型=》引用类型 * 拆箱:将object类型转到值类型 引用类型=》值类型 * 能被拆箱的数据是一定经过装箱的 * * 装箱:对值类型在堆中分配一个对象实例,并将该实例复制到新的对象中 1. 新分配堆区的内存大小(值类型的实例大小+方法表指针和索引) 2. 将值类型的实例字段拷贝到新分配的内存中 3. 返回在堆区中新分配的对象地址 * 拆箱: 1. 获取在堆中属于值类型那部分字段的地址 2. 将引用对象中的值拷贝到栈中的值类型实例中 例子: ``` int value = 3; object obj = value;//将值类型转换到引用类型 装箱 int num = (int)obj;//将引用类型转换到值类型(强转) 拆箱 ``` * 装箱与拆箱的性能损耗:装箱时堆区分配内存,拆箱时对实例字段的拷贝 ##### List * 所属命名空间:System.Collections.Generic 1. 声明时需指定数据类型,不需要声明大小 2. 避免了装箱与拆箱类型 3. 初始容量为4,当容量不满足数据存储要求时动态扩容 4. 动态扩容以自身容量的2倍扩容 5. 数组中间插入删除会影响容量大小,尾部添加不会; 声明:` List listName = new List();` #### LinkedList * 所属命名空间:`System.Collections.Generic` 1. 双向链表 2. 插入与删除快 3. 索引比较慢 声明:`LinkedList listName = new LinkedList();` ### 10月18日笔记 栈:先进后出; * 栈的定义:`Stack stack = new Stack();` * 栈的使用: 1. 入栈:`stack.Push(value);`; 2. 将栈顶数据返回并出栈:`stack.Pop();` 3. 只将栈顶数据返回:`stack.Peek();` 4. 栈的遍历: ` foreach (int item in stack) { Console.WriteLine(item); }` 5. 栈的大小:`stack.Count;` 队列:先进先出 * 队列的定义:`Queue queue = new Queue();` * 队列的使用: 1. 入队:`queue.Enqueue("a");` 2. 将队头数据返回并且删除:`queue.Dequeue();` 3. 将队头数据返回不删除:`queue.Peek();` 4. 队列的遍历:`foreach(string item in queue)` #### 哈希表 * 使用key(键)来访问集合中的vlaue(值); * 键是唯一的,但是值可以是相同的 1. 哈希表的定义:`Hashtable ht = new Hashtable();` 2. 哈希表元素的增加:`ht.Add(1, "abc");` 哈希表的定义:`Hashtable ht = new Hashtable();` 哈希表的遍历: 1. 遍历哈希表的键 ``` foreach (var item in ht.Keys) { Console.WriteLine(ht[item]); } ``` 2. 遍历哈希表的值 ``` foreach (var item in ht.Values) { Console.WriteLine(item); } ``` #### 字典 * 使用键来访问集合中的元素 * 键是唯一的,但是值可以是相同的 字典的定义:`Dictionary dic = new Dictionary();` 字典的遍历: ``` foreach (KeyValuePair item in dic) { Console.WriteLine(item); } ``` ##### 字典与哈希表的总结 * 相同点: 1. 都是以键值对形式存在,键必须是唯一的,值是不需要唯一的,都是无序的键值对 2. 存储的个数不受限制 3. 都是可以采用foreach进行遍历 * 不同点: 1. 键与值的类型不一致,哈希表是object类型,字典取决定义的类型 2. 命名空间不一致 System.Collections.Generic(哈希表) System.Collections(字典) 3. 限制类型不同 4. 效率不同 #### 安装教程 1. xxxx 2. xxxx 3. xxxx #### 使用说明 1. xxxx 2. xxxx 3. xxxx #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request #### 特技 1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) -