Unity笔试总结

alpha blend工作原理

Alpha Blend 实现透明效果,不过只能针对某块区域进行alpha操作,透明度可设。

写出光照计算中的diffuse的计算公式

diffuse = Kd x colorLight x max(N*L,0);Kd 漫反射系数、colorLight 光的颜色、N 单位法线向量、L 由点指向光源的单位向量、其中N与L点乘,如果结果小于等于0,则漫反射为0。

Vertex Shader是什么,怎么计算?

顶点着色器是一段执行在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几个过程。

简述delegate和event关键字

答:delegate 委托,是C#的一种类型,持有对某个方法的引用的类,能够拥有一个签名,引用只能与签名方法相匹配。实现:1、声明一个委托对象,与传递方法具有相同参数列表和返回值类型。2、创建委托对象,将要传递的函数作为参数传入。3、在实现异步调用地方,通过上一步创建对象调用方法。event 事件,在类中声明且生成,通过使用同一个类或其他类的委托与事件处理程序关联。包含事件的类用于发布事件,称为发布器(publisher)类;接受该事件的类称为订阅器(subscriber)类。事件使用发布-订阅模型。两者的区别:1、委托允许直接访问相应处理函数,事件只能通过公布的回调函数去调用。2、事件只能通过“+=”、“-=”方式注册和取消处理函数,委托除此之外还可以“=”直接赋值处理函数。

请列举Unity3D脚本生命周期的几个重要方法

答:Awake(){} 脚本唤醒,系统执行的第一个方法,用于脚本初始化,只执行一次。Start(){} 在Awake之后、Update之前执行,只执行一次。Update(){} 用于逻辑正常更新,每帧由系统自动调用一次。FixedUpdate(){} 固定更新。LateUpdate(){} 推迟更新,每帧调用,在Update之后调用。OnGUI(){} 绘制界面,每帧调用。OnDestroy(){} 当前脚本销毁时调用。

什么是AssetBundle?谈谈对AssetBundle内存分配情况的理解

答:可以把多个游戏对象或资源二进制文件封装到AssetBundle中,提供封装与解包的方法使用很方便。

加载资源三个步骤:
1、www/LoadFromFile/LoadFromMemory等接口加载AssetBundle本身
2、AssetBundle.LoadAsset()等接口从AssetBundle中加载资源
3、对于GameObject类资源,需要通过GameObject.Instantiate()创建Clone

黑色区域:www类本身占用内存,还保留了一份对WebStream数据的引用。使用www = null或www.dispose()释放。前者等待GC,后者立即释放。释放后WebStream引用计数会减一。

橙色区域:WebStream数据,数据真正的存储区域。AssetBundle被加载进来后,这部分内存就被分配了。包含三个内容:1、压缩后的AssetBundle本身。2、解压后的资源。3、一个解压缓冲区。www或AssetBundle对象都只是有一个结构指向了WebStream数据,从而对外部提供操作真正资源数据的方法。当WebStream数据引用为0时,系统会自动释放。为了不频繁的开辟和销毁解压Buffer,绿色Decompression解压缓冲区Unity会至少保留一份。

粉色区域:AssetBundle对象,引用WebStream数据部分,提供从WebStream数据中加载资源的接口。AssetBundle.Unload(bool unloadAllLoadedObjects)释放资源。AssetBundle.Unload(false)释放AssetBundle对象本身,可能引起WebStream释放,导致无法通过接口或依赖关系从该AssetBundle加载资源,但已加载资源可以正常使用。AssetBundle(true)不仅释放WebStream部分,所有被加载出来的资源将被释放。

红色部分:通过Instantiate()创建的GameObject所包含的资源。这些资源根据类型与AssetBundle原始资源(WebStream资源部分)有不同关系。如Texture、shader资源,通常只是使用,不会做出改动,所以仅仅是引用关系;每个GameObject是特殊的,所以是完全复制一份;Mesh和Material,则是引用+复制的关系。

什么是DrawCall,有什么方法可以减少DrawCall

答:CPU通过调用绘制命令来告诉GPU开始进行一个渲染过程(一次DrawCall)。
CPU方面减少DrawCall:
1、使用Draw Call Batching 、Dynamic Batching动态批处理
2、纹理打包成图集减少材质使用
3、少用反光、阴影
4、设置一个合适的Fixed Timestep
5、不要使用网格碰撞器(Mesh Collider)
6、大量或频繁的字符串连接操作一定要用StringBuilder
7、某些可能情况,使用结构体代替类
8、使用对象池重复利用空间
9、尽量不要用foreach,用for
10、不要直接访问GameObjcet的tag属性
11、不要频繁使用GetComponent,访问一次后保留其引用
12、使用OnBecameVisible()和OnBecameInVisible(),控制物体update()函数的执行减少开销
13、使用内建数组,如Vector3.zero而不是new Vector3(0,0,0)
14、使用ref关键字对方法的参数进行优化
15、关闭所有update中的log操作
16、不在update中调用GetComponent、SendMessage、FindWithTag等方法
17、不在update中使用临时变量

GPU方面减少DrawCall:

1、使用纹理图集代替一系列单独小贴图
2、保持材质数目尽可能少
3、如果使用纹理图集和共享材质,用Renderer.sharedMaterial代替Renderer.material
4、使用光照纹理(lightmap)而非实时灯光
5、使用LOD
6、使用mobile版的shader
7、尽可能减少顶点数、背面删减
8、压缩图片,减少显存带宽压力

谈谈你对u3d渲染管线的理解

答:渲染管线(Rendering Pipeline)就是GPU渲染。流程:顶点处理、面处理、光栅化、像素处理。顶点处理:通过一系列坐标系转换将模型顶点在摄像机前位移,最终投影到摄像机屏幕上(本地 - 世界 - 观察 - 投影)。包括顶点坐标变化、逐顶点雾化、材质属性和光照属性处理。面处理:面的组装、截取、剔除。光栅化:消除遮挡面;Texture Operation,根据像素纹理坐标查询对应纹理值;Blending,根据已经画好颜色与正在计算的颜色alpha值混合形成新颜色;Filtering,将正在计算的颜色经过某种滤镜后输出,该阶段后像素的颜色值被写入帧缓存中。像素处理:对每个像素区域着色,对像素贴上贴图形成最终画面。

什么是material,什么是shader,二者有什么关系

答:材质系统定义了如何渲染物件表面信息。shader里面使用材质信息加自身操作,最终呈现物体渲染。shader是material一部分,是根据计算即时演算生成贴图的程序,叫着色器。常用处理无法用固定贴图表现的模型。material是模型的材质,包含贴图、shader、顶点、凹凸等信息。

什么是协同程序?

协程的作用类似多线程,但其本质是单线程的。Unity的协程实在每帧结束之后去检测yield的条件是否满足。(普通的程序会执行到return再返回去执行主线程,而协同程序可以和主线程同时运行,是异步的)。

简述数组、ArrayList和List的主要区别?

数组:优点:通过数组下标查找,索引快;缺点:数组分配在一块连续的数据空间上,因此分配空间时必须确定大小,插入和删除元素的效率低。
ArrayList:优点:解决了数组的缺点,1、不用要指定空间大小,2、插入和删除方便。缺点:1、不安全性,可以插入不同类似的数据(把所有数据都当成object处理),所以我们在使用的时候可能报类型不匹配的错;2、性能差,既使我们保证在插入数据的时候都很小心,都有插入了同一类型的数据,但在使用的时候,我们也需要将它们转化为对应的原类型来处理。这就存在了装箱与拆箱的操作,会带来很大的性能损耗。
List:为了解决ArrayList的缺点,C#2.0后引入了List,在声明List的时候,我们需要声明数据的类型(List),这样子就避免了类型安全问题和装箱拆箱的性能问题。
扩展:装箱与拆箱。装箱就是将值类型转换成 object 或者接口类型的一个过程;拆箱就是从对象中提取对应的值类型的一个过程。
扩展:值类型与引用类型。byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。值类型变量声明后,不管是否已经赋值,编译器为其分配内存;string 和 class统称为引用类型。当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中

简述一下对象池,你觉得在FPS里哪些东西适合使用对象池

对象池可以说时一个集合,用来存放是要反复使用到的对象,例如:敌人、子弹等。因为创建和销毁对象是很耗性能的。当需要用到某一对象资源时便到池子里面去查找,找到这一对象便直接拿出来使用,没有再重新创建。

请简述sealed关键字用在类声明时与函数声明时的作用

sealed修饰的类为密封类,类声明时可防止其他类继承此类,在方法中声明则可防止派生类重写此方法。

请简述private,public,protected,internal的区别

private:只对该类公开
protected:对该类及其派生类公开
public:对所有类公开
internal:只能在包含该类的程序集中使用该类

LOD是什么,优缺点是什么?

LOD(Level of detail)多层次细节,是最常用的游戏优化技术。它按照模型的位置和重要程度决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。缺点是增加了内存。
用法见https://jingyan.baidu.com/article/495ba841b56ee738b30ede81.html

MipMap是什么,作用?

类似LOD,只不过时应用在纹理贴图,当摄像机离得远则贴图模糊,近则贴图清晰。
用法见https://blog.csdn.net/yu__jiaoshou/article/details/51965108

C#、.Net与Mono的关系?

mono是.net的一个开源跨平台工具,就类似java虚拟机,java本身不是跨平台语言,但运行在虚拟机上就能够实现了跨平台。.net只能在windows下运行,mono可以实现跨平台跑,可以运行于linux,Unix,Mac OS等。C#是基于mono的.Net

什么是LightMap?

LightMap:就是指在三维软件里实现打好光,然后渲染把场景各表面的光照输出到贴图上,最后又通过引擎贴到场景上,这样就使物体有了光照的感觉。

ref和out

在C语言中,按值传递的参数在方法内部不管怎么改变,方法外的变量都不会受到影响。那么在C语言里想要写一个Swap方法该怎么做?用指针咯。那么在C#里该怎么做?虽然也可以用指针,但是更通常也更安全的做法就是用ref。
out需要在函数内部return之前必须要赋上一个值
out在参数传入之前可以不用初始化(因为它肯定在return之前会被赋值)
ref需要在函数内部return之前可以不用赋值
ref在参数传入之前一定要初始化
总结:ref可进可出,out只出不进

C#和C++的区别?

C# 是一种完全面向对象的语言。另外C# 是基于IL 中间语言和.NET Framework CLR 的,在可移植性,可维护性和强壮性都比C++ 有很大的改进。

GC(垃圾回收)

什么是托管资源,非托管资源:托管资源是由CLR全权负责的资源,CLR不负责的资源为非托管资源。
对于托管资源通过GC自动清理回收。对于非托管资源,通过代码调用手动进行清除,再由GC回收。
如何正确的释放资源:对于非托管的资源,一般就是,不受CLR管理的对象,windows内核对象,如文件、数据库连接、套接字、COM对象等
如何清理非托管资源:Dispose方法用于清理对象封装的非托管资源,而不是释放对象的内存,对象的内存依然由垃圾回收器控制。
调用Dispose方法,销毁对象,需要显示调用或者通过using语句,在显示调用或者离开using程序块时被调用。
Unity 内部有两个内存管理池: 堆内存和堆栈内存。堆栈上的内存分配和回收十分快捷简单, 因为堆栈上只会存储短暂的或者较小的变量. 内存分配和回收都会以一种顺序和大小可控制的形式进行。堆内存上的内存分配和存储相对而言更加复杂, 主要是堆内存上可以存储短期较小的数据, 也可以存储各种类型和大小的数据. 其上的内存分配和回收顺序并不可控, 可能会要求分配不同大小的内存单元来存储数据。所以垃圾回收主要是指堆上的内存分配和回收, Unity 中会定时对堆内存进行 GC 操作.
GC 操作是一个极其耗费时间的操作, 堆内存上的变量或者引用越多则其运行的操作会更多, 耗费的时间越长.
GC引起的性能问题:帧率下降 —— 在性能瓶颈时期触发GC。性能时好时坏。断断续续的出现卡顿 —— 每次GC的到来, 会导致系统停止所有进程, 并造成大量的CPU 开销, 进而降低游戏运行的流畅度。

如何降低GC带来的影响:减少GC运行所需的时间 —— 减少游戏中的堆内存分配和对象引用数目。减少GC频率 —— 降低对内存的分配和释放频率。在非性能瓶颈时期主动触发GC —— 尝试测算GC和堆空间扩张的时间来使其在可预测, 适宜的时间发生

如何减少垃圾的产生

使用缓存&不要在频繁调用的方法中分配堆内存. 如果在需要频繁调用的方法中进行了堆内存分配, 并且最后舍弃了这个新建的变量, 这将会产生不必要的垃圾, 应该考虑在方法外创建对该变量的引用, 然后重复利用它. 对于无法缓存的对象, 应该尽量降低方法的执行次数, 仅在需要时才去执行它.
使用清空集合替代新建集合. 创建新的集合对象将会在堆空间分配内存. 如果代码中多次创建新的集合, 应该缓存对集合的引用, 在下次需要使用新的集合时, 将缓存的集合清空 ( Clear() ) 而不是建立全新的集合.
使用对象池. 如果游戏中需要频繁的创建再销毁某类对象, 那么为该类对象建立对象池.
参考https://blog.csdn.net/jingangxin666/article/details/82564238

参考https://blog.csdn.net/pdw_jsp/article/details/68946743

如何优化内存?

有很多种方式,例如

1.压缩自带类库;
2.将暂时不用的以后还需要使用的物体隐藏起来而不是直接Destroy掉;
3.释放AssetBundle占用的资源;
4.降低模型的片面数,降低模型的骨骼数量,降低贴图的大小;
5.使用光照贴图,使用多层次细节(LOD),使用着色器(Shader),使用预设(Prefab)。
6.代码中少产生临时变量
下列代码在运行中会产生几个临时对象?
string b = new string(new char[]{‘a’,’b’,’c’});
答案5个

简述四元数的作用,四元数对欧拉角的优点?

四元数用于表示旋转
相对欧拉角的优点:避免万向锁

什么是渲染管道?

是指在显示器上为了显示出图像而经过的一系列必要操作。 渲染管道中的很多步骤,都要将几何物体从一个坐标系中变换到另一个坐标系中去。主要步骤有:
本地坐标->视图坐标->背面裁剪->光照->裁剪->投影->视图变换->光栅化

Unity3D中可不可以使用多线程?

可以,如果同时你要处理很多事情或者与Unity的对象互动小可以用thread,否则使用coroutine。因为unity仅能从主线程中访问unity组件、对象、方法。

矩阵相乘的意义

用于表示线性变换:旋转、缩放、投影、平移、仿射

静态批处理和动态批处理(Batching

如果动态物体共用着相同的材质,那么Unity会自动对这些物体进行批处理。动态批处理操作是自动完成的,并不需要你进行额外的操作。
区别:动态批处理一切都是自动的,不需要做任何操作,而且物体是可以移动的,但是限制很多。静态批处理:自由度很高,限制很少,缺点可能会占用更多的内存,而且经过静态批处理后的所有物体都不可以再移动了。

简述StringBuilder和String的区别?

String是字符串常量。string s1=“test”;实际上是创建了一个名字为s1的string类型对象,系统为s1创建足够的内存来存储它,s1+=“ceshi”,并不是在s1所指向的地址内添加字符串“cehsi”,而是新建了一个“testceshi”字符串并将s1指向这个新的字符串,系统将原来的“test”注销掉,如果频繁的进行字符串的修改会严重影响系统性能。
StringBuilder则不会重新new一个StringBuilder对象来储存新的值,频繁修改时请选它。
C#中没有StringBuffer

unity3D Shader分哪几种,有什么区别?

表面着色器的抽象层次比较高,它可以轻松地以简洁方式实现复杂着色。表面着色器可同时在前向渲染及延迟渲染模式下正常工作。
顶点片段着色器可以非常灵活地实现需要的效果,但是需要编写更多的代码,并且很难与Unity的渲染管线完美集成。
固定功能管线着色器可以作为前两种着色器的备用选择,当硬件无法运行那些酷炫Shader的时,还可以通过固定功能管线着色器来绘制出一些基本的内容。

实现strcpy函数

已知strcpy函数的原型是:char * strcpy(char * strDest,const char * strSrc);;1.不调用库函数,实现strcpy函数。2.解释为什么要返回char *。
char *strcpy(char *strDest, const char *strSrc);

{

  assert((strDest!=NULL) && (strSrc !=NULL)); // 2分

  char *address = strDest; // 2分

  while( (*strDest++ = * strSrc++) != ‘\0’ ) // 2分

      NULL ;

  return address ; // 2分

}
返回char* 是为了实现链式表达式

值类型和引用类型有何区别?

1.值类型的数据存储在内存的栈中;引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的地址。
2.值类型存取速度快,引用类型存取速度慢。
3.值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用
4.值类型继承自System.ValueType,引用类型继承自System.Object
5.值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。
指针传递和引用传递:都会修改到实参本身,引用不是一个对象。
参考https://www.cnblogs.com/yinzm/p/6510938.html

Heap与Stack有何区别?

1.heap是堆,stack是栈。
2.stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的,heap常用new关键字来分配。
3.stack空间有限,heap的空间是很大的自由区。
请写出求斐波那契数列任意一位的值得算法
斐波那契数列:从第3项开始,每一项都等于前两项之和。例如:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,…
递归实现:
int Fib1(int index)
{
if(index<1)
{
return -1;
}
if(index1|| index2)
{
return 1;
}
return Fib1(index-1)+Fib1(index-2);
}

客户端与服务器交互方式有几种?

socket通常也称作”套接字”,实现服务器和客户端之间的物理连接,并进行数据传输,主要有UDP和TCP两个协议。Socket处于网络协议的传输层。
http协议传输的主要有http协议 和基于http协议的Soap协议(web service),常见的方式是 http 的post 和get 请求,web 服务。
http工作原理:

1、由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的TCP连接。 连接
2、HTTP服务器则在那个端口监听客户端发送过来的请求。一旦收到请求, 请求
3、服务器(向客户端)发回一个状态行,比如"HTTP/1.1 200 OK",和(响应的)消息,消息的消息体可能是请求的文 件、错误消息、或者其它一些信息。响应
4、客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客 户机与服务器断开连接 显示内容
Game视图右上角点击Stats
参考:https://www.cnblogs.com/zhaoqingqing/p/6288154.html
有A和B两组物体,有什么办法能够保证A组物体永远比B组物体先渲染?
把A组物体的渲染对列大于B物体的渲染队列,通过shader里面的渲染队列来渲染
参考文章https://blog.csdn.net/qq_34937637/article/details/79653525

作者:cysAAAAA
来源:CSDN
原文:https://blog.csdn.net/qq_36738011/article/details/90728365
版权声明:本文为博主原创文章,转载请附上博文链接!

你可能感兴趣的:(Unity笔试总结)