Unity开发工程师——笔试题终极版
前言之前分享了一篇文章
这篇文章里面只提到问题,但却没有给出答案,因为涉及到的内容也不太难,所以希望大家自己找到答案。
这次我打算做一个终极版的笔试题汇总(持续更新),并给出我在网上找到的答案或者我自己的理解。
这些笔试题我打算分为以下几个类型
随着收集题目变得越来越多,大家想要去了解对应的笔试题类型就用ctrl+f快速定位到相应位置即可。
以下就是我收集到的题目与答案,如有不对的地方希望大家指出来。
C#基础篇
请简述拆箱和装箱
答:
装箱操作:
值类型隐式转换为object类型或由此值类型实现的任何接口类型的过程。
1.在堆中开辟内存空间。
2.将值类型的数据复制到堆中。
3.返回堆中新分配对象的地址。
拆箱操作:
object类型显示转换为值类型或从接口类型到实现该接口值类型的过程。
1.判断给定类型是否是装箱时的类型。
2.返回已装箱实例中属于原值类型字段的地址。
C#中,string str = null 与 string str = "",说明区别。
答:
string str = "" 初始化对象分配空间。
string str = null 表示一个空引用,没有占用空间。
ref与out关键字
答:
ref 关键字使参数按引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。若要使用 ref 参数,则方法定义和调用方法都必须显式使用 ref 关键字。
out 关键字会导致参数通过引用来传递。这与 ref 关键字类似,不同之处在于 ref 要求变量必须在传递之前进行初始化。若要使用 out 参数,方法定义和调用方法都必须显式使用 out 关键字。
什么是序列化?
答:
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
sealed 修饰符有什么特点
答:
1) sealed 修饰符可以应用于类、实例方法和属性。密封类不能被继承。密封方法会重写基类中的方法,但其本身不能在任何派生类中进一步重写。当应用于方法或属性时,sealed 修饰符必须始终与 override一起使用。
2) 将密封类用作基类或将 abstract 修饰符与密封类一起使用是错误的。
3) 结构是隐式密封的;因此它们不能被继承。
class和struct的异同
答:
相同点:
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) 大多数情况下该类型只是一些数据时,结构时最佳的选择。
抽象类(abstract class)和接口(interface)的区别。
答:
抽象类:
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) 如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法。
什么是强类型。
答:
为所有变量指定数据类型称为“强类型”。C#是强类型语言。
什么是托管代码。
答:
使用基于公共语言运行库的语言编译器开发的代码称为托管代码;托管代码具有许多优点,例如:跨语言集成、跨语言异常处理、增强的安全性、版本控制和部署支持、简化的组件交互模型、调试和分析服务等。
什么是CLR?
答:
CLR:公共语言运行库 Common Language Runtime。是一个运行时环境,它负责资源管理(内存分配和垃圾收集),并保证应用和底层操作系统之间必要的分离。
什么是委托?
答:
1) 委托是一种引用方法的类型。
2) 委托类似于 C++ 函数指针,但它是类型安全的。
3) 委托允许将方法作为参数进行传递。
4) 委托可用于定义回调方法。
值类型和引用类型的区别。
答:
1) 值类型通常被分配在栈上,它的变量直包含变量的实例,使用效率比较高。
2) 引用类型分配在托管堆上,引用类型的变量通常包含一个指向实例的指针,变量通过该指针来引用实例。
3) 一个是值COPY,一个是地址COPY。
进程和线程的区别。
答:
1) 进程是系统进行资源分配和调度的单位。
2) 线程是CPU调度和分派的单位。
3) 一个进程可以有多个线程,这些线程共享这个进程的资源。
造器Constructor是否可被override(重写)?
答:构造器Constructor不能被继承,因此不能重写override,但可以被重载Overloade。
堆和栈的区别。
答:
1) 栈:由编译器自动分配、释放。在函数体中定义的变量通常在栈上。存放值类型。
2) 堆:一般由程序员分配释放。用new等分配内存函数分配得到的就是在堆上。存放引用类型。
在C#中using和new这两个关键字有什么意义。
答:
using 关键字有两个主要用途:
1) 作为指令,用于为命名空间创建别名或导入其他命名空间中定义的类型。
2) 作为语句,用于定义一个范围,在此范围的末尾将释放对象。
new 关键字:新建实例或者隐藏父类方法
System.String 和System.StringBuilder有什么区别?
答:
1) System.String是不可变的字符串。
2) System.StringBuilder存放了一个可变的字符串,并提供一些对这个字符串修改的方法。
3) String类在执行字符串拼接的操作上,用“+”会产生新的对象,占用内存。
4) StringBuilder类只是修改字符串的内容,不建立新的对象。
const和readonly有什么区别?
答:
1) const 字段只能在该字段的声明中初始化。
2) 不允许在常数声明中使用 static 修饰符。
3) readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数
C#中的委托是什么?事件是不是一种委托?
答:
委托可以把一个方法作为参数代入另一个方法。
委托可以理解为指向一个函数的引用。
事件是一种特殊的委托。
能用foreach遍历访问的对象需要实现什么接口或声明什么方法的类型。
答:声明IEnumerable接口或实现GetEnumerator()方法。
接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)?
答:接口可以继承接口。抽象类可以实现(implements)接口,抽象类是否可继承实体类,但前提是实体类必须有明确的构造函数。
什么是虚函数?什么是抽象函数?
答:
1) 虚函数:没有实现的,可由子类继承并重写的函数。
2) 抽象函数:规定其非虚子类必须实现的函数,必须被重写。
请简述ArrayList和List<>的主要区别
答:
ArrayList是非泛型列表,存储数据是把所有的数据都当成object类型数据,存在装箱问题,取出来使用的时候存在拆箱问题,装箱拆箱会使性能变差,而且存在数据安全问题,但是优点在于可以让值类型和引用类型相互转换。
List是泛型列表,在使用的时候才会去定义数据类型,泛型避免了拆装箱的问题,存入读取熟读较快,类型也更安全。
反射的实现原理?
答:
审查元数据并收集关於它的类型信息的能力,元数据(编辑后的基本数据单元)就是一大堆表,编译器会创建一个类定义表,一个字段定义表,一个方法定义表等,System.Reflection命名空间包含的几个类,允许你反射(解析)这些元数据的代码。Unity基础篇
一个角色要用到unity中寻路系统,应该添加哪个组件?
答:NavMeshAgent。
NavMeshObstacle组件的作用?
答:寻路网格动态碰撞组件,用于运动的物体阻碍寻路物体效果。
射线中RaycastHit代表什么?
答:射线碰到的碰撞信息
通过什么可以区分射线碰到的游戏对象?
答:通过LayerMask(层的遮罩),RaycastHit返回的碰撞的标签。
怎么判断两个平面是否相交?不能用碰撞体,说出计算方法。
答:在两个平面上分别取一个向量,然后看是否相交。
MeshCollider和其他Collider的一个主要不同点?
答:
MeshCollider是网格碰撞器,对于复杂网状模型上的碰撞检测,比其他的碰撞检测精确的多,但是相对其他的碰撞检测计算也增多了,所以一般使用网格碰撞也不会在面数比较高的模型上添加,而会做出两个模型,一个超简模能表示物体的形状用于做碰撞检测,一个用于显示。
Unity3d中的碰撞器和触发器的区别?
答:
1. 碰撞器物体不能互相进入到对方内部,触发器可以。
2. 触发器角色控制器可以使用,碰撞器中不能使用。
3. 触发器没有物理属性了,碰撞器可以有力存在。
4. 碰撞器调用OnCollisionEnter/Stay/Exit函数,触发器调用OnTriggerEnter/Stay/Exit函数。
物体发生碰撞的必要条件
答:两个物体都必须带有碰撞器(Collider),其中一个物体还必须带有Rigidbody刚体。
当一个细小的高速物体撞向另一个较大的物体时,会出现什么情况?如何避免?
答:穿透(碰撞检测失败)。
避免的方法:把刚体的实时碰撞检测打开Collision Detection修改为Continuous Dynamic数据结构与算法篇
请使用冒泡排序,对一维数组进行降序排列
答:
冒泡排序:它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换。
冒泡排序算法的运作如下:
比较相邻元素。如果第一个比第二个大,就交换它们。
对每一对相邻元素做同样的工作,从开始第一队到结尾的最后一对。在这一点,最后的元素会使最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,知道没有任何一对数字需要比较。写法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 > a)
{
tmp = a;
a = a;
a = 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<a)
{
tmp=a;
a=a;
a=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<a)
{
temp=a;
a=a;
a=temp;
b=true;
}
}
j++;
}
while(b);
}
说出你所了解的数据结构,并说明它们的特点。
答:
数组:长度固定,存储空间连续,读取速度快,插入,删除速度慢
链表:长度不定,存储空间可能不连续,读取速度慢,插入,删除速度快
栈:后进先出
队列:先进先出
字典:键/值对集合,无序,一一对应,查找速度快
树:可形成无限级层次,父子节点。面向对象篇
请描述你所了解的设计模式,并说明在你的项目中哪里使用过?
答:
单例: 对象池,游戏管理器
抽象工厂,
状态:有限状态机,
桥接:有限状态机
策略:AI自动行为操控中每种操控算法的独例
请说出4中面向对象的设计原则,并分别简述它们的含义。
答:
1)单一职责原则:一个类,最好只做一件事,只有一个引起它的变化。
2)开放-封闭原则:对于扩展是开放的,对于更改是封闭的。
3)里氏替换原则:子类必须能够替换其基类。
4)依赖倒置原则:设计应该依赖于抽象而不是具体实现。
5)接口隔离原则:使用多个小的专门的接口而不要使用一个大的总接口。网络篇
请简述TCP与UDP的区别。
答:
TCP是基于连接的,UDP基于无连接的
TCP对系统资源的要求多,UDP较少
UDP程序结构较简单
TCP是面向流数据的,UDP是数据报
TCP保证数据正确性,UDP可能丢包。
TCP保证数据的顺序,UDP不保证。
UDP连接和TCP连接的异同。
答:
1) TCP(Transmission Control Protocol)传输控制协议:一种面向连接的、可靠的、基于字节流的运输层通信协议,三次握手。
2) UDP(User Datagram Protocol)用户数据报协议:它不属于连接型协议,因而具有资源消耗小,处理速度快的优点。缺点是易丢失数据包。
为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
答:
这是因为服务段的LISTEN状态下的SOCKET当收到SYN报文的建立请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你未必会马上关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发的。3D数学篇
四元数是什么?主要作用什么?对欧拉角的优点是什么?
答:
所谓四元数,就是把4个实数组合起来的东西。4个元素中,一个是实部,其余3个是虚部
作用:四元数用于表示旋转
优点:
1)能进行增量旋转
2)避免万向锁
3)给定方位的表达方式有两种,互为负(欧拉角有无数种表达方式)
四元数不会有欧拉角存在的 gimbal lock 问题[万向节死锁]
四元数由4个数组成,旋转矩阵需要9个数
两个四元数之间更容易插值
四元数、矩阵在多次运算后会积攒误差,需要分别对其做规范化(normalize)和正交化 (orthogonalize),对四元数规范化更容易
与旋转矩阵类似,两个四元组相乘可表示两次旋转
向量的点乘、叉乘以及归一化的意义?
答:
1)点乘计算两个向量之间的夹角,还可表示某一方向的投影。
2)叉乘得到的是法向量。
3)标准化向量:用在只关系方向,不关心大小的时候。Shader篇
写出光照计算中的diffuse的计算公式
答:
实际光照强度l=环境光(lambient)+漫反射光(Idiffuse)+镜面高光(lspecular)
环境光:lambient=环境光强度(Aintensity)*环境光颜色(Acolor)
漫反射光:ldiffuse=镜面光照强度(Dintensity)*镜面光颜色(Scolor)*(光的反射向量(R).观察者向量(V))^镜面光指数(n)
MeshRender中material和shader的区别?
答:
MeshRender是模型渲染的组件,有此组件物体才能显示出来
Material是材质球,实际就是shader的实例,并进行赋值,贴图、纹理、颜色等。
Shader是着色器,实际上是一段程序,还可以用来实现一些仅靠贴图不容易实现的效果,如玻璃。
Shader大致分为:
1.表面着色器
2.顶点和片元着色器
3.固定功能着色器
alpha blend工作原理
答:
Alpha Blend是 实现透明效果,Color = 原颜色*alpha/255+目标色*(255-alpha)/255
光照贴图 的优势是什么?
答:
1.使用光照贴图比使用实时光源渲染要快
2.可以降低游戏内存消耗
3.多个物体可以使用同一张光照贴图 上机测试题篇
页:
[1]