Unity笔试面试题目分享


这几天在准备找工作,拿出了以前的一些资料,整理了一下分享出来看看,一是方便自己查漏补缺,二来其中的大部分问题是自己理解概括的,有大佬如果看到有错误的地方能够评论交流一下自然是最好的啦!希望早点找到一个工作可以不用在家躺尸了......

以下开始归纳题目——第一部分

1.点乘与叉乘,几何意义及公式

点乘:计算投影和两个向量之间的夹角 |a||b|cos<a,b>
叉乘:计算两个向量组成平面的法线方向,计算法线,可以计算出a向量与b向量组成的平行四边形的面积 |a||b|sin<a,b>

2.以下代码的意图是什么?有没有什么问题?应该怎么写?

for(int i=0;i<10;i++)
{
  if(list[i]==9)
  {
    list.RemoveAt(i);
  }
}

超出索引值,因为在移除指定下标的元素后会丢失元素,所以索引最多只能到8,而不能到9,并且在遍历的时候不应该删除元素。

3.数组,List,ArrayList的区别

数组:在内存中是连续存储的,有下标,访问迅速,便于赋值以及修改数据,但是需要预先向内存申请指定的空间。

ArrayList:在ArrayList中插入不同类型的数据是允许的,将所有数据都转化为Object来使用,可能出现不匹配的问题,类型是属于不安全的数据结构类型,而且装箱和拆箱操作会导致性能受到很大的损耗。
因此导致了List诞生,也因此衍生了泛型的概念。

List:可以使用泛型来动态的指定对象的类型,减少了类型转换和运行时出错的可能性,动态地根据加入或减少的元素数量进行空间的删减,存储不是连续的,移除某个元素之后会导致后面的元素移动到前面。

4.有一个需求,要求体力每5分钟增加1点,可以通过int GetTimeStamp()函数获取当前时间戳,如何实现该需求,要保存什么数据,或者应当用什么方式来刷新体力。

时间戳:从1970年1月1日00点00分00秒开始至今的总毫秒数。

方法:通过记录上一次保存的时间戳,使用当前的时间戳与上次保存时的时间戳相减,得到的时间除以5分钟换算而成的毫秒数,得到中途经过了多少个5分钟,再增加体力值。

PS:如果需求改为在每天的0点更新(增加2点),则将此时的时间戳减去上次更新的时间戳,并且除以86400秒,则可以得到距离上次更新的时间戳的间隔时间为多少天,多一天则增加2点。
PPS:如果改为在每天的凌晨5点更新,则应该将此时的时间戳以及上次的时间戳都移除去5小时*86400秒,整体偏移5个小时的差别,则可以得到应该更新的次数,并且作出更新。

5.举例说明对象池的作用,设计一个对象池,应该具有哪些重要字段以及方法

对象池的作用:对象池能够节省创建和初始化所耗费的时间,简化对象获取和使用的过程,对于那些被系统频繁请求和使用的对象,使用这种机制可以使性能达到很大提高,是以空间来换时间的操作。
在C#中为了避免GC机制,对象池可以较好地节省消耗的资源。

重要字段有:对象池所使用的对象,能用来判断当前对象是否在使用的flag标签
重要方法有:保存方法,对象池中所使用的保存当前对象的方法,取出对象并且使用对象的方法,都是需要有的。

6.LinkedList是什么?优缺点是什么?用途是什么?

LinkedList是链表,是一种可双向链表的数据结构,底层是用链表实现,由相互引用的节点组成的双向链表,插入数据到某个位置时,数据会形成一个新的节点,改变链表中的对应的两个节点的引用关系就可以完成
插入,删除操作同样可以使用引用修改来完成。
优点为:增删数据,修改数据非常方便,直接修改引用即可。
缺点为:遍历某个数据必须从头开始,直到遍历到那个数字为止,十分不方便。

7.private public protected internal四种修饰符分别是什么含义?

private:私有修饰符,仅在当前的类中可以使用调用。
public:公有修饰符,公开的数据类型,所有的类在引用时都可以访问到,是较为公开的数据类型
protected:保护修饰符,用于当前的类和继承的类都可以调用,可以自由使用,非继承或者外部类无法使用
internal:在同一个程序集中都可以使用的修饰符,修饰在当前程序集中可以使用的变量。

8.反射是什么?用途是什么?

反射提供了封装程序集、模块和类型的对象(Type类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。
如果代码中使用了特性,可以利用反射对它们进行访问。

用途:
1.需要访问程序元数据的特性。
2.检查和实例化程序集中的类型。
3.在运行时构建新类型。使用System.Reflection.Emit中的类。
4.执行后期绑定,访问在运行时创建的类型的方法。

9.ref关键字有什么用?和out关键字有什么区别?

ref可以使一个值成为引用,处理的数据需要有输入也需要有输出,能被大部分的值引用。且使用时需要显示使用ref关键字,ref关键字在使用前必须要先初始化。
out可以让参数通过引用来传递,与ref类似,但是在传递前可以不用初始化,若要使用,则必须显示使用out关键字。out适用于多个return返回值的地方,ref适用于在需要被调用的方法中修改调用者的引用。

10.举例Unity的四个特殊文件夹,分别是干什么用的?

StreamingAssets:文件夹中的文件目录结构和文件会被原封不动地打包进安装包中
Resources:需要动态加载的资源存放的位置
Editors:编辑器,特殊编辑工具存放的位置
Plugins:插件存放的位置

11.类与结构有什么区别?它们和堆,栈有什么联系?

类属于引用类型,类使用堆存储,空间较大,但是访问效率低,类是反应事物的抽象,类可以在声明时进行初始化。具备继承和多态的特点。
结构是值类型。结构使用栈来存储,栈的空间相对较小,但是效率较高。结构体是包含了具体不同类别数据的包装,不具备继承和多态,在声明时不能对结构进行初始化

12.Unity协程和C#线程的区别

Unity中的协程与多线程下的情况类似,有自己的堆栈,自己的局部变量,自己的指令指针,但是与其他协同程序共享全局变量等很多信息。线程,多线程是阻塞式的,每个IO必须开启新的线程,对于多个CPU可以使用Thread开启多线程。

主要不同在于:多处理器情况下,在概念上多线程同时运行多个线程,协程是通过协作来完成的,在任意一个指定时刻只有一个协同程序在工作,其他协程处于休眠状态,协程实际是在一个线程中,只不过每个协程
对CPU进行分时,协程可以访问和使用Unity的所有方法和组件。
同一时间只能执行某个协程,协程适合对某个任务进行分时处理。控制代码在特定的时间执行。协程不是线程,也不是异步执行,跟Update一样,在主线程中执行。不用考虑同步和锁的问题。
协程是一个分部组件,遇到条件(yield return)会挂起,直到条件满足才会被唤起执行后面的语句。

协程要关闭只能使用StopCoroutine函数,且函数的参数只能是字符串形式的输入,否则不会生效。

13.举例说明状态机在游戏中的使用,行为树和状态机的区别和联系

状态机在游戏中的使用,AI、动画系统、UI逻辑、玩家自动挂机。区别在于:状态机是将AI行为分为一个接一个的状态,状态与状态之间通过事件触发来形成,“事件触发型”的控制,行为树是一种流行的AI技术,
涵盖了层次状态机,事件调度,事件计划和行为等一系列技术,是属于高度模块化的状态,去掉了状态中的跳转逻辑,使得状态变成了一个行为。行为与行为之间的跳转主要是通过父节点的类型来进行决定的,可以
并行处理两个行为。同时可以增加控制的节点类型,达到复用行为的目的。

14.stack,Array,List,Queue,Hashset,Dictionary分别说明他们的特点,区别以及应用场景。

stack是栈,先进后出,后进先出的一种数据结构,属于特定的线性表,常用于实现递归方面的场景,还有撤销功能的实现。
Array是数组,是一种顺序存储结构,分配在连续内存中,不能随意扩展,数据类型一致,插入数据慢,占用空间较大,性能高,数据多性能不受到影响。
List是列表,内部采用数组进行实现,可以实现泛型,没有拆箱和装箱的风险,类型安全,但是不是线程安全,动态地根据加入和删除的数据进行空间的分配。
LinkedList是链表,内存中存储不一定是连续的,无法用下标访问,增加删除块,适用于经常增减节点的情况,查询较慢,需要通过遍历来从头挨个找。
HashTable是哈希集合,包含不重复项的无序列表,插入的数据快,类似不支持泛型的字典,存入取出数据时需要使用到哈希值来进行处理。
Dictionary是字典,可以实现泛型,增删改查非常快,键值对的形式来存储数据,可以用来代替实体,当只有ID和另一个属性时,可以大幅度地提升效率。
Queue是队列,先进先出,入队和出队两个操作,同样不是线程安全,特殊的线性表,只允许在表前端进行删除操作,表后端进行插入操作,队列中没有元素时,称为空队列。

15.使用两个stack来实现一个Queue

首先使用一个Stack来进行存储,称为s1然后将s1中的元素存入到s2中,然后循环放入数据时就可以进行操作了,在存储数据时将数据放入s1中,取出数据时将s2的数据取出,就可以实现一个先进先出的队列了。

16.GC是什么意思?在代码设计中为什么要考虑它?

GC是C#独特的垃圾回收机制,在运行.net应用程序时,程序创造出来的对象实例都是被CLR追踪的,CLR会判断在什么时候,内存中的这些对象没有被用到,没有被引用到,它就会去整理这些不再被用到的对象,
在恰当的时机销毁部分对象,释放出这些内存。但是由于销毁的操作非常耗费资源,因此在程序设计中需要尽可能地考虑减少GC的次数。

17.Unity中sharedmaterial和material有什么区别?

一个是可分享材质,另外一个是不共享材质,共享材质可以在程序运行时进行批处理,能够减少程序的消耗。

18.mipmap是什么?它如何使用?有什么优缺点?

mipmap是一种贴图处理方式,将贴图以2倍数缩小,直到1X1将缩小的图存储起来,在渲染时根据像素离眼睛的距离来判断从一个合适的图层中取出texel颜色赋值给像素,可以加速渲染的速度以及减少图像锯齿,
渲染的速度得到了提升,但是消耗了部分的空间来存储不同大小的贴图使用空间来换时间的一种方式。

19.描述AssetBundle的用法

部分特殊资源打包,单独打包,通过更新还有加载加入的方式来把资源加载到游戏中,节省了程序存储空间,控制了游戏包的大小,实现了游戏的热更新。可以把文件分为序列化文件和资源文件,
资源打碎放在对象中,最后统一写进一个单独的文件中,某些二进制资源被单独保存,方便快速加载。

20.Unity中热更新的原理及流程

热更新原理:表示在机器不停机的情况下对于系统进行更改,在游戏运行的过程中进行下载更新,不重启的情况下变更了资源,用户重启客户端就可以实现客户端资源代码的更新的需求或者功能。
热更新流程:资源打包为AssetBundle,并且打包的文件的MD5保存到文件中,更新版本号>>资源提交到资源服务器>>客户端启动,从服务器获得版本号V1,读取本地版本号V2,比较v1和v2,若相等则进入游戏,否则就从服务器下载md5列表,读取本地md5文件列表,对比文件,找到md5改变的文件>>下载md5改变的文件>>更新本地版本配置文件>>进入游戏

21.描述接口用途,接口和抽象类有什么相似和区别?

接口的用途:定义一种协议,任何实现当前接口的类都必须要遵循这个协议来完成,满足这个协议的要求。接口中的方法必须要被继承接口的类实现,这是一种约束。
相似:都可以被继承,都不能实例化,都可以包含方法声明,都要实现未实现的方法。
区别:抽象类可以有构造方法,普通成员变量,静态方法。接口都不能有以上三个,但接口可以多继承,一个类只能继承一个抽象类。

22.值类型和引用类型赋值的区别?

值类型数据存放在栈上,引用类型数据存放在堆上,地址存放在栈上。值类型声明后编译器为其分配内存,声明引用类时仅仅在栈中分配内存存放地址,并没有为其分配堆上的内存空间。
在赋值时,值类型变量会直接赋值给另一个变量,执行一次赋值。但是引用类型会复制引用对象的内存地址,在赋值后会有多个变量指向同一个引用对象实例。

23.面向对象,面向过程的特点,对比的优缺点分别是什么?

面向对象构成问题事务分解为各个对象,建立对象的目的,是为了描述事物在解决问题的步骤中的行为。
面向过程是解决问题的按部就班,分析问题的步骤,用函数一步步实现即可。

优缺点分别是:
面向过程:性能更高,但是开销更大,更消耗资源,例如单片机,嵌入式开发,linux和Unix,性能是较为重要的因素。
面向对象:容易维护,容易复用,容易扩展由于面向对象有封装继承多态的特性,可以设计出低耦合的系统,更灵活,容易进行维护。但是性能较低。

24.举例说明如何使用射线来检测实现碰撞?

AI的射线检测玩家,地面判定条件,碰撞盒检测等。

25.DrawCall是什么?如何降低DrawCall?

DrawCall是CPU调用图像编程接口,来命令GPU进行渲染的操作。
CPU和GPU通过一个命令缓冲区实现并进行工作,其包含一个命令队列,CPU在其中添加命令,GPU读取命令实现并行工作,添加和读取相对独立,使得可以独立工作。
如何降低DrawCall,使用批处理技术,尽量让资源模型公用材质,减少DrawCall的次数,通过将纹理打包成图集的方式来减少材质的使用,尽量减少使用反光材质,阴影以及透明材质,减少物体的多次渲染。使用图集,可以有效减少DrawCall,例如在图集中减少渲染的次数,减少尽可能的多的渲染的次数使用

26.已知三角形的三条边的长度,求三个角度分别是什么?

余弦定理表达式
cosa=(b*b+c*c-a*a)/2bc
cosb=(a*a+c*c-b*b)/2ac
cosc=(a*a+b*b-c*c)/2ab

27.生成一个指定长度的随机字符串,要求只包含大小写字母和数字?

定义一个字符串,内容为“abcdefghijkl.....1234567.....ABCDEF...”再根据指定长度来随机取出字符串中的字母和数字。

28.位运算有哪些?举例说明用途?

&常用于二进制取位操作,将任意数与1取&得到的结果,是判断整数的奇偶,最末尾为0则为偶数,为1为奇数, | 用于无条件赋值,任意数 |1就是把二进制的末尾数变为1,如果需要变为0则只需要把任意数|1后再减一即可,^可以用于比较大小,两次异或同一个数不变,可以用于简单加密,设定一个数为密钥,将要传递的数据与其进行异或,得到新数据,再异或一次,就可以得到原数据了

29.overload和override的相同点和区别点是什么?

重载函数名相同,但是参数名,返回值,参数类型不能相同,名字一样,静态多态。
重写在子类继承父类时,可以定义某方法与父类方法相同的名称和参数,子类在调用时自动调用子类的方法,父类的方法相当于被重写了

相同点:函数方法名相同。

区别点:
重写:方法名,参数,返回值相同,子类不能缩小父类的访问权限,子类不能抛出比父类多的异常,存在子类和父类之间,父类方法必须有virtual关键字。
重载:参数类型,个数,顺序至少有一个不相同,但是方法名相同,不能重载只有返回值不同的方法名。存在于父类,子类和同类中,virtual关键字可有可无。

30.C++头文件保护符的原理和作用?

头文件相当于文本替换,在定义头文件保护符时,因为其第一次被包含,所以没有宏定义过“A_H”因此经过定义
得到了宏“A_H”然后进入编译。如果第二次进入,判断是否定义过宏了,这样可以保证头文件不会被多次包含,只会包含一次,只要有一次就行了,否则编译都会报错。C++头文件保护符
的目的就是避免这类错误。

为了避免命名冲突,预处理器变量经常用全大写字母表示。预处理器变量有两种状态:一定义和未定义。定义预处理器变量和检测器状态所用的预处理器指示不同。

#define指示接受一个名字并定义该名字为预处理器变量。

#ifndef指示检测指定的预处理器变量是否未定义。如果预处理器变量未定义,那么跟在其后面的所有指示都被处理,直到出现#endif。

为了保证头文件在给定的源文件中只处理一次,我们首先检测#ifndef。第一次处理头文件时,测试会成功,因为相应的预处理器还未定义。下一条语句就定义了该预处理器变量。
那样的话,如果我们编译的文件敲好又一次包含了该头文件,#ifndef指示会发现该预处理器已经定义,并且忽略该头文件的剩余部分。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM