[Unity面试] 2021年Unity面试题分享(面试题Lua突破3.8已更新)

Posted 小听歌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Unity面试] 2021年Unity面试题分享(面试题Lua突破3.8已更新)相关的知识,希望对你有一定的参考价值。

【CSND】Unity游戏开发学习群
加入我们一起学习吧:1022366216

备注来源:csdn~~ 阿里嘎多

联系小小老师~可以获取学习游戏开发大礼包!
群内还有更多素材资源领取!!

【重点面试题】代表面试的时候问到的题目
光背答案是没有用的,一定要动手操作一下,才能知道答案为什么是这个。
最新的大厂面试问题,在页面最下面更新了

本文章力求从简单的形式到深入理解原理,再到扩展,丰富知识的层次感,知其所以然。

推荐学习资料
链接1: 游戏编程模式.
链接2: 设计模式.

2021年Unity面试题分享


一、C#语言和面向对象OOP(已更新2021.3.8)

1、【重点面试题】面向对象的三大特性
封装 :隐藏对象的属性,并实现细节(方法),对外提供接口,
public全局,protected子类,internal同集,隐藏private
同类,public属性器,private字段,对赋值进行限定。
sealed修饰符的子类是不能被继承的。

		设计上:分而治之,封装变化、高内聚低耦合
		数据上:把一些基本数据复合成一个自定义类型的数据
		方法上:隐藏实现细节,向外提供接口

继承:重用现有代码
多态:静态多态重载,动态多态重写。
父类行为由子类具体实现,包含virtual虚方法,abstract抽象方法,interface接口
可以涉及题目虚方法、抽象方法、接口的区别

重载和重写区别
静态重载:返回值无关,与参数个数,类型相关。编译阶段。便于扩展和维护
动态重写override(运行时,改写了方法表的新地址)。
抽象方法的父类必须是抽象类,子类是抽象类可以不重写,抽象类不可以被实例化。

举例子非常重要
基类抽象人物工厂(接口),子类具体玩家工厂,子类怪物工厂,子类npc工厂,抽象产品(接口)具体魔法师,弓箭手产品~
资源管理工厂,UI资源管理工厂,音频资源管理工厂,资源管理,UI资源管理,音频资源


2、【重点面试题】值类型和引用类型区别
值类型:包含了所有简单类型(整数、浮点、bool、char)、struct、enum。
继承自System.ValueTyoe
引用类型包含了string,object,class,interface,delegate,array
继承自System.Object

内存区域上的区别
值类型:数据存储在栈上,超出作用域就自动清理
引用类型:数据存储在托管堆上,引用地址在线程栈上,地址指向数据存放的堆上
托管堆会由GC来自动释放 ,线程栈数据在作用域结束后会被清理。

拷贝策略:值类型是拷贝数据,引用类型是拷贝引用地址
如果值类型为传值参数,传值参数会在栈上新开辟一个副本,原先的值类型数据不会改变
如果引用类型是传值参数,传值参数会创建一个新的引用地址,两个引用地址会指向同一个对象实例的数据,实例数据会随着改变进行改变。(这种行为被称为副作用,一般实际项目不会这么操作,要么return返回参数,要么使用ref或者out修饰符)
【扩展Ref引用参数,Out输出参数可以利用这一副作用机制】

通常来讲 变量的值分配 与其声明该变量的位置有关。 局部变量的值 总是在 栈上的。 实例变量的值则和实例本身一起储存在实例储存的地方。 引用类型实例和静态总是储存在 堆上的。
数组的元素、引用类型中的值类型字段等,引用类型的确总是分配在托管堆上, 但是值类型并非总是分配在线程栈上有可能分配在堆上。

值类型和引用类型互相转换:拆箱和装箱
装箱:值类型====》引用类型object
1.分配内存堆
2.值类型数据拷贝到新的内存堆中
3.栈中分配一个新的引用地址指向内存堆
拆箱:引用类型object====》值类型
1.检查确保对象是给定值类型的一个装箱值
2.将该值数据复制到栈中的值类型

string是特殊的引用类型,如果传入参数是string,在方法里修改,原string数值不变。
原因是string的不变性,系统内部做了特殊处理。
链接: B站刘铁猛C#入门精要.


【重点面试题】3、装箱和拆箱的区别
值类型和引用类型的最终基类是Object
装箱:值类型转换成引用类型的过程,生成新的引用
拆箱;引用类型转换成值类型的过程

装箱操作:托管堆分配内存,值类型拷贝数据,object地址指向托管堆对象
拆箱操作:根据object引用地址找到托管堆上的数据,栈上数据拷贝
避免装箱操作,生成新的应用,解决办法就是第一是重载,第二是泛型
链接: 参考资料.

4、public、private、protected、internal、sealed的区别
public全局、private类内部、protected派生类、internal本程序集
sealed声明类就不能继承,声明方法就是不能被重写


【重点面试题】6、什么是接口,描述一下接口的成员具体实现(手撸代码和注意修饰符)
接口interface,不能定义字段,可以定义【非静态的】属性、索引器、事件、方法
默认public,但不能写任何访问修饰符
接口是引用类型,可以通过as运算符强转,获取某对象的接口的引用
接口可以继承N个接口,继承类要实现所有接口的方法

声明接口IA > 继承接口的类B > 类B实现接口所有方法
接口要小而精,定义一组方法,继承接口的派生类要实现接口的所有方法。
接口和抽象类是不能被实例化的对象(引用类型)。

public delegate void DelegateTest();
    public interface ITest //只能包含非静态成员函数,隐式public,但不允许访问修饰符
    
        void Method(string a); //方法
        string Property //属性
        
            get; set;
        
        event DelegateTest EventTest; //事件,需要先定义一个委托
        int this[int index] //索引器
        
            get; set;
        
    

举个例子:游戏门:抽象类,不能实例,很多行为,定义接口,破坏可以击碎
系统接口鼠标行为,停留进入离开
很多设计模式,是对接口的应用,面向接口编程,实现层面更加有层次。
【参考C#图解第十五章接口】


【重点面试题】7、foreach迭代器遍历和for循环遍历的区别
如果集合需要foreach遍历,是否可行,存在一定问题
foreach中的迭代变量item是的只读,不能对其进行修改,比如list.Remove(item)操作
foreach只读的时候记录下来,在对记录做操作,或者直接用for循环遍历
foreach对int[]数组循环已经不产生GC,避免对ArrayList进行遍历

for语句中初始化变量i的作用域,循环体内部可见。
通过索引进行遍历,可以根据索引对所遍历集合进行修改
unity中for循环使用lambda表达式注意闭包问题

Foreach遍历原理
任何集合类(Array)对象都有一个GetEnumerator()方法,该方法可以返回一个实现了 IEnumerator接口的对象。
这个返回的IEnumerator对象既不是集合类对象,也不是集合的元素类对象,它是一个独立的类对象。
通过这个实现了 IEnumerator接口对象A,可以遍历访问集合类对象中的每一个元素对象
对象A访问MoveNext方法,方法为真,就可以访问Current方法,读取到集合的元素。

	    List<string> list = new List<string>()  "25", "哈3", "26", "花朵" ;
 		IEnumerator listEnumerator = list.GetEnumerator();
        while (listEnumerator.MoveNext())
        
            Console.WriteLine(listEnumerator.Current);
        

枚举器的实现(枚举器可用于读取集合中的数据,但不能用于修改集合)

链接: 参考资料.


【重点面试题】8、string和stringbuilder和stringBuffer区别
String不变性,字符序列不可变,对原管理中实例对象赋值,会重新开一个新的实例对象赋值,新开的实例对象会等待被GC。
string拼接要重新开辟空间,因为string原值不会改变,导致GC频繁,性能消耗大

StringBuffer是字符串可变对象,可通过自带的StringBuffer.方法来改变并生成想要的字符串。对原实例对象做拼接的实例,不会生成新的实例对象。
拼接使用StringBuilder和StringBuffer,只开辟一个内存空间,这是性能优化的点。

StringBuilder是字符串可变对象,基本和StringBuilder相同。
唯一的区别是StringBuffer是线程安全,相关方法前带synchronized关键字,一般用于多线程
StringBuilder是非线程安全,所以性能略好,一般用于单线程
三者性能比较 StringBuilder>StringBuffer>String

相关方法
StringBuilder.Append 将信息追加到当前 StringBuilder 的结尾。
StringBuilder.AppendFormat 用带格式文本替换字符串中传递的格式说明符。
StringBuilder.Insert 将字符串或对象插入到当前 StringBuilder 对象的指定索引处。
StringBuilder.Remove 从当前 StringBuilder 对象中移除指定数量的字符。
StringBuilder.Replace 替换指定索引处的指定字符。


9、使用List的区别
list=new list()会导致每增加一个内容就增加新内存,导致原内存浪费,GC频繁
需要添加一个固定参数,只开辟一个内存,list = new list(50)
性能优化的点


10、字符串比较
先用string 变量存储 obj.name ,这用只有一个内存空间保存
如果不存储 obj.name每一次比较都会产生新的内存空间、
比较obj.tag==”Tag“不使用,而是使用避免GC的obj.CompareTag(“tag”)
射线检测SphereColliderNoAlloc可以避免GC,比直接使用SphereCollider性能要好


【重点面试题】11、请简述GC垃圾管理器,和GC产生的原因,并描述如何避免
GC垃圾回收机制,避免堆内存溢出,定期回收那些没有有效引用的对象内存
GC优化,就是优化堆内存,减少堆内存,即时回收堆内存
GC归属于CLR

如何避免
1.减少new的次数
2.字符串拼接使用stringbuilder,字符串比较先定义一个变量存储,防止产生无效内存
3.list,new时候,规定内存大小
4.如果要射线检测,应该使用避免GC的方法XXXXNoAlloc函数
5.foreach迭代器容易导致GC(目前Unity5.5已修复),使用For循环
6.使用静态变量,GC不会回收存在的对象,但静态变量的引用对象可能被回收
7.使用枚举替代字符串变量
8.调用gameobject.tag=="XXX"就会产生内存垃圾;那么采用GameObject.CompareTag()可以避免内存垃圾的产生:
9.不要在频繁调用的函数中反复进行堆内存分配,比如OnTriggerXXX,Update等函数
10.在Update函数中,运行有规律的但不需要每一帧执行的代码,可以使用计时器,比如1秒执行一次某些代码!!!
链接: 参考文章.


12、请描述interface和抽象类之间的不同
接口是一种行为,抽象类是一种不能实例化的对象。
接口interface可以定义方法、属性、索引器、事件
抽象类abstract可以定义字段、静态字段和方法、抽象方法、属性、构造函数
接口可以继承多个接口,抽象类只能继承一个类
接口直接实现所有成员,抽象类重写override抽象方法
接口和抽象都不能被实例化,派生类必须实现基类或接口的方法
抽象类可以派生自另一个抽象类,接口可以多重实现,抽象类只能单一继承
举个例子:抽象类门,多接口继承【可破坏、金属】的行为方法,派生类实例化这个门,接口的实现类实现具体行为,派生类创建这样具体的可破坏的铁门
链接: 参考资料.


【重点面试题】13、反射的实现原理?
定义:运行时,动态获取类型信息,动态创建对象,动态访问成员的过程。
另一种定义:审查元数据并收集元数据的信息。
元数据:编译后的最基本数据单元,就是一堆表,反射就是解析这些元数据。
反射是在运行期间获取到类、对象、方法、数据的一种手段
主要使用类库System.Reflection
反射要点:如何获取类型,根据类型来动态创建对象,反射获取方法以及动态调用方法,动态创建委托
一、动态获取类型信息
1.System.Reflection.Assembly.Load(“XXXX.dll”) 动态加载程序集
2.System.Type.GetType(“XXXX类名”); //动态获取某程序集中某类信息
3.obj.GetType(); //已知对象获取类信息 ——或者——typeof(类型) //已知类类型
二、动态创建对象实例(上一步操作后获得类对象)
System.Activator.CreateInstance(Type type);
三、动态访问成员调用方法(上一步操作后已获取实例对象)
System.Reflection.MethodInfo method = type.GetMethod(“方法名”);//获得方法
System.Reflection.MethodInfo.Invoke(object , new object[]参数) //调用的类实例和实例参数

核心类
System.Reflection.Assembly 描述程序集
System.Type 描述类
System.Reflection.FieldInfo 描述了类的字段
System.Reflection.ConstructorInfo 描述构造函数
System.Reflection.MethodInfo 描述类的方法
System.Reflection.PropertyInfo 描述类的属性

反射耗性能,lua是动态语言,一种小巧的脚本语言,会使用反射机制。

知识扩展
手机端不支持编译,需要热更方案,通过lua的反射机制将旧的DLL文件替换成新的DLL文件。
Xlua是lua框架,由TX鹅肠进行维护,方便了C#与lua相互调用,C#端实现lua虚拟机
链接:参考资料太多,主要搜索,C#反射机制,lua,xlua性能等等。


14、.Net 与 Mono 的关系?
.Net是一个语言平台
Mono为.Net提供集成开发环境,集成并实现了
.NET的编译器、CLR 和基础类库,
使得.Net既可以运行在windows也可以运行于 linux,Unix,Mac OS 等。

15、在类的构造函数前加上 static 会报什么错?为什么?
静态构造函数不允许添加访问修饰符,且必须无参数
原因:无论创建多少类型的对象,静态构造函数只执行一次
类实例化或者首静态成员调用之前,运行库会先调用静态构造函数
静态构造函数优先级高于任何其它构造函数
也无法使用this和base来调用静态构造函数
一个类只能有一个静态函数,如果有静态变量,系统也会自动生成静态函数


16、C# String 类型比 stringBuilder 类型的优势是什么?
string功能性更强,通用性更好,用途更广泛
string不可变性,线程栈同步
编译器已将把string,并通过操作优化成stringbuilder,在性能上不差,一般可以用string代替stringbuilder


17、C# 函数 Func(string a, string b)用 Lambda 表达式怎么写?
Lambda表达式(任意参数)=> 表达式 ; => 读作goesto
(a,b)=> ;


【重点面试题】18、C#中有哪些常用的容器类,各有什么特点,性能区别?
Stack栈:先进后出,入栈和出栈,底层泛型数组实现,入栈动态扩容2倍
Queue队列:先进先出,入队和出队,底层泛型数组实现,表头表尾指针,判空还是满通过size比较
Queue和Stack主要是用来存储临时信息的

Array数组:需要声明长度,不安全
ArrayList数组列表:动态增加数组,不安全,实现了IList接口(表示可按照索引进行访问的非泛型集合对象),Object数组实现
List列表:底层实现是泛型数组,特性,动态扩容,泛型安全
将泛型数据(对值类型来说就是数据本身,对引用类型来说就是引用)存储在一个泛型数组中,添加元素时若超过当前泛型数组容量,则以2倍扩容,进而实现List大小动态可变。(注:大小指容量,不是Count)
LinkList链表
1、数组和List、ArrayList集合都有一个重大的缺陷,就是从数组的中间位置删除或插入一个元素需要付出很大的代价,其原因是数组中处于被删除元素之后的所有元素都要向数组的前端移动。
2、LinkedList(底层是由链表实现的)基于链表的数据结构,很好的解决了数组删除插入效率低的问题,且不用动态的扩充数组的长度。
3、LinkedList的优点:插入、删除元素效率比较高;缺点:访问效率比较低。

C#则List和LinkedList的区别
List是数组列表,LinkedList是双向链表,List读取速度快,时间复杂度是O(1),增删比较麻烦,时间复杂度是O(n).
LinkedList读取时间复杂度是O(n),增删时间复杂度是O(1)

HashTable哈希表(散列表)
概念:不定长的二进制数据通过哈希函数映射到一个较短的二进制数据集,即Key通过HashFunction函数获得HashCode
装填因子:α=n/m=0.72 ,存储的数据N和空间大小M
然后通过哈希桶算法,HashCode分段,每一段都是一个桶结构,一般是HashCode直接取余。
桶结构会加剧冲突,解决冲突使用拉链法,将产生冲突的元素建立一个单链表,并将头指针地址存储至Hash表对应桶的位置。这样定位到Hash表桶的位置后可通过遍历单链表的形式来查找元素。
1、Key—Value形式存取,无序,类型Object,需要类型转换。
2、Hashtable查询速度快,而添加速度相对慢
3、Hashtable中的数据实际存储在内部的一个数据桶里(bucket结构体数组),容量固定,根据数组索引获取值。

Directionary<TKey,TVaule>字典,有序,泛型存储不需要进行类型装换(不需要装箱拆箱),碰撞阈值扩容~
HashSet:一组不包含重复的元素集合【LeetCode算法217存在重复元素】

性能排序:
插入性能: LinkedList > Dictionary > HashTable > List
遍历性能:List > LinkedList > Dictionary > HashTable
删除性能: Dictionary > LinkedList > HashTable > List
小结:
在修改较频繁,且查找和删除也较多时,首选LinkedList,
在主要以删除为主,插入为辅,且查找较少时,首选Dictionary,
在查找频繁,而又无需修改的情况下,则首选List。

//哈希表结构体
private struct bucket 
   public Object key;//键
    public Object val;//值
    public int hash_col;//哈希码

//字典结构体
private struct Entry 
    public int hashCode;    // 除符号位以外的31位hashCode值, 如果该Entry没有被使用,那么为-1
    public int next;        // 下一个元素的下标索引,如果没有下一个就为-1
    public TKey key;        // 存放元素的键
    public TValue value;    // 存放元素的值


private int[] buckets;      // Hash桶
private Entry[] entries;    // Entry数组,存放元素
private int count;          // 当前entries的index位置
private int version;        // 当前版本,防止迭代过程中集合被更改
private int freeList;       // 被删除Entry在entries中的下标index,这个位置是空闲的
private int freeCount;      // 有多少个被删除的Entry,有多少个空闲的位置
private IEqualityComparer<TKey> comparer;   // 比较器
private KeyCollection keys;     // 存放Key的集合
private ValueCollection values;     // 存放Value的集合

链接: Stack参考链接.
链接: Queue参考链接.
链接: ArrayList参考链接.
链接: List参考链接.
链接: HashTable参考链接.
链接: Dictionary参考链接.


19、C#中常规容器和泛型容器有什么区别,哪种效率高?
常规容器有拆箱和装箱操作,速度慢,消耗性能
泛型容器效率更高


20、有哪些常见的数值类?
简单数值类型:整数型、字符型、布尔型、实数型
复合类型:结构类型、枚举类型


21、C#中委托和接口有什么区别?各用在什么场合?
委托delegate:unity事件与委托密切相关,回调机制,减少对象之间数据交互
接口interface:多人协作,完全抽象,类单继承
委托是约束方法的集合
接口是约束类具备的功能集合,解决类单继承问题


22、C#中unsafe关键字是用来做什么的?什么场合下使用?
unsafe 非托管代码,配合fixed一起使用 ,用在需要指针操作的场合
项目背包系统的任务装备栏使用到


【重点面试题】23、C#中ref和out关键字有什么区别?知道Ref的深层原理是什么?
ref修饰引用参数。参数必须赋值,带回返回值,又进又出
out修饰输出参数。参数可以不赋值,带回返回值之前必须明确赋值,
引用参数和输出参数不会创建新的存储位置

如果ref参数是值类型,原先的值类型数据,会随着方法里的数据改变而改变,
如果ref参数值引用类型,方法里重新赋值后,原对象堆中数据会改变,如果对引用类型再次创建新对象并赋值给ref参数,引用地址会重新指向新对象堆数据。方法结束后形参和新对象都会消失。实参还是指向原始对象,值不够数据改变了
【参考C#图解教程:引用类型作为值参数和引用参数】


24、For,foreach,Enumerator.MoveNext的使用,与内存消耗情况
for通过索引或下标一次进行遍历
foreach和Enumerator.MoveNext通过迭代进行遍历
内存消耗本质没有多少区别
迭代器有一个状态机
before
running:yield return 或 yield break 或迭代结束
after


25、函数中多次使用string的+=处理,会产生大量内存垃圾(垃圾碎片),有什么好的方法可以解决。
使用stringbuilder的append


26、当需要频繁创建使用某个对象时,有什么好的程序设计方案来节省内存?
Unity对象池
设计单例模式全局实例化一次


27、Foreach循环迭代时,若把其中的某个元素删除,程序报错,怎么找到那个元素?以及具体怎么处理这种情况?(注:Try…Catch捕捉异常,发送信息不可行)
foreach迭代器不能进行操作
在循环中记录索引值或者key值,在迭代结束后,查找到这个元素,在进行删除操作


28、GameObject a=new GameObject() GameObject b=a 实例化出来了A,将A赋给B,现在将B删除,问A还存在吗?
存在
a引用地址在线程栈中,数据内容在托管堆中
b引用地址在线程栈中,数据内容指向A的托管堆中的内容
B删除,只是删除b的引用地址


【重点面试题】29、C#引用和C++指针的区别
C#不支持指针,但可以使用Unsafe,不安全模式,CLR不检测
C#可以定义指针的类型、整数型、实数型、struct结构体
C#指针操作符、C#指针定义
使用fixed,可以操作类中的值类型
相同点:都是地址
指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点:
指针是个实体,引用是个别名。
sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
引用是类型安全的,而指针在不安全模式下


【重点面试题】30、using的作用
资源:实现了IDisposable接口的类或结构。
using语句确保这些资源能够被适当的释放(Resource.Dispose)
using原理
using(分配资源) 使用资源 ——> 释放资源 (隐式)
使用资源(可能会导致异常)会被放进Try块里,释放资源(有无异常)都会放进在finally块

using(分配资源)

	try 使用资源 
	finally Resource.Dispose

using指令,using+命名空间(或命名空间一个类型) 在源文件的顶端声明
也可以不使用using,直接命名空间.类.成员方法


【重点面试题】31、字典Dictionary的内部实现原理
泛型集合命名空间using System.Collections.Generic;
任何键都必须是唯一

该类最大的优点就是它查找元素的时间复杂度接近O(1),实际项目中常被用来做一些数据的本地缓存,提升整体效率。
实现原理
1.哈希算法:将不定长度的二进制数据集给映射到一个较短的二进制长度数据集一个Key通过HashFunc得到HashCode
2.Hash桶算法:对HashCode进行分段显示,常用方法是对HashCode直接取余
3.解决碰撞冲突算法(拉链法):分段会导致key对应的桶会相同,拉链法的思想就像对冲突的元素,建立一个单链表,头指针存储到对应的哈希桶位置。反之就是通过确定hash桶位置后,遍历单链表,获取对应的value

Key值 HashFunc Buckets桶 Entries入口(最小数据结构)

Dictionary字典中最小的数据结构体Entry,调用Add(Key,Value)方法添加的元素都会被封装在这样的一个结构体中。

private struct Entry 
    public int hashCode;    // 除符号位以外的31位hashCode值, 如果该Entry没有被使用,那么为-1
    public int next;        // 下一个元素的下标索引,如果没有下一个就为-1
    public TKey key;        // 存放元素的键
    public TValue value;    // 存放元素的值

Collection版本控制,字典重要变量version,这个变量,在每一次新增、修改和删除操作时,都会使version++
之后每一次迭代过程都会检查版本号是否一致,如果不一致将抛出异常。
这样就避免了在迭代过程中修改了集合,造成很多诡异的问题。

链接: Dictionary实现原理资料.


【重点面试题】32、泛型是什么
多个代码对 【不同数据类型】 执行 【相同指令】的情况
泛型:多个类型共享一组代码
泛型允许类型参数化,泛型类型是类型的模板
5种泛型:类、结构、接口、委托、方法
类型占位符 T 来表示泛型

泛型类不是实际的类,而是类的模板
从泛型类型创建实例
声明泛型类型》通过提供【真实类型】创建构造函数类型》从构造类型创建实例
类<T1,T2> 泛型类型参数

性能:泛型不会强行对值类型进行装箱和拆箱,或对引用类型进行向下强制类型转换,所以性能得到提高
安全:通过知道使用泛型定义的变量的类型限制,编译器可以在一定程度上验证类型假设,所以泛型提高了程序的类型安全。


【重点面试题】33、结构体和类有什么区别


二、Unity脚本基础(已更新2021.3.8)

【重点面试题】1、Unity3D中的协程(coroutine),C#线程和进程之间的区别是什么?

简记:协程和线程区别
协程(协同程序Coroutine): 同一时间只能执行某个协程。开辟多个协程开销不大。协程适合对某任务进行分时处理。 Unity提供了StartCoroutine来开启协程,当你在 StartCoroutine 的函数体里处理一段代码时,利用 yield语句等待执行结果,这期间不影响主程序的继续执行,可以协同工作。

举个例子:LoadLevelAsync(异步加载关卡)后台加载场景的方法。允许你在后台加载新资源和场景,再利用协同,你就可以在前台用 loading 条或动画提示玩家游戏处于加载中,同时后台协同处理加载的事宜。

进程progress:进程是线程的容器。
线程thread: 同一时间可以同时执行多个线程。开辟多条线程开销很大。线程适合多任务同时处理,并发并行。
协程:具体多返回点的方法,时间分片(帧),Unity只能是单线程,只能在主线程调用Api,对象


2、Unity3D 是否支持写成多线程程序?如果支持的话需要注意什么?
支持,如果掌握的Unity多线程的方法,就可以从容的使用多个硬件处理器或处理很难划分管理数据块。
【注意】
1、只能从主线程中访问 Unity3D 的组件,对象和 Unity3D 系统调用
2、如果同时你要处理很多事情或者与 Unity 的对象互动,可以用 thread,可以通过排序来解决一个线程改变一个数据的问题,否则使用协程coroutine。
3、C#中有 lock 这个关键字,以确保只有一个线程可以在特定时间内访问特定的对象

【重点面试题】3、OnEnable、Awake、Start 运行时的发生顺序?哪些可能在同一个对象周期中反复的发生?
Awake > OnEnable>Start 推荐看脚本生命周期顺序
OnEnable可以在同一周期反复的发生
SetActive(true)就会反复触发OnEnable事件
SetActive(false)就会反复触发OnDisable事件

其中Awake函数一般用于实现单例模式;当脚本被实例化时,调用awake,完成成员变量的初始化,在单例模式中会有一个虚方法OnStart在awake中调用,在实际脚本中重写OnStart方法
OnEnable函数是在游戏对象可以调用时调用;
OnDisable是在游戏对象不可用时调用;
Start函数则是在场景中显示该游戏对象前调用一次,用于开始设置物体属性和渲染;
FixedUpdate函数具有固定更新频率,一般进行游戏对象的物理引擎的更新;
Update函数则是渲染帧更新,每秒更新一定频率;
LateUpdate函数是延迟更新,只有在每一帧的所有Update函数都执行完了过后才会执行;
而OnGUI函数则是在每一帧更新时调用。

4、Unity3D 如何获知场景中需要加载的数据?如何动态资源加载?
instantiate:最简单的一种方式,以实例化的方式动态生成一个物体。

Assetsbundle:即将资源打成 asset bundle 放在服务器或本地磁盘,然后使用WWW模块get 下来,然后从这个bundle中load某个object,unity官方推荐也是绝大多数商业化项目使用的一种方式。

Resource.Load:可以直接load并返回某个类型的Object,前提是要把这个资源放在Resource命名的文件夹下,Unity不管有没有场景引用,都会将其全部打入到安装包中

AssetDatabase.loadasset :这种方式只在editor范围内有效,游戏运行时没有这个函数,它通常是在开发中调试用的。


5、Unity中碰撞器(Collider)和触发器(Trigger)的区别?
碰撞器(Collider)有碰撞效果,IsTrigger=false,可以调OnCollisionEnter/Stay/Exit函数

触发器(Trigger)没有碰撞效果,isTrigger=true,可以调用OnTriggerEnter/Stay/Exit函数


6、U3D中,几种施加力的方式,描述出来
首先需要对象要有rigidbody组件
Rigidbody.AddForce /AddForceAtPosition
2D Constant Force
Force/Relative Force/Torque


7、物体自旋转使用的函数叫什么?物体绕某点旋转使用函数叫什么?
transform.Rotate
transform.RotateAround


8、u3d 提供了一个用于保存读取数据的类,(playerPrefs),请列出保存读取整形数据的函数
PlayerPrefs类是一个本地持久化保存与读取数据的类
PlayerPrefs类支持3中数据类型的保存和读取,浮点型,整形,和字符串型。
分别对应的函数为:
SetInt();保存整型数据;GetInt();读取整形数据;
SetFloat();保存浮点型数据; GetFlost();读取浮点型数据;
SetString();保存字符串型数据; GetString();读取字符串型数据;


9、unity3d 从唤醒到销毁有一段生命周期,请列出系统自己调用的几个重要方法。
(init)Awake>OnEnable>Start > (Physic)FixedUpdate> (GameLogic)Update>Yield >LateUpdate>OnGui>OnDisable>OnDistroy


10、物理更新一般在哪个系统函数里?
FixedUpdate,每固定帧绘制时执行一次,和 update 不同的是 FixedUpdate 是渲染帧执行,如果你的渲染效率低下的时候 FixedUpdate 调用次数就会跟着下降。

FixedUpdate 比较适用于物理引擎的计算,因为是跟固定帧渲染有关。
Update 就比较适合做游戏逻辑更新。
LateUpdate比较适合相机更新,update后更新,防止视角移动,游戏对象发生空帧未出现。


11、反向旋转动画的方法是什么?
1.将动画速度调成-1
2.改代码animation.speed=-1


【重点面试题】12、用代码实现第三人称角色控制器?第一人称角色控制器
大致思路:
摄像机与角色的距离范围
摄像机旋转、平移
鼠标控制摄像机
//代码稍后会在博客中贴出,转链接,目前未实现


13、获取、增加、删除组件的命令分别是什么?
获取:GetComponent
增加:AddComponent
删除:Destroy


14、Animation.CrossFade 是什么?
动画淡入淡出

15、Application.LoadLevel 命令作用是什么?
加载关卡,已弃用
现在使用SceneManager.LoadScene

16、调式记录到控制台的命令是什么?
Debug.Log();

17、编辑器类存放路径是什么?
Asset/Editor

18、使用原生 GUI 创建一个可以拖动的窗口命令是什么?
GUI.DragWindow

19、localPosition 与 Position 的使用区别?
localPosition :自身坐标系,相对于父级的位置
Position :世界坐标系中的位置

20、Mathf.Round和Mathf.Clamp和Mathf.Lerp含义?
Mathf.Round:四舍五入
Mathf.Clamp:左右限值
Mathf.Lerp:插值

【重点面试题】21、写一个计时器工具,从整点开始,格式为00:00:00
分小时、分、秒

22、写出 Animation 的五个方法
AddClip 将 clip 添加到名称为 newName 的动画中。
Blend 在后续 time 秒中将名称为 animation 的动画向 targetWeight 混合。
CrossFade 在后续 time 秒的时间段内,使名称为 animation 的动画淡入,使其他动画淡出。
CrossFadeQueued 使动画在上一个动画播放完成后交叉淡入淡出。
IsPlaying 名称为 name 的动画是否正在播放?
PlayQueued 在先前的动画播放完毕后再播放动画。
RemoveClip 从动画列表中移除剪辑。
Sample 对当前状态的动画进行采样。
Stop 停止所有使用该动画启动的正在播放的动画。

23、用鼠标实现在场景中拖动物体,用鼠标滚轮实现缩放(用一个 Cube 即可)
在场景中添加一个Plan,Camera,Directional Light,Cube。添加两个脚本一个挂在Camera上,另一个挂在Cube上。
1.鼠标滚轮实现缩放:将摄像机的镜头拉近或者拉远,调整摄像机的视角就可以实现2.鼠标实现在场景中拖动物体:解决思路就是将世界坐标转换成屏幕坐标,然后计算物体与鼠标之间移动量,循环鼠标被按下操作,得到鼠标的当前位置,加上计算好的移动量,将新的坐标赋值给物理就行了。 具体代码实现:http://www.cnblogs.com/hewencong/p/4299722.html

24、<愤怒的小鸟>给予初速度以后,怎么让小鸟受到重力和空气阻力的影响而绘制抛物线轨迹,说出具体的计算方法
Vector3 v 代表初速度 v’代表现在的速度, 假设小鸟是沿的 z 轴也就是transform.forward 方向,运动的质量为 m,那么 v‘=v-new Vector3(0,mgt,ft),transform.Translate(v’)做的就是抛物线运动(g 为重力加速度不要用现实中的需要自己调试,f 为阻力也要自己调试
设置,t 为时间)

25、当游戏中需要频繁创建一个物体时,我们需要怎样做能够节省内存?
1.使用预制体对象
2.使用对象池技术

26、碰撞检测需要物体具备什么属性?
能检测碰撞发生的方式有两种,一种是利用碰撞器,另一种则是利用触发器

27、如何使子控件居中,如果使用UGUI怎么实现
锚点设置为中心

28、去掉敏感字的程序(手写程序)
字符串replace

29、写出WWW的几个方法
WWW.LoadFromCacheOrDownload:可被用于将Assets Bundles自动缓存到本地磁盘
WWW.Dispose :释放现有的 WWW 对象。
WWW.isDone:是否完成下载?(只读)
WWW.progress:下载进度(只读)。

【重点面试题】30、启用MipMaps对内存的影响是?MipMap的作用?如何操作?
增加约33%的内存,1/4 +1/16
Lod相关知识

31、采用Input.mousePosition来获取鼠标在屏幕上的位置
左下角为原点(0,0),右上角为(Screen.Height,Screen.Width)

32、Unity中销毁GameObject的方式,简述Destroy和DestroyImmediate的区别
Destroy销毁消息对象,内存中还是存在,只有内存不够才被清除释放内存
DestroyImmediate立即销毁对象,并释放内存

33、如何检测物体是否被其他对象遮挡
射线检测
EventSystem.IsPointerOverGameObject
是否具有给定 ID 的指针是否位于 EventSystem 对象上


34、UnityAction和UnityFunc的区别
unity中需要带上修饰event,事件与委托密切相关,两行代码变一行代码
public event Action myEvent;

UnityAction本质上就是委托,带泛型参数最多4个,且没有返回值的方法
Action<T1,T2,T3>
UnityFunc本质上也是委托,带泛型参数最多4个,可以有返回值的方法
Func<T1,T2,T3,Return>

Action和Func的重要区别:
Action只用于没有返回值的方法,Func只用于有返回值的方法
它们泛型里的区别:
Action的泛型里要和方法参数的类型相同,且只有一种泛型
Func的泛型里前者和方法参数类型相同,最后一个与返回值类型相同

一般用于回调方法,注册事件,类直接数据交互松耦合
链接: 参考资料.


【重点面试题】35、unity常用资源路径有哪些

//获取的目录路径最后不包含  /
//获得的文件路径开头包含 /
Application.dataPath; //Asset文件夹的绝对路径
//只读
Application.streamingAssetsPath;  //StreamingAssets文件夹的绝对路径(要先判断是否存在这个文件夹路径)
Application.persistentData ; //可读写

//资源数据库 (AssetDatabase) 是允许您访问工程中的资源的 API
AssetDatabase.GetAllAssetPaths; //获取所有的资源文件(不包含meta文件)
AssetDatabase.GetAssetPath(object) //获取object对象的相对路径
AssetDatabase.Refresh(); //刷新
AssetDatabase.GetDependencies(string); //获取依赖项文件


Directory.Delete(p, true); //删除P路径目录
Directory.Exists(p);  //是否存在P路径目录
Directory.CreateDirectory(p); //创建P路径目录

AssetDatabase //类库,对Asset文件夹下的文件进行操作,获取相对路径,获取所有文件,获取相对依赖项
Directory //类库,相关文件夹路径目录进行操作,是否存在,创建目录,删除等操作

【重点面试题】36、向量的点乘、叉乘以及归一化的意义?
叉乘cross:可以获得两个向量A和B所构成平面,垂直的向量C,和左手坐标系类似,可以用来判断角色移动方向,判断顺时针还是逆时针旋转

点乘 dot :用来求向量之间的夹角,判断向量是否在同一方向、以及B向量在A向量上的投影

a·b>0 方向基本相同,夹角在0°到90°之间
a·b=0 正交
a·b<0 方向基本相反,夹角在90°到180°之间

归一化normalized:用在只关系方向,不关心大小的情况下


三、Unity性能优化(必问)(已更新2021.3.8)

lod 是什么,优缺点是什么
MipMap 是什么?作用?
当游戏中需要频繁创建一个物体对象时,我们需要怎么做来节省内存。
如何优化内存?
动态加载资源的方式?和区别
请简述一下对象池原理,什么情况下使用?
19.使用mipmap有什么好处?什么情况下使用?
Unity内存优化?GC垃圾回收
你认为unity在开发过程中哪些地方比较容易造成内存泄漏和内存泄漏问题?如何避免?
如何解决过多创建和删除对象带来的卡顿问题
Unity资源加载的有几种方式,简述asset bundie
背包系统中只有20个格子,现在有总共有100个物体,除了显示在视野中的20个外,对其他的处理方法?(注:将其他隐藏起来不可行,对象池得有具体的说明)

四、设计模式

1、用过哪些设计

以上是关于[Unity面试] 2021年Unity面试题分享(面试题Lua突破3.8已更新)的主要内容,如果未能解决你的问题,请参考以下文章

2022年Unity 面试题 |五萬字 二佰道| Unity面试题大全,面试题总结全网最全,收藏一篇足够面试

2022年Unity 面试题 |五萬字 二佰道| Unity面试题大全,面试题总结全网最全,收藏一篇足够面试

2021年米哈游unity社招面试经历

[Unity3D]上海某大型游戏公司的基础面试题

游戏开发宝藏Unity学习路线,三万字大纲,从基础到大神(面试题大纲 | 知识图谱 | Unity游戏开发工程师)

游戏开发宝藏Unity学习路线,三万字大纲,从基础到大神(面试题大纲 | 知识图谱 | Unity游戏开发工程师)