标签:
答:
装箱操作:
值类型隐式转换为object类型或由此值类型实现的任何接口类型的过程。
1.在堆中开辟内存空间。
2.将值类型的数据复制到堆中。
3.返回堆中新分配对象的地址。
拆箱操作:
object类型显示转换为值类型或从接口类型到实现该接口值类型的过程。
1.判断给定类型是否是装箱时的类型。
2.返回已装箱实例中属于原值类型字段的地址。
答:attribute翻译成特性,用来标识类,方法 。
property翻译为属性,性质用于存取类的字段 。
markup翻译成标记。
tag翻译成标签。
答:程序集的一个重要特性是它们包含的元数据描述了对应代码中定义的类型和方法。
答:ref 关键字使参数按引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。若要使用 ref 参数,则方法定义和调用方法都必须显式使用 ref 关键字。
out 关键字会导致参数通过引用来传递。这与 ref 关键字类似,不同之处在于 ref 要求变量必须在传递之前进行初始化。若要使用 out 参数,方法定义和调用方法都必须显式使用 out 关键字。
答:1) new 运算符 :用于创建对象和调用构造函数。
2) new 修饰符 :用于向基类成员隐藏继承成员。
3) new 约束 :用于在泛型声明中约束可能用作类型参数的参数的类型。
答:string str = "" 初始化对象分配空间。
string str = null 表示一个空引用,没有占用空间。//实际情况是,string为null时依然为"",但该题常考。
答:序列化是将对象状态转换为可保持或传输的格式的过程。 与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
答:
1) sealed 修饰符可以应用于类、实例方法和属性。密封类不能被继承。密封方法会重写基类中的方法,但其本身不能在任何派生类中进一步重写。当应用于方法或属性时,sealed 修饰符必须始终与 override一起使用。
2) 将密封类用作基类或将 abstract 修饰符与密封类一起使用是错误的。
3) 结构是隐式密封的;因此它们不能被继承。
答:
相同点:
1) 语法类似。
不同点:
1) class是引用类型,继承自System.Object类; struct是值类型,继承自System.ValueType类,因此不具多态性。但是注意,System.ValueType是个引用类型。
2) 从职能观点来看,class表现为行为; 而struct常用于存储数据。
3) class支持继承,可以继承自类和接口; 而struct没有继承性,struct不能从class继承,也不能作为class的基类,但struct支持接口继承。
4) 实例化时,class要使用new关键字; 而struct可以不使用new关键字,struct在声明时就进行了初始化过程,所有的成员变量均默认为0或null。
答:1) 堆栈的空间有限,对于大量的逻辑的对象,创建类要比创建结构好一些。
2) 结构表示如点、矩形和颜色这样的轻量对象。例如,如果声明一个含有 1000 个点对象的数组,则将为引用每个对象分配附加的内存。在此情况下,结构的成本较低。
3) 在表现抽象和多级别的对象层次时,类是最好的选择。
4) 大多数情况下该类型只是一些数据时,结构时最佳的选择。
答:
抽象类:
1) 抽象方法只作声明,而不包含实现,可以看成是没有实现体的虚方法。
2) 抽象类不能被实例化。
3) 抽象类可以但不是必须有抽象属性和抽象方法,但是一旦有了抽象方法,就一定要把这个类声明为抽象类。
4) 具体派生类必须覆盖基类的抽象方法。
5) 抽象派生类可以覆盖基类的抽象方法,也可以不覆盖。如果不覆盖,则其具体派生类必须覆盖它们。
接口:
1) 接口不能被实例化。
2) 接口只能包含方法声明。
3) 接口的成员包括方法、属性、索引器、事件。
4) 接口中不能包含常量、字段(域)、构造函数、析构函数、静态成员。
5) 接口中的所有成员默认为public,因此接口中不能有private修饰符。
6) 派生类必须实现接口的所有成员。
7) 一个类可以直接实现多个接口,接口之间用逗号隔开。
8) 一个接口可以有多个父接口,实现该接口的类必须实现所有父接口中的所有成员。
抽象类和接口的异同:
相同点:
1) 都可以被继承。
2) 都不能被实例化。
3) 都可以包含方法声明。
4) 派生类必须实现未实现的方法。
区 别:
1) 抽象基类可以定义字段、属性、方法实现。接口只能定义属性、索引器、事件、和方法声明,不能包含字段。
2) 抽象类是一个不完整的类,需要进一步细化,而接口是一个行为规范。微软的自定义接口总是后带able字段,证明其是表述一类“我能做。。。”。
3) 接口可以被多重实现,抽象类只能被单一继承。
4) 抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中。
5) 抽象类是从一系列相关对象中抽象出来的概念, 因此反映的是事物的内部共性;接口是为了满足外部调用而定义的一个功能约定, 因此反映的是事物的外部特性。
6) 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法。
7) 接口可以用于支持回调,而继承并不具备这个特点。
8) 抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的。
9) 如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法。
答:1) 操作系统和运行库环境通常会在应用程序间提供某种形式的隔离。
2) 应用程序域为安全性、可靠性、版本控制以及卸载程序集提供了隔离边界。
3) 应用程序域可以理解为一种轻量级进程。起到安全的作用。占用资源小。
答:为所有变量指定数据类型称为“强类型”。C#是强类型语言。
答:使用基于公共语言运行库的语言编译器开发的代码称为托管代码;托管代码具有许多优点,例如:跨语言集成、跨语言异常处理、增强的安全性、版本控制和部署支持、简化的组件交互模型、调试和分析服务等。
答:CTS:通用系统类型 Common Type System。所有.NET语言共享这一类型系统,实现它们之间无缝的互操作。该方案还提供了语言之间的继承性。
答:CLR:公共语言运行库 Common Language Runtime。是一个运行时环境,它负责资源管理(内存分配和垃圾收集),并保证应用和底层操作系统之间必要的分离。
答:CLS:公共语言规范 Common Language Specification。可以保证C#组件与其他语言组件间的互操作性。
答:1) 委托是一种引用方法的类型。
2) 委托类似于 C++ 函数指针,但它是类型安全的。
3) 委托允许将方法作为参数进行传递。
4) 委托可用于定义回调方法。
答:1) Active Directory存储了有关网络对象的信息,并且让管理员和用户能够轻松地查找和使用这些信息。
2) Active Directory使用了一种结构化的数据存储方式,并以此作为基础对目录信息进行合乎逻辑的分层组织。
答:程序集(中间语言,源数据,资源,装配清单)。
答:1) 值类型通常被分配在栈上,它的变量直接包含变量的实例,使用效率比较高。
2) 引用类型分配在托管堆上,引用类型的变量通常包含一个指向实例的指针,变量通过该指针来引用实例。
3) 一个是值COPY,一个是地址COPY。
值类型 |
引用类型 |
||
内存分配地点 |
分配在栈中 |
分配在堆中 |
|
效率 |
效率高,不需要地址转换 |
效率低,需要进行地址转换 |
|
内存回收 |
使用完后,立即回收 |
使用完后,不是立即回收,等待GC回收 |
|
赋值操作 |
进行复制,创建一个同值新对象 |
只是对原有对象的引用 |
|
函数参数与返回值 |
是对象的复制 |
是原有对象的引用,并不产生新的对象 |
|
类型扩展 |
不易扩展 |
容易扩展,方便与类型扩展 |
答:.NET Framework 的垃圾回收器管理应用程序的内存分配和释放。每次使用 new 运算符创建对象时,运行库都从托管堆为该对象分配内存。只要托管堆中有地址空间可用,运行库就会继续为新对象分配空间。但是,内存不是无限大的。最终,垃圾回收器必须执行回收以释放一些内存。垃圾回收器优化引擎根据正在进行的分配情况确定执行回收的最佳时间。当垃圾回收器执行回收时,它检查托管堆中不再被应用程序使用的对象并执行必要的操作来回收它们占用的内存。
答:索引器允许类或结构的实例按照与数组相同的方式进行索引。索引器类似于属性,不同之处在于它们的访问器采用参数。可以用任意类型索引。
1) 索引器使得对象可按照与数组相似的方法进行索引。
2) get 访问器返回值。set 访问器分配值。
3) this 关键字用于定义索引器。
4) value 关键字用于定义由 set 索引器分配的值。
5) 索引器不必根据整数值进行索引,由您决定如何定义特定的查找机制。
6) 索引器可被重载。
7) 索引器可以有多个形参,例如当访问二维数组时。
答:1) 进程是系统进行资源分配和调度的单位。
2) 线程是CPU调度和分派的单位。
3) 一个进程可以有多个线程,这些线程共享这个进程的资源。
答:启动一个线程是调用start()方法,导致操作系统将当前实例的状态更改为ThreadState.Running。
答:构造器Constructor不能被继承,因此不能重写override,但可以被重载Overloade。
答:静态成员override、virtual 或 abstract。
抽象类不能是密封的sealed或静态的static。
答:using 关键字有两个主要用途:
1) 作为指令,用于为命名空间创建别名或导入其他命名空间中定义的类型。
2) 作为语句,用于定义一个范围,在此范围的末尾将释放对象。
new 关键字:新建实例或者隐藏父类方法
答:.NET错误处理机制采用try->catch->finally结构。 throw
发生错误时,层层上抛,直到找到匹配的catch为止。
答:error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。
不可能指望程序能处理这样的情况。
exception 表示一种设计或实现问题。
也就是说,它表示如果程序运行正常,从不会发生的情况。
答:1) TCP(Transmission Control Protocol)传输控制协议:一种面向连接的、可靠的、基于字节流的运输层通信协议,三次握手。
2) UDP(User Datagram Protocol)用户数据报协议:它不属于连接型协议,因而具有资源消耗小,处理速度快的优点。缺点是易丢失数据包。
答:1) System.String是不可变的字符串。
2) System.StringBuilder存放了一个可变的字符串,并提供一些对这个字符串修改的方法。
3) String类在执行字符串拼接的操作上,用“+”会产生新的对象,占用内存。
4) StringBuilder类只是修改字符串的内容,不建立新的对象。
答:1) const 字段只能在该字段的声明中初始化。
2) 不允许在常数声明中使用 static 修饰符。
3) readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly 字段可能具有不同的值。
4) 另外,const 字段是编译时常数,而 readonly 字段可用于运行时常数。
答:委托可以把一个方法作为参数代入另一个方法。
委托可以理解为指向一个函数的引用。
事件是一种特殊的委托。
答:在类型或成员的声明中使用 unsafe 修饰符。因此,类型或成员的整个正文范围均被视为不安全上下文,无法由 CLR 进行验证的代码。
答:封装、继承、多态。
答:声明IEnumerable接口或实现GetEnumerator()方法。
答:接口可以继承接口。抽象类可以实现(implements)接口,抽象类是否可继承实体类,但前提是实体类必须有明确的构造函数。
答:String类是sealed类故不可以继承。
答:数组、String类都没有Length()方法,它们只有Length属性。
答:即使没有创建类的实例,也可以调用该类中的静态方法、字段、属性或事件。如果创建了该类的任何实例,不能使用实例来访问静态成员。静态成员通常用于表示不会随对象状态而变化的数据或计算。
答:1) 虚函数:没有实现的,可由子类继承并重写的函数。
2) 抽象函数:规定其非虚子类必须实现的函数,必须被重写。
答:?:。格式如下:ndition ? first_expression : second_expression。
答:装箱。
答:delegate.
答:[Serializable]
答:要同时修改Equale和GetHash() ? 重载了"==" 就必须重载 "!="。
答:62移动成2的6次方,26。
答:可以使用指针。也就是非安全代码,就是不在 CLR 完全控制下执行的代码,它有可能会导致一些问题,因此他们必须用 “unsafe” 进行表明。
&是位运算符,表示按位与运算。
&&是逻辑运算符,表示逻辑与(and)
可以。
(1) 实现1
string[] s ={ "111", "22222" };
ArrayList list = new ArrayList();
list.AddRange(s);
(2)实现2
string[] s ={ "111", "22222" };
ArrayList list = new ArrayList(s);
强名是由程序集的标识加上公钥和数字签名组成的,其中,程序集的标识包括简单文本名称、版本号和区域性信息(如果提供的话)。它使用对应的私钥从程序集文件中生成。(程序集文件包含程序集清单,其中包含组成程序集的所有文件的名称和哈希。)
1. for:使用于确定次数的循环。
2. foreach:使用于遍历的元素是只读的。//Unity中此处是个坑,建议不要用foreach
3. while:次数不确定,条件随机变化。
4. do...while:次数不确定,条件随机变化,但至少要保证能被执行一次。
1. 在应用程序和远程设备中使用协议和网络地址初始化套接字。
2. 在应用程序中通过指定端口和地址建立监听。
3. 远程设备发出连接请求。
4. 应用程序接受连接产生通信scoket。
5. 应用程序和远程设备开始通讯(在通讯中应用程序将挂起直到通讯结束)。
6. 通讯结束,关闭应用程序和远程设备的Socket回收资源。
答:会执行,在return前执行。
public class A { public virtual void Fun1( int i ) { Console.WriteLine( i ); } public void Fun2( A a ) { a.Fun1( 1 ); Fun1( 5 ); } } public class B : A { public override void Fun1( int i ) { base.Fun1( i + 1 ); } public static void Main() { B b = new B(); A a = new A(); a.Fun2( b ); b.Fun2( a ); } }
答案:
2
5
1
6
答案:
public static void Main() { int input = int.Parse( Console.ReadLine() ); int sum = 0; for( int i = 0; i <= input; i++ ) { if( ( i % 2 ) == 1 ) { sum += i; } else { sum = sum - i; } } Console.WriteLine( sum ); }
答:不对,有相同的hash code。
答案:可以作用在byte和long上,也可以作用在string上。
答: 1. short s1 = 1; s1 = s1 + 1;有错,s1是short型,s1+1是int型,不能隐式转化为short型。可修改为s1 =(short)(s1 + 1) 。
2. short s1 = 1; s1 += 1正确。
题目:需要实现对一个字符串的处理,首先将该字符串首尾的空格去掉,如果字符串中间还有连续空格的话,仅保留一个空格,即允许字符串中间有多个空格,但连续的空格数不可超过一个。
答案:
string inputStr = " xx xx ";
inputStr = Regex.Replace( inputStr.Trim(), @"/s+", " " );
int i = 5;
int j = 5;
if( Object.ReferenceEquals( i, j ) )
Console.WriteLine( "Equal" );
else
Console.WriteLine( "Not Equal" );
答案:Not Equal。因为比较的是对象
答:const不能用static修饰。
class A
{
public virtual void F()
{
Console.WriteLine( "A.F" );
}
}
abstract class B : A
{
public abstract override void F();//abstract与override关键字可以同时使用
}
答案:没有错误!可以通过编译器。
注意:网上有资料说abstract与override关键字不可以同时使用,这种说法是错误的 !
A. public enum var1{ Mike = 100, Nike = 102, Jike }
B. public enum var1{ Mike = “1”, Nike, Jike }
C. public enum var1{ Mike=-1 , Nike, Jike }
D. public enum var1{ Mike , Nike , Jike }
答案B
class A
{
public static int X;
static A()
{
X = B.Y + 1;
}
}
class B
{
public static int Y = A.X + 1;
static B()
{
}
static void Main()
{
Console.WriteLine( "X={0},Y={1}", A.X, B.Y );
}
}
答:x=1,y=2
class Class1
{
private string str = "Class1.str";
private int i = 0;
static void StringConvert( string str )
{
str = "string being converted.";
}
static void StringConvert( Class1 c )
{
c.str = "string being converted.";
}
static void Add( int i )
{
i++;
}
static void AddWithRef( ref int i )
{
i++;
}
static void Main()
{
int i1 = 10;
int i2 = 20;
string str = "str";
Class1 c = new Class1();
Add( i1 );
AddWithRef( ref i2 );
Add( c.i );
StringConvert( str );
StringConvert( c );
Console.WriteLine( i1 );
Console.WriteLine( i2 );
Console.WriteLine( c.i );
Console.WriteLine( str );
Console.WriteLine( c.str );
}
}
答案:10, 21, 0, str, string being converted
注意:此处加逗号“,”是为了答案看起来清晰,实际结果是纵向排列的,因为调用了Console.WriteLine()。
public abstract class A
{
public A()
{
Console.WriteLine( ‘A‘ );
}
public virtual void Fun()
{
Console.WriteLine( "A.Fun()" );
}
}
public class B : A
{
public B()
{
Console.WriteLine( ‘B‘ );
}
public new void Fun()
{
Console.WriteLine( "B.Fun()" );
}
public static void Main()
{
A a = new B();
a.Fun();
}
}
答案:
A
B
A.Fun()
答:
string的字符串为常量不可修改。
1.可以满足字符串常量池的需要。即对于创建相同文本时,string为同一对象。
2.允许string对象缓存HashCode。字符串不变形保证了hash码的唯一性。
3.安全性,string被许多的类库用来当做参数。
StringBuilder实际是一个char类型的集合,完成了组装拼接等工作。
String更适合做程序的参数,StringBuilder用于做String类型的组装。
答:
StringBuilder的优势在于拼接字符串。
//String的优势在于对字符串做一些处理,在使用过程中看具体的需求。
答:
int number = 46234;
string strNumber=number.ToString();
int length = strNumber.Length;
方法1:
int result=0;
for (int i = 0; i < length; i++)
{
result += number % 10;
number \= 10;
}
方法2:
for (int i = 0; i < length; i++)
{
result += number / (int)Math.Pow(10, i) % 10;
}
方法3:
for (int i = 0; i < strNumber.Length; i++)
{
result += int.Parse(strNumber[i].ToString());
}
注:
答:
冒泡排序:它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换。
冒泡排序算法的运作如下:
l 比较相邻元素。如果第一个比第二个大,就交换它们。
l 对每一对相邻元素做同样的工作,从开始第一队到结尾的最后一对。在这一点,最后的元素会使最大的数。
l 针对所有的元素重复以上的步骤,除了最后一个。
l 持续每次对越来越少的元素重复上面的步骤,知道没有任何一对数字需要比较。
写法1
public void Bubble1(int[] a)
{
bool b;
int tmp;
for (int i = 0; i < a.Length; i++)
{
b = false;
for (int j = i+1; j < a.Length ; j++)
{
if (a[j] > a[i])
{
tmp = a[j];
a[j] = a[i];
a[i] = tmp;
b = true;
}
}
if(!b)break;
}
}
写法2
void Bubble2(int[] a)
{
bool b=false;
int tmp;
for(int i=0;i<a.Length;i++)
{
b=false;
for(int j=0;j<a.Length-i;j++)
{
if(a[j]<a[j+1])
{
tmp=a[j];
a[j]=a[j+1];
a[j+1]=tmp;
b=true;
}
}
if(!b) break;
}
}
写法3
void Bubble3(int[] a)
{
bool b=true;
int j=0;
int temp;
do
{
b=false;
for(int i;i<a.Length-j;i++)
{
if(a[i]<a[i+1])
{
temp=a[i];
a[i]=a[i+1];
a[i+1]=temp;
b=true;
}
}
j++;
}
while(b);
}
object o1 = 1;
object o2 = o1;
o1 = 2;
Console.WriteLine(o2);
答:
1;
原因:o1将数据1在堆中引用赋值给o2,而后又将引用指向数据2,故o2仍指向数据1的引用。
答:
[访问修饰符][可选修饰符] 返回类型 方法名称(参数列表)
{
//方法体;
return 结果;
}
答:
在很多语言当中如Java\C#中字符串string被设计成具有不可变性的。从系统优化方面考虑这样的设计好处主要由三点。
首先,它允许在一个字符串上执行各种操作,而不实际地更改字符串。
字符串不可变,还因为这在操作或访问一个字符串时不会发成线程同步问题。
除此之外,CLR可通过一个String对象共享多个完全一致的String内容。这样能减少系统中的字符串数量,者正是字符串留用技术的目的。
1.字符串常量池的需要。字符串常量池是内存堆中一个特殊的存储区域,当创建一个string对象时,假设此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。
2.允许string对象缓存HashCode。字符串不变性保证了hash码的唯一性,因此可以放心地进行缓存,这是一种性能优化手段,因为这不必每次都去计算新的哈希码。
3.安全性。string被许多的类库用来当做参数,例如网络连接URL,文件路径Path,还有反射机制所需要的String参数等,假若string可变的,将会引起各种安全隐患。
综上,string不可变的原因包括设计考虑,效率优化问题,以及安全性三大方面。
另:在C++中string类型是可变的。
答:
方法名称相同,但参数列表不同称为方法重载。用于在不同条件下解决同一类型的问题。
class A
{
public static int a;
static A()
{
a = B.b + 1;
}
}
class B
{
public static int b;
static B()
{
b = A.a + 1;
}
}
static void Main()
{
Console.WriteLine(A.a);
Console.WriteLine(B.b);
}
答:2 ,1
class C
{
public static int a;
public C()
{
a++;
}
static C()
{
a++;
}
}
static void Main()
{
C c1 = new C();
C c2 = new C();
C c3 = new C();
Console.WriteLine(C.a);
}
答:4
答:
静态函数没有访问修饰符,其他C#代码从来不调用它,但在加载类时,总是由.NET运行库调用它,所以像public或private这样的访问修饰符就没有任何意义。出于同样原因,静态构造函数不能带任何参数,一个类也只能有一个静态构造函数。静态构造函数只能访问类的静态成员,不能访问类的实例成员。
也就是说只加static不会报错,加了static再加了别的就会报错。
答:(a,b)=>{}
注:该题有两种错误可能:
一种是方法没有些返回值即Func(string a,string b)为 void Func(string a,string b)此时,Lambda表达式可写作(a,b)=>{}
一种是Func为Func<string,string>此时Lambda可写作a=>b//a,b为string类型
public static int Fibonacci(int num)//Fibonacci
{
return (num==0||num==1)?1:(Fibonacci(num-2)+Fibonacci(num-1));
}
注:计算斐波那契数列依据数列定义来建立方法是不推荐的写法。因为此时计算第n位的数的时间复杂度为O((3/2)n)。相比较而言,利用循环可以将时间复杂度缩小到O(n)该方法较为实用,而且实现简单,在没有特殊要求的情况下属于建议的写法。另一种优化的写法可以将时间复杂度优化到O(logn),实现代码较复杂,且隐含的时间常数较大。但适合面试者展示其知识面和代码实现的能力。
两种写法如下:
写法一:
public long Fibonacci1(int num)
{
return (num==0||num==1)?1:(Fibonacci1(num-2)+Fibonacci1(num-1));
}
写法二:
public long Fibonacci2(int num)
{
int result[2]={0,1};
if(n<2)
return result[n];
long fibOne=1;
long fibTwo=0;
long fibN=0;
for(int i=2;i<=n;i++)
{
fibN=fibOne+fibTwo;
fibTwo=fibOne;
fibOne=fibN;
}
return fibN;
}
答:
从概念上区分,值类型时直接存储其值,而引用类型是存储对值的引用。
实际中,对于值类型的对象,初始化一个实例,该实例代表的是其内部存储的数据本身。而引用类型的对象,初始化一个实例,该实例代表的是其初始化数据在内存当中的引用。
值类型隐式继承自System.ValueType.该类为抽象类,只能序列化,不能被显式继承。
值类型:枚举,结构
引用类型:数组,委托,类,接口
答:
ArrayList是非泛型列表,存储数据是把所有的数据都当成object类型数据,存在装箱问题,取出来使用的时候存在拆箱问题,装箱拆箱会使性能变差,而且存在数据安全问题,但是优点在于可以让值类型和引用类型相互转换。
List是泛型列表,在使用的时候才会去定义数据类型,泛型避免了拆装箱的问题,存入读取熟读较快,类型也更安全。
解析:首先,这道题的描述是不严谨的,GC作为CLR的一种机制是由系统调用的,只要运行与CLR上的托管代码GC的工作本身是不可避免的。
但是抛开这些细节不谈,我可以在这里写把我认为这道题可能涉及到的东西写一下。
如果只把GC理解成一种可以自动释放内存的方式,使程序员省去了手动释放内存的烦恼,或试图引用已释放的内存空间的错误,这无疑是片面的,虽然这的确是GC非常重要的功能。但GC的作用远不如此。
GC的产生是与CLR堆内存特殊的分配方式有关的。CLR要求所有的资源都从托管堆分配,托管堆有一个指针NextObjPtr它的初始为保留地址空间的基地址。NextObjPtr指向下一个对象在堆中的分配位置。每当添加一个对象,NextObjPtr指针的值就会加上对象占据的字节数。这样做有两个好处:
首先,在许多应用程序中,差不多同时分配的对象彼此间有较强的联系,而且经常差不多在同一时间访问。在垃圾回收环境中,如果对象在内存中连续分配,会由于引用的本地性而获得性能上的提升。具体地说,这意味着进程的工作集小于非托管环境中运行相似的应用程序。
另一方面,这意味着代码使用的对象可以全部驻留在CPU的缓存中,加快CPU对这些对象的访问速度。
但托管堆具有这些好处,是因为它做了一个相当大胆的假设,地址的空间和存储是无限的。那么如何让它由假设变为可实现的,这就是GC垃圾回收的工作。
GC的垃圾回收算法是基于世代法和启发式算法的。
世代法是指将堆分为0代,1代,2代。每当0代的堆满时就触发一次垃圾回收。当第0代的的内存并没被释放,就会被提升到1代,如果1代在被回收时仍然保留的内存,就会被提升到2代。这样压缩空间的的方法保证了堆内存的连续性,增加了访问对象的速度。而启发式算法是指在基于应用程序对新建对象的模式,进行启发式的对第1代,第2代堆进行释放,或扩大第0代的堆空间。
建议只有在以下两种情况下才调用Dispose或Close:确定必须清理资源;或者确定可以安全地调用Dispose或Close,并希望将对象从中介列表中删除,禁止对象提升(到另一代)从而提高性能。
那么避免因为GC过程而影响性能的编程方法如下:
答:
语法不同:
1.抽象类中可以有字段,接口没有。
2.抽象类中可以有实现成员,接口只能包含抽象成员。
3.抽象类中所有成员修饰符都可以使用,接口中所有的成员都是对外的,所以不需要修饰符修饰。
用法不同:
1.抽象类是概念的抽象,接口关注与行为。
2.抽象类的子类与父类的关系是泛化关系,耦合度较高,而实现类和接口之间是实现关系,耦合度比泛化低。
3.一个类只能继承自一个类,但是可以实现多个接口。
string a="abc";//1个对象
a=(a.ToUpper()+"123").Substring(0,2);
答:三个临时对象
注:string a="abc";//1个对象
a=(a.ToUpper()+"123").Substring(0,2);
a.ToUpper()//1个临时对象
"123"//1个临时对象
a.ToUpper()+"123"//1临时个对象
.Substring(0,2)//1个对象由于它将引用赋给了a所以它不是临时对象
在语句运行时结束后不存在引用的对象。
List<int> Is=new List<int>(new int[]{1,2,3,4,5});
foreach(int item in Is)
{
Console.WriteLine(item*item);
Is.Remove(item);
}
答:会产生运行时错误,抛出一个InvalidOperationException异常,因为foreach是只读的。不能一边遍历一边修改。使用foreach时候不要对内容进行修改。
答:sealed访问修饰符用于类时,表示该类不能被继承;对于方法表示不能重写该方法。当应用于方法或属性时,sealed 修饰符必须始终与 override 一起使用。
注:
若要确定是否密封类、方法或属性,通常应考虑以下两点:
派生类利用自定义类的功能所获得的可能好处。
派生类在修改类之后导致其无法正常工作或按预期工作的可能性。
答:public:对任何类和成员都公开,无限制访问
private:仅对该类公开
protected:对该类和其他派生类公开
internal:只能在包含该类的程序集中访问该类
protected internal:protected+internal
注:
可见性修饰符:
修饰符 |
应用于 |
说明 |
public |
所有类型或成员 |
任何代码均可以访问该项 |
protected |
类型和内嵌类型的所有成员 |
只有派生的类型能够访问该项 |
internal |
所有类型或成员 |
只能在包含它的程序集中访问该项 |
prvate |
类型和内嵌类型的所有成员 |
只能在它所述的类型中访问该项 |
protected internal |
类型和内嵌类型的所有成员 |
只能在包含它的程序集和派生类型的代码中访问该项。 |
其他修饰符
修饰符 |
应用于 |
说明 |
new |
函数成员 |
成员用相同的签名隐藏继承的成员 |
static |
所有成员 |
成员不作用于类的具体实例 |
virtual |
仅函数成员 |
成员可以由派生类重写 |
abstract |
仅函数成员 |
虚拟成员定义了成员的签名,但没有提供实现代码 |
override |
仅函数成员 |
成员重写了继承的而虚拟或抽象成员 |
sealed |
类、方法和属性 |
对于类,不能继承自密封类。对于属性和方法,成员重写已继承的虚拟成员,但任何派生类中的任何成员都不能重写该成员。当应用于方法或属性时,sealed 修饰符必须始终与 override 一起使用。 |
extern |
仅静态[DllImport]方法 |
成员在外部用另一种语言实现
|
答:审查元数据并收集关于它的类型信息的能力。
注:
公共语言运行时程序管理应用程序域,应用程序域构成具有相同应用程序范围的对象周围定义的边界。 此管理包括将每个程序集加载到相应的应用程序域中和控制每个程序集内的类型层次结构的内存布局。
程序集包含模块、模块包含类型,而类型包含成员。 反射提供封装程序集、模块和类型的对象。 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
Reflection,中文翻译为反射。
这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息,例如:
Assembly类可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。
Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。
MethodInfo包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。
诸如此类,还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。
答:MONO是.NET的一个开源跨平台工具。.NET只能在Windows下运行,Mono可以实现跨平台,可以运行与Linux,Unix,Mac OS等。
答:
1)单一职责原则:一个类,最好只做一件事,只有一个引起它的变化。
2)开放-封闭原则:对于扩展是开放的,对于更改是封闭的。
3)里氏替换原则:子类必须能够替换其基类。
4)依赖倒置原则:设计应该依赖于抽象而不是具体实现。
A.高层模块不应该依赖底层模块,两个都应该依赖抽象。
B.抽象不应该依赖细节,细节应该依赖抽象(要对抽象编程,不要对实现编程。)
5)接口隔离原则:使用多个小的专门的接口而不要使用一个大的总接口。
6)迪米特法则:如果两个类不彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
7)合成/聚合复用原则:尽量使用合成/聚合,尽量不要使用类继承。
注:
答:电脑没有绝对的无序,hashtable是通过哈希码让开发者感觉无序。
注:这里的无序是指集合的添加顺序与其输出顺序无关。Hashtable是通过哈希码或哈希函数,对其内部元素进行排列的,所以其元素的添加顺序与其输出顺序无关。
答:
单例: 对象池,游戏管理器
抽象工厂,
状态:有限状态机,
桥接:有限状态机
策略:AI自动行为操控中每种操控算法的独例
注:扩展补充。
创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)、访问者模式。
答:列表:
特点:有序,内存空间连续,读取速度快,大小可变,通过位置索引取元素.
缺点:输入,删除的速度慢
类库已实现:List<T>、 ArrayList
字典:
特点:键值对的集合,通过键查找值,查找方便,无序
类库已实现:Dictionary<TKey,TValue>、Hashtable
栈:
特点:后进前出 LIFO(Last In First Out)
支持操作:入栈,出栈,读栈顶,
类库已实现:Stack<T>、Stack
队列:
特点:前进前出 FIFO(First In First Out)
支持操作:入队,出队,读队首
类库已实现:Queue<T>、Queue
链表:
特点:节点内存空间不连续,插入,删除速度快,读取速度慢.
类库已实现:LinkedList<T>
数组:
特点:内存空间连续,长度固定,读取速度快
类库已实现:Array
树:
特点:可以将对象组织为层次结构,
实现:可使用设计模式中的组合模式实现树结构
答:
Override: 是指方法的重写,C#中可重写abstract、vritual 、override三类方法,重写是子类重写父类的以上三种方法,是动态绑定的,调用速度比方法隐藏慢,但灵活性好,重写本质是对方法表的修改
Overload:是指方法的重载,一个类中的多个同名方法,参数列表不同,主要指参数个数或类型不同,重载的目的是为了让实现相似功能的方法用一个名称表示,方便程序员编写和理解代码。可读性好。
重写和重载都是多态的一种体现。
答:
数组:长度固定,大小不变,存储空间连续,读取速度快,插入,删除速度慢。
集合:长度不定,大小可变,存储空间可能不连续,适合存放不确定数量的数据。
答:
方法一:实现IComparer, 使用Array.Sort(Array, IComparer )
方法二:使用OrderBy或OrderByDescending 方法加Lambda表达式
如:arr = arr.OrderBy(p=>p.age).ToArray();
答: 在按钮上绑定一个带有OnClick事件List<EventDelegate>;
EventDelegate();
EventDelegate(Callback call);//void Callback()
EventDelegate(MonoBehaviour target,string methodName)
答:必须安装QuickTime播放器,不支持FLV视频格式。
答:MovieTexture。
答:
存储:PlayerPrefs.SetInt ("MyKey", 1);
获取:int myKey = PlayerPrefs.GetInt ("MyKey");
答:
PlayerPrefs.DeleteKey ("MyKey");
PlayerPrefs.DeleteAll ();
答:NavMeshAgent。
答:寻路网格动态碰撞组件,用于运动的物体阻碍寻路物体效果。
答:射线碰到的碰撞信息
答:通过LayerMask(层的遮罩),RaycastHit返回的碰撞的标签。
答:Generic Humanoid类人样式。
关键帧动画
默认骨骼动画(人型)
CAT
动画->点缓存->关键帧
答:
l 简单化对类人角色动画设计与功能实现的工作流程。
l Mecanim动画系统使用一个可视化编程工具来管理动画之间复杂的交互。
l 对完成绑定骨骼的类人角色,能够对身体不同的地方使用不同的逻辑进行动画控制。
l 可以在角色动画播放过程中添加动画事件。
l 人形动画骨骼动画可重复使用。
答:Panel是一个容器,它将包含所有UI小部件,并负责将所包含的部件组合。
Anchor是NGUI中屏幕分辨率的自适应性,来适应不同的分辨率的屏幕显示。
注:
答:以平面任意两点,取两平面法向量,链接两点线段,线段是否与法向量的夹角来判断是否平行。
法向量如果平行,看线段与法向量的夹角,如果平行则两平面重合,判断边界是否相交。
如果平行,且有夹角,说明不重合两平面平行,一定不相交。
如果法向量不平行,将该线段的在两平面投影方向上的射线判断与平面的边界条件,如果两条射线的交点都在边界内,则相交,否则不相交。
注:
答:MeshCollider是网格碰撞器,对于复杂网状模型上的碰撞检测,比其他的碰撞检测精确的多,但是相对其他的碰撞检测计算也增多了,所以一般使用网格碰撞也不会在面数比较高的模型上添加,而会做出两个模型,一个超简模能表示物体的形状用于做碰撞检测,一个用于显示。
MeshCollider有Mesh属性
答:
答:两个物体都必须带有碰撞器(Collider),其中一个物体还必须带有Rigidbody刚体。
注:动的物体,你希望发生碰撞效果的物体要带能接受物理引擎的操作,最常见的就是挂载Rigidbody组件。
答:CharacterController自带胶囊碰撞器,里面包含有刚体的属性;
Rigidbody就是刚体,使物体带有刚体的特征。
注:
答:穿透(碰撞检测失败)。将物体移动放在FixedUpdate中,尽量使用物理引擎自己的移动方法。
避免的方法:把刚体的实时碰撞检测打开Collision Detection修改为Continuous Dynamic
注:
答:
主要是三个阶段:
1.Collider.OnCollisionEnter 进入碰撞,当collider/rigidbody开始触动另一个rigidbody/collider时OnCollisionEnter被调用。
2.Collider.OnCollisionStay 逗留碰撞,每个collider/rigidbody触动rigidbody/collider,将在每帧调用OnCollisionStay。通俗的说,一个碰撞器或刚体触动另一个刚体或碰撞器,在每帧都会调用OnCollisionStay,直到它们之间离开不接触。
3.Collider.OnCollisionExit 退出碰撞,当 collider/rigidbody停止触动另一个 rigidbody/collider时,OnCollisionExit被调用。
答:
答:主要有关节动画、单一网格模型动画(关键帧动画)、骨骼动画。
1.关节动画:把角色分成若干独立部分,一个部分对应一个网格模型,部分的动画连接成一个整体的动画,角色比较灵活,Quake2中使用这种动画。
2.单一网格模型动画由一个完整的网格模型构成,在动画序列的关键帧里记录各个顶点的原位置及其改变量,然后插值运算实现动画效果,角色动画较真实。
3.骨骼动画,广泛应用的动画方式,集成了以上两个方式的优点,骨骼按角色特点组成一定的层次结构,有关节相连,可做相对运动,皮肤作为单一网格蒙在骨骼之外,决定角色的外观,皮肤网格每一个顶点都会受到骨骼的影响,从而实现完美的动画。
答:反转动画,将动画的播放速度调到-1。
答:AddClip 添加剪辑、Blend 混合、Play 播放、Stop 停止、Sample 采样 、CrossFade淡入淡出切换动画、IsPlaying是否正在播放某个动画
答:动画层作为一个具有层级动画编辑概念的工具,可以用来制作和处理任何类型的动画。202,203题
答:ITween是补间动画的一个插件,主要作用就是给出开始、结束的值、时间,此插件实现各种动画,晃动,旋转,移动,褪色,上色,音量控制等等。
方法: 1.MoveTo 物体移动
2.ColorTo:随着时间改变对象的颜色组
3.LookTo:随时间旋转物体让其脸部朝向所提供的Vector3或Transform位置
答:
法线贴图:是一种特殊的纹理,可以应用在3D表面,让低模呈现出更明显的凹凸效果。一
般应用在CG动画、美术效果要求较高的单机游戏
CG动画:游戏中的CG动画其实是用3D模拟引擎制作的游戏短片,一般画面效果比较真实。
答:Unity支持多线程,如果同时要处理很多事情或者与Unity的对象互动小可以用thread,否则使用coroutine。
注意:1.虽然支持多线程,但是仅能从主线程中访问Unity3D的组件,对象和Unity3D系统调用,所以如果使用的话需要把组件中的数值传到开启的新线程中。
2.C#中有lock这个关键字,以确保只有一个线程可以在特定时间内访问特定的对象
答:多线程程序同时运行多个线程,除主线程之外的线程无法访问Unity3D的对象、组件、方法,而在任一指定时刻只有一个协程在运行。
答:在主线程运行时同时开启另一段逻辑处理,来协助当前程序的执行。使用户可以编写更灵活的程序来控制运动、序列以及对象的行为。
答:所谓四元数,就是把4个实数组合起来的东西。4个元素中,一个是实部,其余3个是虚部
作用:四元数用于表示旋转
优点:
1)能进行增量旋转
2)避免万向锁
3)给定方位的表达方式有两种,互为负(欧拉角有无数种表达方式)
四元数不会有欧拉角存在的 gimbal lock 问题[万向节死锁]
四元数由4个数组成,旋转矩阵需要9个数
两个四元数之间更容易插值
四元数、矩阵在多次运算后会积攒误差,需要分别对其做规范化(normalize)和正交化 (orthogonalize),对四元数规范化更容易
与旋转矩阵类似,两个四元组相乘可表示两次旋转
答:游戏界面可以看到很多摄像机的混合。
答:正交和透视。
正交无法看到一个物体距离自己有多远,或者远近的变化,物体也不会随着距离而收缩,所以一般做2D游戏或者是UI时会使用正交摄像机。
透视一般看物体会随着距离有大小的变化,一般3D游戏里经常使用这种摄像机。
答:Prefab是预制件(预制物),一般当游戏中需要频繁创建一个物体时,使用预制物可以节省内存,方便创建,方便对游戏物体进行操作,方便对属性进行修改。
答:动态加载再实例化,如果自己不主动清理内存的话,再次加载不会增加内存的,会自动去取之前已经加载好的assets,如果这一个assets你都嫌多的话,那你只能减资源了,比如,模型面数,纹理尺寸等
答:实际光照强度l=环境光(lambient)+漫反射光(Idiffuse)+镜面高光(lspecular)
环境光:lambient=环境光强度(Aintensity)*环境光颜色(Acolor)
漫反射光:ldiffuse=镜面光照强度(Dintensity)*镜面光颜色(Scolor)*(光的反射向量(R).观察者向量(V))^镜面光指数(n)
答:MeshRender是模型渲染的组件,有此组件物体才能显示出来
Material是材质球,实际就是shader的实例,并进行赋值,贴图、纹理、颜色等。
Shader是着色器,实际上是一段程序,还可以用来实现一些仅靠贴图不容易实现的效果,如玻璃。
Shader大致分为:1.表面着色器
2.顶点和片元着色器
3.固定功能着色器
答:Alpha Blend是 实现透明效果,Color = 原颜色*alpha/255+目标色*(255-alpha)/255
答:1.使用光照贴图比使用实时光源渲染要快
2.可以降低游戏内存消耗
3.多个物体可以使用同一张光照贴图
答:顶点着色器:顶点着色器是一段执行在GPU上的程序,用来取代fixed pipeline中的
transformation和lighting,Vertex Shader主要操作顶点。
Vertex Shader对输入顶点完成了从local space到homogeneous space(齐次空间)的变换过程,homogeneous space即projection space的下一个space。在这其间共有world transformation, view transformation和projection transformation及lighting几个过程。
答:4种,Directionl light ,Point Light ,Spot Light,Area Light
平行光:Directional Light
点光源:Point Light
聚光灯:Spot Light
区域光源:Area Light
答:FixedUpdate,每固定帧绘制时执行一次,和Update不同的是FixedUpdate是渲染帧执行,如果你的渲染效率低下的时候FixedUpdate调用次数就会跟着下降。FixedUpdate比较适用于物理引擎的计算,因为是跟每帧渲染有关。Update就比较适合做控制。
答:Awake –>OnEnable->Start
OnEnable在同一周期中可以反复地发生
Awake用于在游戏开始之前初始化变量或游戏状态。在脚本整个生命周期内它仅被调用一次.Awake在所有对象被初始化之后调用,所以你可以安全的与其他对象对话或用诸如GameObject.FindWithTag这样的函数搜索它们。每个游戏物体上的Awke以随机的顺序被调用。因此,你应该用Awake来设置脚本间的引用,并用Start来传递信息。
Start在脚本的生命周期中只被调用一次。它和Awake的不同是Start只在脚本实例被启用时调用。你可以按需调整延迟初始化代码。Awake总是在Start之前执行。这允许你协调初始化顺序。
但Start可以作为协程,达到在同一周期调用多次的效果。不过实质上依然是调用一次。
答:ExcutionOrder有关,不设定顺序的时候初始化Start的顺序不确定。
答:
|
方法名(按脚本声明周期排列) |
作用 |
备注 |
Editor |
Reset |
重置为默认值。 |
在用户点击见识版面的Reset按钮或者首次添加该组件时被调用。此方法只在编辑模式下被调用。 |
Initialization |
Awake |
|
|
OnEnable |
|
|
|
Start |
Start在behaviour的生命周期中只被调用一次。它和Awake的不同是Start只在脚本实例被启用时调用。你可以按需调整延迟初始化代码。Awake总是在Start之前执行。这允许你协调初始化顺序。 |
Start仅在Update方法第一次被调用前调用。 |
|
Physics |
FixedUpdate |
处理Rigidbody时,需要用FixedUpdate代替Update。例如:给刚体加一个作用力时,你必须应用作用力在FixedUpdate里的固定帧,而不是Update中的帧。(两者帧长不同) |
如果固定时间步长小于实际帧更新时间,那么每一帧物理周期将会可能发生不止一次。 |
yield WaitForFixedUpdate |
|
||
Internal physics update |
|
||
OnTriggerXXX |
|
||
OnCollisionXXX |
|
||
Input events |
OnMouseXXX |
|
|
Game logic |
Update |
|
如果一个协成之前已经yield了,但是现在由于恢复了,那么将执行剩下的部分。 |
yield null |
|
||
yield WaitForSeconds |
|
||
yield WWW |
|
||
yield StartCoroutine |
|
||
Internal animation update |
|
||
LateUpdate |
|
||
Scene rendering |
OnWillRenderObject |
如果对象可见每个相机都会调用它。此函数在消隐过程中被调用,在渲染所有被消隐的物体之前被调用。你可以用它来创建具有依赖性的纹理并且只有在被渲染的物体可见时才更新这个纹理。举例来讲,它已用于水组件中。 |
|
OnPreCull |
消隐决定哪个物体对于相机来说是可见的.OnPreCull仅是在这个过程被调用。 |
只有脚本被附加到相机上时才会调用这个函数。 |
|
OnBecameVisible |
|
|
|
OnBecameInvisible |
|
|
|
OnPreRender |
在相机渲染场景之前被调用。 |
只有脚本被附加到相机并被启用时才会调用这个函数。 |
|
OnRenderObject |
|
|
|
OnPostRender |
在相机完成场景渲染之后被调用。 |
只有该脚本附于相机并启用时才会调用这个函数。OnPostRender可以是一个协同程序。 |
|
OnRenderImage |
|
|
|
Gizmo rendering |
OnDrawGizmos |
|
只在编辑模式下调用 |
GUI rendering |
OnGUI |
|
在每一帧更新时调用多次 |
End of frame |
yield WaitForEndOfFrame |
|
|
Pausing |
OnApplicationPause |
|
在程序检测到暂停时,会在帧的结尾处被调用。 |
Disable/enable |
OnDisable |
|
在脚本是失效时被调用,被销毁时也会被调用。如果再启用的话,OnEnable会再一次被调用。 |
Decommissioning |
OnDestroy |
|
|
OnApplicationQuit |
|
|
答:FixedUpdate因为不受到计算机帧频的影响,所以适合于做物理方面的更新。
答:LateUpdate,它是在Update结束后才调用。一般来说摄像机的跟随,都是在所有Update操作完成后进行的,以免出现摄像机已经推进了,但是还有角色未刷新的空帧出现。
答:当物体是否可见切换之时。可以用于只需要在物体可见时才进行的计算。
当renderer(渲染器)在任何相机上都不可见时调用:OnBecameInvisible
当renderer(渲染器)在任何相机上可见时调用:OnBecameVisible
答:PlayerPrefs.SetInt()
PlayerPrefs.GetInt()
答:单独的动画状态,混合树。
答:
动画层中的权重是指某个动画层在整个动画中的影响,Weight的取值范围为(0~1)
若权重值为1则此层动画将与整个动画融合
若权重值为0则此层动画与整个动画完全不融合
答:使用动画层来管理身体不同部分的复杂状态机。比如:你可以使用下半身(动画)层来管理走动/跑动;使用上半身(动画)层来控制攻击/挥手动作。
Animator anim ;
int idle = Animator.StringToHash("Base Layer.Idle");
AnimatorStateInfo currentBaseStage;
void Start () {
anim = GetComponent<Animator>();
}
void Update () {
currentBaseStage = anim.GetCurrentAnimatorStateInfo(0);
if (currentBaseStage.nameHash == idle && !anim.IsInTransition(0)) {
anim.SetBool("hit", false);
}
答:判断当前播放的动画名字若是默认层的Idle,并且没有在进行动画切换,则设置hit动画参数为false。
答案:9
答:动画事件。
答:
1D是使用一个动画参数来控制blend tree中几个动画判断的融合,
2D是使用两个动画参数来控制blend tree中几个动画判断的融合。
答:
public Camera myCamera;
myCamera.WorldToScreenPoint(hit);
答:网格寻路。
答:
1.Resources.Load();
2.AssetBundle
区别:
Resources资源的加载是动态加载内部的,AssetBundle 是动态加载外部的。
Resources是动态内部调用,Resources在编辑环境下是project窗口的一个文件夹,调用里面的资源,可以用Resources类,比如Resources.Load,打包后这个文件夹是不存在的,会统一生成Assets资源。
AssetBundle 是外部调用,要用AssetBundle 首先要先把资源打包为.assetbundle文件,再动态的去加载这个文件,本地或者网络服务器都可以。
WWW读取文件
答: MonoBehaviour.OnLevelWasLoaded
答:Destory
答:获取:GetComponent
增加:AddComponent
没有删除组件只有让组件不生效:enable
答:加载关卡 。
UnityEngine.SceneManagement名称空间下
SceneManager.LoadScene//加载场景
SceneManager.LoadSceneAsync//异步加载场景
答: Debug.Log();
答:层索引。射线。
答:
localPosition:自身位置,相对于父级物体的变换的位置,局部坐标其实就是自身的坐标。
Position:在世界坐标transform的位置,世界坐标是不会变的,一直以世界坐标轴的XYZ为标准。
答:Transform.Rotate()。
答:
public class TestAround : MonoBehaviour
{
public GameObject ObjA;
public GameObject ObjB;
Vector3 NormalVector3;//法线
float speed = 5;
void Start()
{
GetNormal(AroundType.Horizontal);
}
private void GetNormal(AroundType aroundType)
{
Vector3 temp = ObjB.transform.position - ObjA.transform.position;
switch (aroundType)
{
case AroundType.Vertical:
NormalVector3=Vector3.Cross(Vector3.forward,temp);
break;
case AroundType.Horizontal:
NormalVector3 = Vector3.Cross(Vector3.up, temp);
break;
default:
break;
}
}
void Update()
{
Debug.DrawLine(NormalVector3,ObjA.transform.position);
Vector3 temp = ObjB.transform.position - ObjA.transform.position;
Debug.DrawLine(temp, ObjA.transform.position);
Around(ObjA, ObjB);
}
private void Around(GameObject objA, GameObject objB)
{
Quaternion quater=Quaternion.Euler( NormalVector3*Time.deltaTime*speed);
objB.transform.position = quater * objB.transform.position;
}
}
enum AroundType
{
Vertical,
Horizontal
}
答:Transform 父类是 Component
答:添加刚体使小鸟模拟受到重力和空气阻力影响。不添加刚体的话写受力分析。
设横轴为方向的加速度为ax,垂直轴为ay。
小鸟的Vx,Vy
Vx=Vx+t*ax;(Vx>=0)
Vy=Vy+t*ay;
答:PhysicMaterial 物理材质:主要是控制物体的摩擦,弹力等物理属性。
Material材质:主要是控制一个物体的颜色,质感等显示。
答:rigidbody.AddForce/AddForceAtPosition,都在rigidbody系列函数中。
答:Hinge Joint,可以模拟两个物体间用一根链条连接在一起的情况,能保持两个物体在一个固定距离内部相互移动而不产生作用力,但是达到固定距离后就会产生拉力。
答: Unity内一种用于实现自动寻路的网格。
答: JavaScript,C#,Boo
答:1)点乘计算两个向量之间的夹角,还可表示某一方向的投影。
2)叉乘得到的是法向量。
3)标准化向量:用在只关系方向,不关心大小的时候。
答:用于表示线性变换:旋转、缩放、投影、平移、仿射。
注意:矩阵的蠕变:误差的积累。
答:Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。
使用动态字体时,Unity将不会预先生成一个与所有字体的字符纹理。当需要支持亚洲语言或者较大的字体的时候,若使用正常纹理,则字体的纹理将非常大。
答:render是渲染器,渲染器可以使物体显示在屏幕上。
MeshRender是网格渲染,SkinnedMeshRender是蒙皮网格渲染器
答:骨骼蒙皮动画,模型本身是静态的,是因为通过蒙皮,使模型每个点都有Skin数据,Skin数据包括顶点受到哪些骨骼影响以及这些骨骼影响顶点的权重值,还有动画数据,有了Skin数据的模型就可以根据动画数据进行显示动画了。
答:1)将Assets目录和Library目录一起迁移。
2)导出包。
3)用unity自带的assets Server功能。
答:LOD是Level of detail简称,意为多层次细节,是最常用的游戏优化技术,LOD技术指根据物体模型的几点在显示环境中所处的位置和重要性,决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。
优点:可根据距离动态的选择渲染不同细节的模型
缺点:增加美工工作量,增大了游戏的容量。
注:精简文字描述。
答:LOD(Level of detail)多层次细节,是最常用的游戏优化技术,LOD技术指根据物体模型的几点在显示环境中所处的位置和重要性,决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。
优点:可根据距离动态的选择渲染不同细节的模型
缺点:增加美工工作量,增大了游戏的容量。
答:自身阴影:因物体自身的遮挡而使光线照射不到它上面的某些可见面
工作原理:利用背面剔除的方法求出,即假设视点在点光源的位置。
投射阴影:因不透明物体遮挡光线使得场景中位于该物体后面的物体或区域收不到光照照射而形成的阴影。
工作原理:从光源处向物体的所有可见面投射光线,将这些面投影到场景中得到投影面,再将这些投影面与场景中的其他平面求交得出阴影多边形,保存这些阴影多边形信息,然后在按视点位置对场景进行相应处理得到所要求的视图(利用空间换时间,每次只需依据视点位置进行一次阴影计算即可,省去了一次消隐过程)若是动态光源此方法就无效了。
答:MipMapping:在三维计算机图形的贴图渲染中有常用的技术,为加快渲染进度和减少图像锯齿,贴图被处理成由一系列被预先计算和优化过的图片组成的文件,这样的贴图被称为MipMap。
答:指定身体的某一部分是否参与渲染
答:需要得到Mono.Data.Sqlite.dll 文件与System.Data.dll文件。
答:1.压缩自带类库;
2.将暂时不用的以后还需要使用的物体隐藏起来而不是直接Destroy掉;
3.释放AssetBundle占用的资源;
4.降低模型的片面数,降低模型的骨骼数量,降低贴图的大小;
5.使用光照贴图;
6.使用多层次细节(LOD);
7.使用着色器(Shader);
8.使用预设(Prefab)等。
解析:内存开销无外乎三大部分:1.资源内存占用;2.引擎模块自身内存占用;3.托管堆内存占用
在一个较为复杂的大型项目中,资源的内存占用往往占据了总体内存的70%以上。因此,资源使用是否恰当直接决定了项目的内存占用情况。一般来说,一款游戏项目的资源主要可以分为以下几种:纹理(Texture),网络(Mesh),动画片段(AnimationClip)、音频片段(AudioClip)、材质(Material)、着色器(shader)、字体资源(Font)以及文本资源(TextAsset)等等。其中,纹理,网格、动画片段和音频片段则容易造成较大内存开销的资源。
纹理资源可以说是几乎所有游戏项目中占据最大内存开销的资源。一个6万片的场景,网格资源最大才不过10MB,但一个2048*2048的纹理,可能直接就达到16MB,因此,项目中纹理资源的使用是否恰当会及大地印象项目的内存占用。
那么,纹理资源再使用时应该注意哪些地方呢?
纹理格式。纹理格式是研发团队最需要关注的纹理属性。因为它不仅影响纹理的内粗你占用,同时还决定了纹理的加载效率。一般来说,我们建议开发团队尽可能根据硬件的种类选择硬件支持的纹理格式比如Android平台的ETC、iOS平台的PVRTC、WindowsPC上的DXT等等。因此,我们在UWA测评报告中,将纹理格式进行了罗列,一边开发团队进行快速查找,一步定位。
在使用硬件支持的纹理格式时,你可能会遇到以下几个问题:
色阶问题
由于ETC、PVRTC等格式均为有损压缩,因此,当纹理色差范围跨度较大时,均不可避免地造成不同程度的阶梯状的色阶问题。因此,很多研发团队使用RGBA32/ARGB32格式来实现更好的效果。但是,这种做法将造成很大的内存占用。比如,同样一张1024*1024的纹理,如果不开启Mipmap,并且为PVRTC格式,则其内存占用为512KB,而如果转换为RGBA32位,则很可能占用更达到4MB。所以,研发团队在使用RGBA32或ARGB32格式的纹理时,一定要慎重考虑,更为明智的选择是尽量减少纹理的色差范围,时期尽可能使用硬件支持的压缩格式进行存储。
ETC1不支持透明通道问题
在Android平台上,对于使用OpenGLES2.0的设备,其纹理格式仅能支持ETC1格式,该格式有个较为严重的问题,即不支持Alpha透明通道,使得透明贴图无法直接通过ETC1格式来进行存储。对此。我们建议研发团队将透明贴图尽可能分成两张,即一张RGB24位纹理记录原始纹理的颜色部分和一张Alpha8纹理记录原始纹理的透明通道部分。然后,将这辆张贴图分别转化为ETC1格式的纹理,并通过特定的Shader来进行渲染,从而来达到支持透明贴图的效果。该种方法不仅可以极大程度上逼近RGBA透明贴图的渲染效果,同时还可以降低纹理的内存占用,是我们非常推荐的使用方式。
当然,目前已经有越来越多的设备支持了OpenGLES3.0这样Android平台上你可以进一步使用ETC2甚至ASTC,这些纹理格式均为支持透明通道且压缩比更为理想的纹理格式。如果你的游戏适合人群为中高端设备用户,那么不妨直接使用这两种格式来作为主要存储格式。
纹理尺寸
一般来说,纹理尺寸越大,占用内存越大。
Mipmap功能
Mipmap旨在有效降低渲染带宽的压力,提升游戏的渲染效率。但是,开启Mipmap会有将纹理内存提升1.33倍。对于具有较大纵深感的3D游戏来说,3D场景模型和角色我们一般是建议开启Mipmap功能的,但很多2DUI纹理就不必要了。
Read&Write
一般情况下,文理资源“Read&Write”功能在Unity引擎中是默认关闭的。但是,我们仍然在项目深度优化时发现了不少项目的纹理资源会开启该选项。对此,我们建议压发团队密切关注文理资源中该选项的使用,因为开启该选项将会使文理内存增大一倍。
网格资源在比较复杂的游戏中,往往占据较高的内存。对于网格资源来说,它在使用时应该注意那方面呢?
1) Normal\Color和Tangent(切线)
在我们深度优化过的大量项目中,Mesh资源的数据经常会含有大量的Color数据,Normal数据和Tangent数据。这些数据的存在将大幅增加Mesh资源的文件体积和内存占用。其中Color数据和Normal数据主要为3DMax、Maya等建模软件导出时设计所生成,而Tangent一般为导入引擎时生成。
更为麻烦的是,如果项目对Mesh进行DrawCallBatching操作的话,那么狠有可能进一步增加总体内存的占用。正因如此,我们在UWA测评报告中为每个Mesh展示了其Normal,Color和Tangent属性的具体使用情况,研发团队可以直接针对每种属性进行排序查看,直接定位出现冗余数据的资源。一般来说这些数据主要为Shader所用,来生成较为酷炫的效果。所以,建议研发团队针对项目中的网格资源进行详细检测,查看该模型的渲染Shader中是否需要这些数据进行渲染。
答:
解析:CPU方面
就目前Unity移动游戏而言,CPU方面的性能开销主要可归结为两大类:引擎模块性能开销和自身代码性能开销。其中,引擎模块中又可细致划分为渲染模块、动画模块、物理模块、UI模块、粒子系统、加载模块和GC调用等等。
通过大量的性能测评数据,我们发现渲染模块、UI模块和加载模块,往往占用了CPU性能开销的Top3。
1降低DrawCall
降低DrawCall的方法则主要是减少渲染物体的材质种类,并通过DrawCallBatching来减少其数量。Unity文档对DrawCallBatching的原理和注意事项非常详尽,感兴趣的朋友可以直接查看Unity官方文登。
但是,需要注意的是游戏性能并非DrawCall越小越好。这是因为,决定渲染模块心梗的除了DrawCall之外,还有用于传输渲染数据的总先带宽。当我们使用DrawCallBatching将同种的材质和网络模型拼合在仪器室,可能会造成同一时间需要传输的数据(Texture、VB/IB等)大大增加,以至于造成堵塞。在资源无法计时传输过去的情况下,GPU只能等待,从而反倒降低了游戏的运行帧率。
DrawCall和总线带宽是天平的两端,我们需要做的是尽可能维持天平的平衡,任何一边过高或过低,对性能来说都是无益的。
2简化资源
简化资源师非常行之有效的优化手段。在大量的移动游戏中,其渲染资源其实是过量的。过量的过量的网络资源、不合规的纹理资源等等。
关于渲染模块在CPU方面的优化方法还有很多,比如LOD、Occlusion Culling和Culling Distance等等。
2 UI模块
UI模块同样也是几乎所有的游戏项目中必备的模块。一个性能优异的UI模块可以将游戏的用户体验再抬高一个档次。
在NGUI优化方面,UIPanel.LateUpdate为性能优化的重中之重。
对于UIPanel.LateUpdate的优化,主要着眼与UIPanel的布局,其原则如下:
尽可能将动态UI元素和静态UI元素分离到不同的UIPanel中,从而尽可能将因为变动的UI元素引起的冲欧控制在较小的范围内;
尽可能让动态UI元素按照同步性进行划分,即运动帧率不同的UI元素尽可能分离放在不同的UIPanel中;
控制同一个UIPanel中动态UI元素的数量,数量越多,所创建的Mesh越大,从而使得重构的开销曾加。比如战斗过程中的HUD运动协调会出现的比较多,此时,建议研发团队将运动血条分离成不同的UIPanel,每组UIPanel下5~10个动态UI为宜。这种做法,其本质是从概率上尽可能降低单振中UIPanel的重建开销。
3加载模块
加载模块同样也是任何游戏项目中所不可缺少的组成成分。与之前两个模块不同的是,加载模块的性能开销比较集中,主要出现于场景切换处,且CPU占用峰值均较高。
这里,我们先来说说场景切换时,其性能开销的主要体现形式。对于目前的Unity版本而言,场景切换时的主要性能开销主要体现在两个方面,前一场景的场景卸载和下一场景的场景加载。下面,我们就具体来说说两个方面的性能瓶颈:
1场景卸载,对于Unity引擎而言,场景卸载一般是由引擎自动完成的,即当我们调用类似Application.LoadLevel的API时,引擎即会开始对上一场景进行处理,其性能开销主要被以下几部分占据。
Destroy
引擎在切换场景时会手机为表示成DontDestoryOnLoad的GameObject及其Component,然后进行Destroy。同时,代码中的OnDestory被触发执行,这里的性能开销主要取决于OnDestroy回调函数中的代码逻辑。
Resources.UnloadUnusedAssets
一般情况下,场景切换过程中,该API会被调用两次,依次为引擎在切换场景是自动调用,另一次则为用户手动调用(一般出现在场景加载后,用户调用它来确保上一场景的资源被卸载干净)。在我们测评过的大量项目中,该API的CPU开销主要集中在500ms~3000m之间。其耗时开销主要取决于场景中Asset和Object的数量,数量越多,则耗时越慢。
2场景加载
场景加载过程的性能开销又可细分成以下几个部分:
资源加载
资源加载几乎占据整个加载过程的90%以上,其加载效率主要取决于资源的加载方式(Resource.Load或AssetBundle加载)、加载量(纹理、网格、材质等资源数据的大小)和资源格式(纹理、音频格式等)。不同的加载方式、不同的资源格式,其加载效率可谓千差万别。
Instantiate实例化
在场景加载过程中,往往伴随着大量的Instantiate实例化操作。在Instantiate实例化时,引擎底层会查看相关的资源是否已经被加载,如果没有,则会先加载其相关资源,再进行实例化,这其实是大家遇到大多数“Instantiate”实例化问题的根本原因。
AssetBundle文章中所提倡的资源依赖关系打包并进行预加载,从而来缓解Instantiate实例化的压力。
另一方面,Instantiate实例化的性能开销还体现在脚本代码的序列化上,如果脚本中序列化的信息很多,则Instantiate实例化时的时间亦会很长。最直接的例子就是NGUI,其代码中存在很多SerializedField标识,从而在实例化时带来了较多的代码序列化开销。因此,在打架增加序列化信息时,这一点是需要大家时刻关注的
以上是游戏项目中性能开销最大的三个模块,当然,游戏类型的不同、设计的不同,其他模块仍然会有较大的CPU占用。比如ARPG游戏中动画系统和物理系统,音乐休闲类游戏中的音频系统和例子系统等。
4 代码效率
逻辑代码在一个较为复杂的游戏项目中往往占据较大的CPU开销。这种情况在MOBA\ARPG\MMORPG等游戏类型中非常常见。
在项目优化过程中,我们经常会想知道,到底哪些函数占据了大量的CPU开销。同时,绝大多数的项目其中性能开销都遵循着“二八原则”,即80%的性能消耗都集中在20%的函数上。
答:
插件功能 |
插件名称 |
界面制作 |
NGUI |
2D游戏制作 |
2D Toolkit |
可视化编程 |
PlayMaker |
插值插件 |
iTween、HOTween |
路径搜寻 |
SimplePath |
美术及动画制作 |
RageSpline、Smooth Moves |
画面增强 |
Bitmap2Material、Strumpy Shader Editor |
摄像机管理 |
Security Camera |
资源包 |
Nature Pack |
造路插件 |
EasyRoads3D |
即时遮挡剔除/LOD多细节层次 |
Instant Occlusion Culling |
答:是指在显示器上为了显示出图像而经过的一系列必要操作。
渲染管道中的很多步骤,都要将几何物体从一个坐标系中变换到另一个坐标系中去。
主要有三步:应用程序阶段,几何阶段 光栅阶段
本地坐标->视图坐标->背面裁剪->光照->裁剪->投影->视图变换->光栅化。
答:计算机中显示的图形一般可以分为两大类——矢量图和位图。矢量图使用直线和曲线来描述图形,这些图形的元素是一些点、线、矩形、多边形、圆和弧线等等,它们都是通过数学公式计算获得的。例如一幅花的矢量图形实际上是由线段形成外框轮廓,由外框的颜色以及外框所封闭的颜色决定显示出的颜色。由于矢量图形可通过公式计算获得,所以矢量图形文件体积一般较小。矢量图形最大的优点是无论放大、缩小或旋转等不会失真;最大的缺点是难以表现色彩层次丰富的逼真图像效果。
答:矩阵:横轴排列的二维数据表格
矩阵运算:
加减 限制条件:行和列必须相同,对应相加相减得到结果
乘法 限制条件:要求左矩阵的行和右矩阵的列必须同,行数和左矩阵相等,列数和右矩阵相等,结果的第i行第j列,是左矩阵的第i行和右矩阵的第j列逐个相乘并把结果相加,得到结果是结果 的 第i行第j列。
答:角度和弧度
度和弧度之间的换算 1弧度 = 180度 /pi(约57.3)
1度=pi/180弧度(约0.017)
答:矢量有方向如力速度 标量只有大小没有方向如温度
矢量取模就是只要大小不要方向
单位向量 有方向 大小为1的向量
矢量的加法:是矢量的几何和,服从平行四边形规则
矢量满足交换律,满足结合律
在直角坐标系中,矢量等于骑在坐标轴上投影的矢量之和(二维矢量可以看做三维矢量的特例也就是说,三维成立,二维也成立)
矢量减法:
大小相等 方向相反 称为逆矢量。
任意多个矢量首尾相连接组成闭合多边形,其结果必为0
矢量的乘法:点积(内积、标量积)、叉积(外积)结果是矢量
点积方法 dot
注:这题要问什么?
答:
1.Resources
2.AssetBundle
3.WWW
答:实时光源会对性能有影响,解决方案有以下几种:
1使用unity的烘焙系统烘焙替代实时光源。
2使用三维建模软件的烘焙系统烘焙代替实时光源。
3一般移动端开发实时光源只为了提供实时阴影效果,所以设置实施光照的有效层。
答:
Time.timeScale = 0;即可让游戏暂停。
Time.timeScale = 1时,Update、LateUpdate、FixedUpdate 都按正常的时间来执行。
Time.timeScale = 2时,Update和 LateUpdate的执行速度是之前的2倍,而FixedUpdate还是按正常时间来执行
答:Prefab可以理解为是一个游戏对象及其组件的集台,作用是使游戏对象及资源能够被重复使用。相同的对象可以通过一个预设来创建,此过程可理解为实例化,预设可以进行批量修改。
答:NGUI 很好的解决了这一点,屏幕分辨率的自适应性,原理就是计算出屏幕的宽高比跟原来的预设的屏幕分辨率求出一个对比值,然后修改摄像机的 size 。
注:
答:
SceneManager.LoadScene//加载场景
SceneManager.LoadSceneAsync//异步加载场景
答:1、表面着色器(surface shaders)
通常情况下用户都会使用这种Shader,它可以与灯光、阴影、投影器进行交互。表面着色器的抽象层次比较高,它可以容易地以简洁方式实现复杂的着色器效果。表面着色器可同时正常工作在前向渲染及延迟渲染模式下。表面着色器以句Cg/HLsL语言进行编写。
2、顶点和片段着色器(Vertex and fragment Shaders)
如果需要一些表面着色器无法处理的酷炫效果,或者编写的Shader不需要与灯光进行交互,或是想要的只是全屏图像效果,那么可以使用顶点和片段着色器。这种Shader可以非常灵活地实现需要的效果,但是需要编写更多的代码,并且很难与Unity的渲染管线完美集成。顶点和片段着色器同样是用Cg/HLsL语言来编写。
3、固定功能管线着色器(Fixed Function Shaders)
如果游戏要运行在不支持可编程管线的老旧硬件上,那么需要编写这种Shader了。固定功能管线着色器可以作为片段或表面着色器的备用选择,这在当硬件无法运行那些酷炫Shader的时候,还可以通过固定功能管线着色器来绘制出一些基本的内容。固定功能管线着色器完全以ShaderLab语言编写,类似于微软的Effects或是Nvidia的CgFX。
答:UI纹理不需要强制使用2次幂,以NGUI举例,NGUI会制作图集,图集是2次幂的。
答:
碰撞器(Collider)有碰撞效果,IsTrigger=false,可以调用OnCollisionEnter/Stay/Exit函数
触发器(Trigger)没有碰撞效果,isTrigger=true,可以调用OnTriggerEnter/Stay/Exit函数
答:TCP是基于连接的,UDP基于无连接的
TCP对系统资源的要求多,UDP较少
UDP程序结构较简单
TCP是面向流数据的,UDP是数据报
TCP保证数据正确性,UDP可能丢包。
TCP保证数据的顺序,UDP不保证。
答:这是因为服务段的LISTEN状态下的SOCKET当收到SYN报文的建立请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你未必会马上关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发的。
答:这是因为虽然双反都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,案例可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要家乡网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LASK_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是来重发可能都是的ACK报文。
答:角色控制器中Move和SimpleMove函数都是用于实现角色游戏对象移动的函数,它们的区别在于当我们在每一帧调用SimpleMove函数时,引擎将为该角色控制器对象添加一个重力,而Move函数则不会添加重力,需要我们通过代码去实现重力的效果。
答:游戏引擎是指一些已经编写好的可编辑电脑游戏系统或者一些交互式实时图像应用程序的核心组件。这些系统为游戏设计者提供各种编写游戏所需要的各种工具,其目的在于让游戏设计者能容易和快速地做出游戏程序而不用由零开始。大部分都支持多种操作系统平台,如Linux、Mac OS、微软Windows。游戏引擎包含以下系统:渲染引擎(即“渲染器”,含二维图像引擎和三维图像引擎)、物理引擎、碰撞检测系统、音效、脚本引擎、电脑动画、人工智能、网络引擎以及场景管理。
1.Editor
Editor文件夹可以在根目录下,也可以在子目录里,只要名子叫Editor就可以。比如目录:/xxx/xxx/Editor 和 /Editor 是一样的,无论多少个叫Editor的文件夹都可以。Editor下面放的所有资源文件或者脚本文件都不会被打进发布包中,并且脚本也只能在编辑时使用。一般呢会把一些工具类的脚本放在这里,或者是一些编辑时用的DLL。 比如我们现在要做类似技能编辑器,那么编辑器的代码放在这里是再好不过了,因为实际运行时我们只需要编辑器生成的文件,而不需要编辑器的核心代码。
2.Editor Default Resources
Editor Default Resources注意中间是有空格的,它必须放在Project视图的根目录下,如果你想放在/xxx/xxx/Editor Default Resources 这样是不行的。你可以把编辑器用到的一些资源放在这里,比如图片、文本文件、等等。它和Editor文件夹一样都不会被打到最终发布包里,仅仅用于开发时使用。
3.Gizmos
我觉得这个文件夹其实没什么用处,如下代码所示它可以在Scene视图里给某个坐标绘制一个icon。它的好处是可以传一个Vecotor3 作为图片显示的位置。
4.Plugins
如果做手机游戏开发一般 andoird 或者 ios 要接一些sdk 可以把sdk依赖的库文件 放在这里,比如 .so .jar .a 文件。这样打完包以后就会自动把这些文件打在你的包中。
5.Resources
可以在根目录下,也可以在子目录里,只要名子叫Resources就可以。比如目录:/xxx/xxx/Resources 和 /Resources 是一样的,无论多少个叫Resources的文件夹都可以。Resources文件夹下的资源不管你用还是不用都会被打包进.apk或者.ipa
Resource.Load :编辑时和运行时都可以通过Resource.Load来直接读取。
Resources.LoadAssetAtPath() :它可以读取Assets目录下的任意文件夹下的资源,它可以在编辑时或者编辑器运行时用,它但是它不能在真机上用,它的路径是”Assets/xx/xx.xxx” 必须是这种路径,并且要带文件的后缀名。
AssetDatabase.LoadAssetAtPath():它可以读取Assets目录下的任意文件夹下的资源,它只能在编辑时用。它的路径是”Assets/xx/xx.xxx” 必须是这种路径,并且要带文件的后缀名。
我觉得在电脑上开发的时候尽量来用Resource.Load() 或者 Resources.LoadAssetAtPath() ,假如手机上选择一部分资源要打assetbundle,一部分资源Resource.Load().那么在做.apk或者.ipa的时候 现在都是用脚本来自动化打包,在打包之前 可以用AssetDatabase.MoveAsset()把已经打包成assetbundle的原始文件从Resources文件夹下移动出去在打包,这样打出来的运行包就不会包行多余的文件了。打完包以后再把移动出去的文件夹移动回来。
6. StreamingAssets
这个文件夹下的资源也会全都打包在.apk或者.ipa 它和Resources的区别是,Resources会压缩文件,但是它不会压缩原封不动的打包进去。并且它是一个只读的文件夹,就是程序运行时只能读不能写。它在各个平台下的路径是不同的,不过可以用Application.streamingAssetsPath 它会根据当前的平台选择对应的路径。
有些游戏为了让所有的资源全部使用assetbundle,会把一些初始的assetbundle放在StreamingAssets目录下,运行程序的时候在把这些assetbundle拷贝在Application.persistentDataPath目录下,如果这些assetbundle有更新的话,那么下载到新的assetbundle在把Application.persistentDataPath目录下原有的覆盖掉。
因为Application.persistentDataPath目录是应用程序的沙盒目录,所以打包之前是没有这个目录的,直到应用程序在手机上安装完毕才有这个目录。
StreamingAssets目录下的资源都是不压缩的,所以它比较大会占空间。
http://www.xuanyusong.com/archives/3229
标签:
原文地址:http://www.cnblogs.com/fastcam/p/5930446.html