c# unity3d随机生成敌人 怎么让生成的小怪不重叠

Unity3D开发技巧:如何避开unity编辑器的那些坑
招聘信息:
文/瀚阳以下总结一部分来自经验之谈,一部分来自其他人的分享。总的来讲,Unity开发原型和效果、验证想法,确实是无比便利。可能一个月就把核心玩法做得差不多。强大的编辑器功能让我们也有很大的可扩展空间来协助我们开发工具。可是编辑器是把双刃剑。如果提前看清楚有什么坑在前面,或者其他人踩过什么坑。我想这会对项目风险的把控会有很大帮助。避开unity的坑1.制作抽象的prefab来做关卡编辑尽可能制作抽象的prefab来做关卡编辑,该prefab应该足够抽象简单(只有一个GameObject,然后通过Gizmo来绘制是个不错的手段),否则以后变化的时候(常见的就是改美术资源),所有关卡都lost prefab,那么对策划来说是一场灾难。可以考虑通过数据表+编辑器的方式来提供策划操作同时也不再需要担心lost prefab的问题。prefab越简单抽象越不容易丢失,prefab之间嵌套的正确方式是通过链接而不是挂在节点下面。2.尽可能避免修改Scene,方法有几种:使用xml之类的数据组织场景尽量多让scene由prefab组成,这样变动都在prefab上使用工具做场景Merge3.不要过度依赖Component特性来开发,考虑数据驱动。4.逻辑容易散落在编辑器各处,可以做一个中心管理。利用Unity的特性组织好hierarchy,不管是编辑的时候还是运行的时候,编辑的时候可以通过工具来简化组织层级的工作。让每个场景自己能跑。利用基于组件的架构,尽可能少的使用继承(用C#的话),多通过组合来完成开发。遇到需要数据访问的通用接口,我们可以通过组合的方式来完成,而不是提供一个公共基类接口来继承,只要大家都认识这个公共组件就可以取到数据了。遇到通用的事件派发,我们可以用字符串拼接的方式派发到指定的对象或者更参数组合派发事件到对象身上。框架采用星型架构+事件机制,由于Unity3D没有一个所谓的入口函数,不利于代码跟踪,这样的基础架构能带来很多便利。unity界面扩展能力很强,而且借助CLR(commom language runtime)的反射能力,C#里面开发界面非常容易。做好tag、layer规划,要考虑业务中哪类物体之间需要交互。在代码里面get某个prefab或者GameObject,可以考虑利用界面拖目标过来,这样更加直观,而且也能对抗变化,比如目标名字变了也不怕,而且还能节省代码量。代码这里针对C#,静态强类型面向对象本身就是一个坑,继承带着两个职责,一个是复用代码,一个是接口继承。虽然性能比lua高那么一丢丢,因为性能瓶颈不在业务本身,设计上的问题要严重得多。我认为像lua这种动态语言的元编程才能够贯彻单点真理,通过元编程把真理推导到系统的每一处。让代码始终保持语义,而我认为写业务代码最重要的是保持语义。保持语义的简单有效评判方法就是看这个类中的某个函数,单独看它能否看懂;多个接口能否组成完备的解决方案。静态强类型面向对象语言比较适合需求稳定的严谨的系统开发,而不是游戏开发。容易经过多次的策划需求冲刷,语义很容易扭曲,各种抽象泄露、各种hack。好吧,跑题了。Unity3D容易被破解,因为发布版本的IL是非常容易被反编译的,要做好混淆的考虑。在Unity3D中混淆要考虑对编辑器的影响。复杂类型尽量使用引用类型,值类型反射麻烦,不方便序列化以及做成编辑器。值类型要小心赋值对象是否只是临时对象。引用类型释放之后,引用它的指针会置为null,可以放心使用。foreach、linq、协程慎用,反射只在编辑器中使用。考虑封装Time,方便做暂停。考虑使用调度器来完成功能,而不是在Update自己维护状态,这样做暂停也很容易,代码更清晰,功能更内聚。增量更新要一开始就想清楚。美术Unity3D可以通过扩展编辑器让非技术人员编辑界面来工作,组织好美术资源规格、路径,并且自动生成prefab。游戏场景物件也要规划好逻辑节点,这个也应该通过编辑器扩展好。复杂功能也应该通过编辑器开发给策划微调,特别是可视化比较重要的模块,比如动作调整。制作原型美术,让开发提升开发效率。有统一的约定,比如模型总是中心对齐,角色总是脚部对齐,统一的缩放、统一的动画骨骼命名,资源有统一的路径。支持换装(avatar)要一开始就想清楚。资源加载和优化尽可能早地给出雏形(只是雏形,帮助你对需求的把握,因为这时候你还不知道热点在哪),因为一旦没有规划好异步和资源释放,那么阻塞卡顿和内存飙升那是意料之内的。因为有雏形,那么代码会间接一点,也为改变提供了空间。
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量6646点击量5944点击量4816点击量4543点击量3285点击量3068点击量2796点击量2552点击量2519
&2016 Chukong Technologies,Inc.
京公网安备89博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)当前位置: >
全面解析Unity3D自动生成的脚本工程文件
时间: 08:57 来源:Unity之家 作者:unity.jb51.net 浏览:
我们在Unity3D开发的时候,经常会看到它会产生不少固定命名工程文件,诸如:Assembly-CSharp-vs.csproj&Assembly-CSharp-firstpass-vs.csprojAssembly-CSharp-Editor-vs.csprojAssembly-CSharp-Editor-firstpass-vs.csproj看得不少人云里雾里的。那么,这些工程是如何产生的呢?各自的作用是什么?下面就来逐一解析。一. 首先从脚本语言类型来看,Unity3D支持3种脚本语言,都会被编译成CLI的DLL。如果应用中含有C#脚本,那么Unity3D会产生以Assembly-CSharp为前缀的工程,名字中包含&vs的&是产生给Visual Studio使用的,不包含&vs&的是产生给MonoDevelop用的。如果工程中这3中脚本都存在,那么Unity3D将会生成3种前缀类型的工程。二. 对于每一种脚本语言,根据脚本放置的位置(其实也部分根据了脚本的作用,比如编辑器扩展脚本,就必须放在Editor文件夹下),Unity3D会生成4种后缀的工程。其中的firstPass就表示先编译,Editor表示放在Editor文件夹下的脚本。下面以C#脚本为例。如果工程中只有C#脚本,不考虑为VS和MonoDevelop各自生成工程的差异性,我们可以得到4个工程文件:Assembly-CSharp-firstpass-vs.csprojAssembly-CSharp-Editor-firstpass-vs.csprojAssembly-CSharp-vs.csproj&Assembly-CSharp-Editor-vs.csproj(1) 所有在Standard Assets,Pro Standard Assets或者 Plugins文件夹中的脚本会产生一个Assembly-CSharp-firstpass-vs.csproj文件,并且先编译;(2) 所有在Standard Assets/Editor, Pro Standard Assets/Editor 或这Plugins/Editor文件夹中的脚本产生Assembly-CSharp-Editor-firstpass-vs.csproj工程,接着编译;(3) 所有在Assets/Editor外面的, 并且不在(1),(2)中的脚本文件(一般这些脚本就是我们自己写的非编辑器扩展的脚本)会产生Assembly-CSharp-vs.csproj工程,被编译;(4) 所以在Assets/Editor中的脚本产生一个Assembly-CSharp-Editor-vs.csproj工程,被编译。之所有这样建立工程并按此顺序编译,也是因为DLL间存在的依赖关系所决定的。好了,至此也说得比较清楚了,也不会因为见到那么多的工程文件而疑惑了。最后问诸位一个小问题:一个Unity3D的工程,最多可以产生多少个工程文件?答案是:4*3*2 = 24个
(责任编辑:脚印)
免责声明:Unity之家部分内容来源于互联网,如有侵权,请联系我们,本站将立即进行处理。
猜你也喜欢看这些 ??????
其他类型的开发经验 ??????当优美的C#与Unity3D结合时开发游戏变的超Esey(一)必须掌握的C#100个基本点。
&&&&Unity3D提供支持三种脚本语言,但是Jscript并非一我们所熟知的那个Jscript,有着很大的不同,所以建议使用优美的C#语言来写脚本,下面是必须掌握的100个C#基本点。
1.C#中使用//(双斜杠)来表明本行的剩余部分代表注释。
2.C#中语句是按顺序执行的,每条语句以分号结尾。
3.C#中的函数执行一系列语句的行为,称为语句块---一对大括号中包含0条或多条语句。
4.一个函数可以通过定义参数来从调用者处接受输入数据,也可以通过定义返回类型来输出数据给调用者。
5.Main函数定义为执行程序的默认入口点。Main函数可以不返回任何值,也可以返回一个整数给执行程序环境。Main函数也可以不定义任何参数,或者定义一个string数组作为参数(该参数将由传递给执行程序的参数填充)。
6.Using指令用于使用命名空间。
7.C#中的标识符以字母或下划线开头,并且大小写敏感。
8.关键字是由编译器保留的名字,不能用作标识符,下面是C#的关键字列表:
9.如果想将关键字作为标识符,那么可以加上前缀@以使它合法,例如:class @class。注意@符号并不构成标识符的一部分,所以@myVariable和myVariable是相同的。
10.一些关键字是上下文相关的,它们不用使用符号@就能够被用作标识符。这些关键字如下:
11.C#中所有的值都是一个特定类型的实例,一个值的含义,以及一个变量可以拥有的所有可能的值,是由它的类型决定的。
12.预定义类型(也叫内建类型)是被编译器特别支持的类型,如int、float、bool等。
13.自定义类型可以包含成员数据和成员函数。需要使用new运算符来创建一个新的自定义类型的实例,当new运算符实例化一个对象后,该对象的构造函数将被调用。构造函数的定义类似于普通函数定义,除了函数名必须和类名相同以及去除了返回类型。
14.在类型的实例上操作的数据成员和函数成员称为实例成员。而不在类型的实例上,但在类型本身上操作的数据成员和函数成员,必须被标记为static。
15.public关键字用于显露成员给其他类使用。
16.C#能够在两个相兼容的类型之间进行转换,转换总是根据一个已存在的值而创建一个新值。转换可以是隐式的或者显式的,隐式的转换自动发生,如int x = 5; long y = x。而显式的转换需要一个映射,如int x = 5; short y = (short)x。一般来说,当编译器能够保证在两个类型之间转换总是成功的,且不丢失任何信息,那么就允许隐式转换,否则的话,就必须使用显式转换。
17.C#中的类型可以分为值类型和引用类型。值类型包括了大多数内置类型(所有的数值类型(int, float等),char类型,bool类型以及struct与enum类型),引用类型包括了所有的class,array,delegate与interface类型。
18.一个值类型的变量或常量包含的内容仅仅是一个值,对一个值类型的实例进行赋值总是会拷贝实际数据到该实例。引用类型的变量或常量包含的内容是对一个拥有实际数据的对象的引用,对一个引用类型的实例进行赋值总是会拷贝引用,而不会拷贝拥有实际数据的对象实例,这就允许多个变量引用同一个对象。
19.一个引用类型的实例可以被设置为null,以表明没有引用任何对象,使用一个为null的实例的成员将产生一个运行时错误。相比之下,一个值类型的实例不能被设置为null。
20.C#中的预定义类型分类:
21.C#预定义的数字类型如下:
22.整型可以使用10进制或16进制标记法,16进制标记法需要加0x前缀,如0x7F。实数可以用10进制或指数标记法,如1e06。
23.浮点数转换为整数将丢弃小数部分,如果想要进行四舍五入的转换,则可以使用System.Convert类。
24.需要注意从1个很大的整数转换为浮点数时可能会丢失精度,如int x = ; float f = // 此时f = 。
25.算数运算符(+,-,*,/,%)可以用于所有的数值类型(除了8位和16位的整数类型)。
26.自增,自减运算符用于使数值类型的变量加1或减1。运算符可以放在变量前(++x)或者变量后(x++),取决于想在表达式计算之前还是之后更新变量的值。
27.整数除法总是会丢弃余数。如果除以一个值为0的变量将产生一个运行时错误(DivideByZeroException),除以字面值0将产生编译错误。
28.在运行时,整数类型进行算术运算可能会造成溢出,默认情况下,不会有异常被抛出。C#文档表明对于溢出结果是不可预料的,但是Common Language Runtime(CLR)总是产生一个wraparound行为,也就是说,将最小的int值减1的结果是最大的int值,如int x = int.MinV x--; // 此时x = int.MaxValue。
29.checked运算符通知运行时当溢出时抛出一个OverflowException异常,checked运算符可以用于++, --, -(一元), +, -, *, /以及整数类型之间的显示转换。checked运算符可以用于一个表达式或者一个语句块,如:
1 int a = <span style="color:#80;
2 int b = <span style="color:#80;
3 int c = checked( a * b );
// 用于表达式
// 用于语句块
可以使用/checked[&#43;/-]编译选项来检测/不检测程序中所有发生的溢出,如果使用了/checked&#43;来检测程序中所有发生的溢出,而对于某些特定的表达式或语句块又不想使用检测功能,那么可以像checked运算符一样使用unchecked运算符来关闭检测。
30.C#提供了下面这些位运算符:
31.8位和16位的整型包括了byte, sbyte, short和ushort,这些类型自身没有算数运算符,所以C#在需要时会将它们转换为更大的类型,这样就会导致如果接收运算结果的是一个较小的整型时,会发生编译错误,如short x = 1, y = 1; short z = x &#43; // 此处编译错误。在这种情况下,为了使加法能够执行,x, y会被隐式的转换为int,这意味着结果也是int,而int不能被隐式的转换为short(因为会导致数据丢失),所以,为了使编译通过,我们需要加上显式转换,short
z = (short)(x &#43; y);
32.float和double类有一些常量用于NaN(Not a Number), &#43;∞, -∞, MaxValue, MinValue和Epsilon。当发生除0时将导致结果为无穷&#20540;,如:
。当发生0除0或者无穷减无穷时将导致结果为NaN,如
。当使用==时,一个NaN&#20540;永远不会等于另一个&#20540;,即使是另一个NaN&#20540;,所以如果要测试一个&#20540;是否为NaN,必须使用float.IsNaN或者double.IsNaN函数。当使用Object.Equals函数时,也可以判断两个NaN&#20540;是否相等,如object.Equals (0.0/0.0, double.NaN) // true。
33.float类型适用于科学计算,而decimal类型适用于金融计算或者表示那些人为的&#20540;,下面是double类型与decimal类型的区别:
34.由于float和double类型的数&#20540;在内部是以2为基表示的,所以许多以10为基的小数部分字面&#20540;无法被精确的表示,如
,这就是为什么float和double类型不适用于金融计算的原因。相比之下,由于decimal类型的数&#20540;是以10为基表示的,所以这种情况不会出现。
35.C#的bool类型是一个可以被指定为true或者false的逻辑&#20540;。尽管一个bool类型仅需要1位的存储空间,但是运行时将使用1个字节的存储空间,因为这是运行时和处理器可以有效工作的最小单位。所以为了避免空间上的浪费,C#提供了一个BitArray类,该类位于System.Collections命名空间下,旨在只使用1位存储空间表示每个bool类型的&#20540;。
36.对于引用类型,是否相等,默认情况下是取决于所引用的对象是否相同,而不是对象内部的实际数据。因此,一个对象的两个拥有相同数据的实例被认为是不相等的,除非该对象所属的类型重载了==运算符以达到使他们相等的效果。
37.相等和比较运算符,==, !=, &, &, &=, &=,可以用于所有的数&#20540;类型,但是用于实数时需要谨慎小心(参见34)。这些运算符也可以用于enum(枚举)类型成员,比较的是他们代表的整型数&#20540;。
38.&&和||运算符用于测试条件与和条件或,他们是短路运算符,也就是说,当前一个表达式不满足时,后续表达式将不再计算,如if ( a && b ),如果表达式a为假,那么表达式b将不会计算,这是十分有用的,例如:if (sb != null && sb.Length & 0),可以避免抛出NullReferenceException异常。&和|运算符也可用于测试条件与和条件或,但是他们不是短路运算符,所以,一般很少用于条件测试。还有一个三目运算符,形式如q ? a : b,当q为真时,a将被计算,否则b将被计算。
39.C#的char类型代表一个Unicode字符,占用2个字节。一个char的字面表示在一个单引号中,如char c = 'A';转义字符用来表示那些不能显示的字符,一个转义字符是一个反斜杠紧跟一个字符,具有特定的含义,转义字符表如下:
。\u(或\x)转义字符允许你通过使用4位16进制来指定任意的Unicode字符,如char newLine = '\u000A';
40.当一个数&#20540;类型可以容纳一个unsigned short类型的&#20540;时,从char到该数&#20540;类型就可以隐式转换。否则的话,就需要显示转换。
41.string类型表示一个不可变的Unicode字符序列。一个string的字面表示在一个双引号中,如string s = &hello&;尽管string是引用类型,而不是&#20540;类型,但是它的==运算符却遵循&#20540;类型的比较语义,如string a = &test&, b = &test&; Console.Write (a == b); // True。转义字符同样也可以用在string类型中,如string a = &\\\\server\\fileshare&;C#还提供逐字字符串字面&#20540;,以@开头并且不再解析转义字符,如string
b = @&\\server\fileshare&;和上面的字符串a是等价的。
42.&#43;运算符用于连接两个字符串,如string a = &s& &#43; &w&;可能某个操作数不是string类型,那么该操作数类型的ToString函数将被调用,如string a = &s& &#43; 5;等价于string a = &s& &#43; 5.ToString();因为string类型是不可变的,所以多次的使用&#43;运算符构建一个新的string是十分低效的。取而代之的,可以使用System.Text.StringBuilder类型,这代表了一个可变的字符串,以及拥有可以高效的添加,删除,插入,替换子串的方法。
43.string类型不支持&和&运算符来进行比较操作,必须使用string类型的CompareTo函数。
44.字符串的索引返回特定位置上的字符,如Console.Write (&word&[2]); // r。
45.切记:string类型代表的字符串是不可变的,所有操作字符串的函数将返回一个新的字符串,原来的字符串不会被改变。
46.一个数组(Array)代表一个特定类型的固定数量的元素。一旦数组被创建,它的长度就不能被改变了。数组元素总是被存储在连续的内存块中,以供高效的存取。一个数组以元素类型紧跟方括号来表示,如char[] vowels = new char[5]; 方括号也用于数组索引,存取一个特定位置的元素,如vowels[0] = 'a'; 在运行时所有的数组索引都会进行范围检测,如果使用了一个无效的索引,那么将抛出一个IndexOutOfRangeException异常。
47.数组的初始化表达式可以方便的同时声明和赋&#20540;一个数组,如char[] vowels = new char[] {'a','e','i','o','u'}; 或者更简单的方式char[] vowels = {'a','e','i','o','u'}; 可以使用for循环来遍历一个数组中的所有元素,同时,由于数组总是实现了IEnumerable&T&,所以也能够使用foreach来枚举数组成员:
1 char[] vowels = {'a','e','i','o','u'};
3 for (int i = 0; i & vowels.L i&#43;&#43;)
Console.Write (vowels[i]);
8 foreach (char c in vowels)
Console.Write (c);
48.创建一个数组总是会使用默认&#20540;来预初始化每个元素,一个类型的默认&#20540;就是在内存中每一位都是0,对于&#20540;类型来说,就是0,对于引用类型来说,就是null。无论元素类型是什么,数组自身总是引用类型的。
49.多维数组有两种形式:矩形数组(Rectangular arrays)和不规则数组(Jagged arrays)。矩形数组声明时使用逗号来分割每一维,如int[,] matrix = new int [3, 3]; 一个矩形数组可以像下面这样初始化:
int[,] matrix =
new int[,]
int[,] matrix =
不规则数组使用连续的方括号来代表每一维,如声明一个2维数组,最外维的大小为3:int[][] matrix = new int[3][]; 可以发现,声明时并没有指定内维的大小,与矩形数组不同的是,每个内维数组可以使任意大小的。因为内维数组被隐式初始化为null而不是一个空数组,所以内维数组必须手动创建:
int[][] matrix =
new int[3][];
(int i = 0; i & matrix.L i&#43;&#43;)
matrix[i] =
j = 0; j & matrix[i].L j&#43;&#43;)
matrix[i][j] = i * 3 &#43;
不规则数组可以像下面这样初始化:
int[][] matrix =
new int[][]
int[] {0,1,2},
int[] {3,4,5},
int[] {6,7,8}
int[][] matrix =
int[] {0,1,2},
int[] {3,4,5},
int[] {6,7,8}
50.还有另一个简洁的数组初始化表达式,它省略了new关键字之后的类型名,让编译器去推断数组类型。当传递数组类型的参数时,这是一个十分有用的缩写:
Foo (char[] data) { ... }
Foo ( new[]{'a','e','i','o','u'}
51.栈和堆是存放变量与常量的地方,它们有截然不同的生命期语义。
52.栈是用来存放局部变量和参数的内存块,当一个函数被调用和退出时,栈就会逻辑增长和减小,考虑下面的函数:
1 static int Factorial( int n )
if ( 0 == n )
return ( n * Factorial(n-1) );
这是一个递归函数,也就说它会自己调用自己。每次函数被调用时,就会有一个新的int参数被分配在栈上,而当函数退出时,int参数被释放。
53.堆是用来存放对象(也就是引用类型的实例)的地方。无论何时当一个对象被创建时,它被分配在堆上,并且一个对该对象的引用将被返回。运行时有一个垃圾收集器,会定期的释放在堆上的对象,所以你的计算机不会耗尽内存。只要一个对象没有对它自身的任何引用,那么该对象就可以被垃圾收集器释放。堆也可以存放静态字段和常量,与在堆上分配的对象不同的是,他们不会被垃圾收集器释放,而是直到应用程序结束才会销毁。
54.注意:不能像C&#43;&#43;那样显示的释放对象,一个未被引用的对象最终会被垃圾收集器释放。
55.C#执行明确的分配政策。在实践中,这意味着除了一个不安全的上下文以外,不可能访问到未初始化的内存。明确的分配政策有下面3个含义:
1.局部变量在读取前必须指定一个&#20540;。
2.当一个函数被调用时,必须提供函数需要的参数。(除非为默认参数)
3.所有的其他变量(如数组元素)在运行时会自动初始化。
56.所有类型的实例都有一个默认&#20540;,预定义类型的默认&#20540;就是内存中每一位都是0,引用类型的默认&#20540;是null,数&#20540;类型和枚举类型的默认&#20540;是0,char类型的默认&#20540;是'\0',bool类型的默认&#20540;是false。可以使用default关键字来获取任意类型的默认&#20540;。
57.可以使用ref和out修饰符来控制如何传递参数,如下:
58.默认情况下,C#中参数的传递方式是&#20540;传递。也就是说将&#20540;类型的实例传递给函数时,将创建一份&#20540;的副本。将引用类型的实例传递给函数时,将创建一份引用的副本,而不是所引用的对象的副本。举例如下:
1 static void Foo( int n )
&#43;&#43;n;
Console.WriteLine( n );
7 static void Main(string[] args)
int x = 10;
// 创建一份x的副本
Console.WriteLine( x ); // x仍旧是10,因为Foo函数改变的是副本
13 ////////////////////////////////////////////////////////////////////////////
15 static void Foo( StringBuilder strBuilder )
// strBuilder与sb引用同一个对象
strBuilder.Append( &Reference Copy& );
// strBuilder是引用的副本,所以改变它不会影响实参
strBuilder = null;
24 static void Main(string[] args)
StringBuilder sb = new StringBuilder();
Foo( sb );
Console.WriteLine( sb.ToString() ); // Reference Copy
59.C#提供了ref修饰符来进行引用传递,例如:
1 static void Foo( ref int n )
&#43;&#43;n;
Console.WriteLine( n );
7 static void Main(string[] args)
int x = 10;
Foo( ref x );
// 引用传递,也就是说形参n与实参x现在引用同一块内存
Console.WriteLine( x ); // x现在是11
60.out修饰符与ref修饰符相&#20284;,除了:
1.传递到函数之前无需指定&#20540;。
2.函数返回之前必须被指定&#20540;。
out修饰符通常用于从函数获取多返回&#20540;的情形。
61.params修饰符可以用于一个函数的最后一个参数上,以表明该函数接受任意数量的特定类型的参数,但必须声明为数组形式,如:
1 static int Sum (params int[] ints)
int sum = 0;
for (int i = 0; i & ints.L i&#43;&#43;) sum &#43;= ints[i];
8 // 可以这样调用Sum函数
9 Console.WriteLine ( Sum (1, 2, 3, 4) );
62.从C#4.0开始,提供了默认参数,当一个参数在声明时被指定了一个默认&#20540;时,它就是默认参数,如void Foo (int x = 23){ ... }。当调用函数时,默认参数可以被省略,如Foo();此时声明时指定的默认&#20540;将被传递给函数。
63.除了使用位置指定参数,还可以使用名称指定参数,如void Foo (int x, int y){ ... }; Foo(x:1, y:2); 以名称指定参数可以使用任意顺序,如Foo(y:2, x:1)和Foo(x:1, y:2)是等价的。也可以混合使用位置和名称指定参数,但是以名称指定参数必须出现在最后,如Foo(1, y:2); 以名称指定参数与默认参数一起使用时显得特别有用,如void Bar (int a=0, int b=0, int
c=0, int d=0) { ... } 就可以使用以下方式只提供一个&#20540;给d:Bar( d:3 );
64.通常有在声明变量的同时初始化的情况,此时,如果编译器能够从初始化表达式中推断出类型的话,那么就可以使用var来代替类型声明,如var x = 5;等价于int x = 5; 这称为隐式类型变量,它是静态类型,如下面的语句将产生编译错误:var x = 5; x = &string&; // 编译错误,x是int类型
65.一个赋&#20540;表达式使用=运算符将一个表达式的结果赋&#20540;给一个变量,如int x = x * 5; 赋&#20540;表达式能够和其他表达式组合,如y = 5 * (x = 2); 初始化多个变量时,这种方式特别有用:a = b = c = d = 0; 复合赋&#20540;运算符是组合赋&#20540;运算符与另一个运算符的语法简写,如x *= 2;等价于x = x * 2;
66.当一个表达式包含多个运算符时,优先级和结合律决定了计算顺序。高优先级的运算符将在低优先级的运算符之前计算。当运算符的优先级相同时,运算符的结合律决定了运算顺序。如1&#43;2*3的计算顺序为1&#43;(2*3),因为*运算符的优先级高于&#43;运算符。结合律分为左结合与右结合,左结合就是计算时从左至右,如8/4/2的计算顺序为(8/4)/2,因为/运算符是左结合。右结合就是计算时从右至左,如x=y=3,先将3赋&#20540;给y,然后将表达式y=3的结果(即3)赋&#20540;给x。
67.下表以优先级从高至低的顺序列出了C#中的运算符,相同副标题下的运算符具有相同的优先级:
68.一条声明语句声明了一个新的变量,在声明变量时可以选择是否使用表达式初始化该变量,声明语句以分号结尾。可以在一条声明语句中声明多个同类型的变量,当中以逗号分隔,如int a = 5, b = 6; 一个常量的声明与变量的声明类&#20284;,除了常量在声明后不能被改变,以及必须在声明时进行初始化。
69.一个局部变量的生命期为当前语句块,不能在当前语句块或任何嵌套的语句块中声明另一个同名局部变量。
70.表达式语句意味着表达式&做了&某些事情,如下:
赋&#20540;或修改一个变量
实例化一个对象
调用一个函数
一个没有做上面任何事情的表达式是非法的语句,如string s = &foo&; s.L // 非法的语句。 当调用一个构造函数或者一个有返回&#20540;的函数的时候,并不是必须使用该结果的,如new StringBuilder();是合法的语句。
71.在switch语句中只能使用能够被静态求&#20540;的表达式,类型被限制在内建的整型,string类型和枚举类型。在每一个case从句的最后,必须使用一些跳转语句类型明确说明接下去该如何执行,可选的跳转语句如下:
break - 跳转到switch语句的结尾
goto case x - 跳转到另一个case从句
goto default - 跳转到default从句
任何其他的跳转语句 - 也就是return,throw,continue或goto标签
当多个&#20540;需要执行相同的代码时,可以依次列出:
1 switch (cardNumber)
3 case 13:
4 case 12:
5 case 11:
Console.WriteLine (&Face card&);
8 default:
Console.WriteLine (&Plain card&);
72.foreach语句迭代一个可枚举对象中的每一个元素,C#中大多数表示元素集合或元素列表的类型都是可枚举的,比如数组和字符串,如:
1 foreach ( char c in &foreach& )
Console.Write( c );
73.跳转语句包括break, continue, goto, return以及throw。break语句结束一个迭代语句或switch语句的执行。continue语句放弃循环中剩余的语句,立即开始下一次迭代。goto语句跳转到一个标签(使用冒号后缀表示)执行。return语句结束函数的执行,如果函数具有返回类型,那么必须返回一个该类型的表达式。
74.命名空间是一个域,在该域中的类型名称必须是唯一的。命名空间不受成员访问权限(private, internal, public等等)的影响。没有定义在任何命名空间的类型就说处于全局命名空间。
75.可以使用完整合&#26684;名称引用一个类型,也就是包括该类型定义所在的所有的命名空间。如:System.Security.Cryptography.RSA rsa = System.Security.Cryptography.RSA.Create(); 使用using指令导入命名空间后,就可以不需要使用完整合&#26684;名称来引用一个类型,如:using
System.Security.C RSA rsa = RSA.Create();
76.在外层命名空间声明的名称可以在内层命名空间中直接使用,而不需要使用完整合&#26684;名称,如
1 namespace Outer
namespace Middle
class Class1 {}
namespace Inner
class Class2 : Class1 {}
如果想要使用同一命名空间层级下不同分支里的一个类型,只需要使用完整合&#26684;名称的一部分即可:
1 namespace MyTradingCompany
namespace Common
class ReportBase {}
namespace ManagementReporting
class SalesReport : Common.ReportBase {}
77.如果相同的类型名称同时出现在内层和外层的命名空间中,那么内层的命名空间中的类型将替代外层的命名空间中的类型。如果想要使用外层的命名空间中的类型,需要使用包括命名空间的合&#26684;名称。
78.导入命名空间可能会导致类型名称冲突。相比导入整个命名空间,可以只导入需要的类型,并给类型一个别名,如
1 using PropertyInfo2 = System.Reflection.PropertyI
2 class Program { PropertyInfo2 }
命名空间也可以给定别名,如
1 using R = System.R
2 class Program { R.PropertyI }
79.一个字段是一个类或结构体中的成员变量。一个字段可以使用readonly修饰符来防止它在构造后被修改,一个只读字段只能在声明时或构造函数中赋&#20540;。字段的初始化是可选的,未初始化的字段有默认&#20540;(0, \0, null, false),字段的初始化在构造函数之前,以他们声明出现的顺序执行。
80.一个类型可以重载方法(有多个相同名称的方法),只要参数类型不同即可,如
1 void Foo (int x);
2 void Foo (double x);
3 void Foo (int x, float y);
4 void Foo (float x, int y);
81.一个类或结构体可以重载构造函数,通过使用this关键字,一个重载的构造函数可以调用另外一个重载的构造函数,如
1 public class Wine
public Wine (decimal price) {...}
public Wine (decimal price, int year)
: this (price) {...}
当一个构造函数调用另外一个构造函数时,被调用的构造函数先执行。
82.对于类来说,当且仅当没有定义任何构造函数时,编译器会自动生成一个无参的构造函数。对于结构体来说,无参的构造函数是结构体的内部结构,因此不能自己定义。结构体的无参的构造函数负责使用默认&#20540;初始化结构体中的每一个字段。
83.为了简化对象的初始化,对象的可访问的字段或属性可以在构造后的单条语句中初始化,如
1 public class Bunny
public string N
public bool LikesCarrots, LikesH
public Bunny () {}
public Bunny (string n) { Name = }
9 // 可以像下面这样初始化
10 Bunny b1 = new Bunny {
Name=&Bo&,
LikesCarrots = true,
LikesHumans = false
16 Bunny b2 = new Bunny (&Bo&) {
LikesCarrots = true,
LikesHumans = false
84.属性从外部看像字段,但在内部他们包含逻辑,像函数一样。一个属性像字段那样声明,但是附加了get/set块,如
1 public class Stock
decimal currentP
public decimal CurrentPrice
get { return currentP }
set { currentPrice = }
get和set表示属性的访问器,当属性被读取的时候,get访问器就会运行,它必须返回与属性类型相同的一个&#20540;。当属性被赋&#20540;的时候,set访问器就会被运行,它包含一个名称为value的与属性类型相同的隐式参数。一个属性如果只定义了get访问器,那么它就是只读的。如果只定义了set访问器,那么它就是只写的。
85.大多数属性的实现仅仅只是读与写一个与属性同类型的私有字段,一个自动属性声明可以告诉编译器提供这种实现,自动属性声明如下:
1 public class Stock
public decimal CurrentPrice { get; set; }
此时编译器就会自动生成一个私有字段供属性读和写,该字段的名称是编译器生成的,所以不能引用。
86.索引器提供了一种自然的语法来访问一个类或结构体中的元素。写一个索引器需要定义一个名称为this的属性,然后在方括号中指定参数:
1 class Sentence
string[] words = &The quick brown fox&.Split();
public string this [int wordNum]
get { return words [wordNum];
set { words [wordNum] = }
一个索引器可以使用多个参数。每个类型可以定义多个索引器,只要它们的参数类型不同即可。如果省略set访问器,那么索引器就是只读的。
87.一个常量是一个无法改变其&#20540;的字段。一个常量在编译时期被静态求&#20540;,无论何时使用它,编译器将用字面&#20540;替换它。一个常量使用关键字const进行声明,声明时必须被初始化:
1 public class Test
public const string Message = &Hello World&;
88.静态构造函数在每个类型上执行一次,而不像普通构造函数那样在每个实例上执行一次。一个类型只能定义一个静态构造函数,并且必须无参。当一个类型被使用之前,运行时会自动调用静态构造函数,有2件事会触发这种行为:实例化一个类型的对象以及访问类型中的静态成员。有个需要注意的地方:如果静态构造函数引发一个未处理的异常,那么在应用程序的生命期内,该类型都无法使用。
89.一个类可以被标记为静态的,表明它必须完全由静态成员组成。
90.终结器(Finalizers)是只有类拥有的函数,该函数在垃圾收集器回收一个未被引用的对象的内存时被调用。终结器的语法是类名加上前缀~:
1 class Class1
~Class1() { ... }
91.一个类只能有一个基类,但它自身可以作为许多类的基类。
92.引用是多态的,也就是说一个类型的变量可以引用它的子类对象。多态的实现基于这样一个事实:子类拥有其基类的所有功能。
93.一个对象的引用能够:
隐式的向上转型到基类的引用
显式的向下转型到子类的引用
在兼容的引用类型之间向上转型或向下转型将执行引用转换,引用转换将创建一个指向同对象的引用。向上转型总是成功的,而向下转型是否成功取决于对象的类型是否合适。
94.一个向上转型操作从子类引用创建一个基类引用。一个向下转型操作从基类引用创建一个子类引用,一个向下转型需要显式指定因为可能会失败,如果失败,将抛出InvalidCastException异常:
House h = new House();
// 向上转型总是成功
Stock s = (Stock)a;
// 向下转型失败: a不是Stock类型
95.as运算符执行向下转型,如果转型失败,会返回null,而不是抛出异常。as运算符不能用于数&#20540;转换。
96.is运算符用于测试是否一个引用转换将会成功,换句话说,也就是是否一个对象派生自一个特定类(或实现了一个接口)。
97.函数,属性,索引器以及事件都可以被声明为virtual,被标记为virtual就能够被想要提供特殊实现的子类覆盖(overridden),子类通过使用override修饰符来实现覆盖:
1 public class Asset
public string N
public virtual decimal Liability { get { return 0; } }
7 public class House : Asset
public decimal M
public override decimal Liability
{ get { return M } } // 覆盖基类的属性,提供特殊实现。使用override修饰符
虚方法和覆盖方法的签名,返回类型以及访问权限必须一致。如果一个覆盖方法想要调用基类的实现,那么可以使用base关键字。
98.一个被声明为抽象(abstract)的类不能被实例化。抽象类可以定义抽象成员,抽象成员类&#20284;于虚成员,但他们不需要提供一个默认实现。子类必须提供抽象成员的实现,除非子类也被声明为抽象类。
99.一个基类和一个子类可以定义相同的成员,例如:
1 public class A
{ public int Counter = 1; }
2 public class B : A
{ public int Counter = 2; }
此时可以说B类中的Counter字段隐藏了A类中的Counter字段。编译器会生成警告并且按如下的方法解决二义性:A类的引用对象绑定A.Counter,B类的引用对象绑定B.Counter。但是有时候,可能是故意想要隐藏成员,在这种情况下,可以使用new修饰符:
public class A
int Counter = 1; }
public class B : A { public new int Counter = 2; }
new修饰符其实只是使得编译器不再会生成警告,并且可以告诉其他程序员,这是故意的。
100.一个覆盖的方法可以使用sealed关键字来密封它的实现,从而防止被其子类覆盖。也可以密封类,从而隐式的密封所有的虚函数。
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?}

我要回帖

更多关于 unity3d 生成随机地图 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信