面试题一
C#
1. 装箱和拆箱是什么?
装箱是把栈空间的数据转移到堆空间上去,值类型传引用类型上去
int i = 2;
object o = i;
拆箱是把堆空间的数据转移到栈空间上去,引用类型传到值类型上去
i = (int) o
2. 值和引用类型在变量赋值时的区别是什么?
值类型的数据赋值的时候是指向同一块内存区域,当前一个改变的时候后一个也会跟着改变。
引用类型的数据在赋值的时候是重新在栈上开辟了一段新的内存区域,所以一个改变的时候对另一个不会有影响。
3. 委托和事件在使用上的区别是什么?
委托是一个类型,事件是一种特殊的委托,是在类中定义的成员。
委托通常用于回调函数,多播委托等场景,而事件不能直接调用,而是使用特定的语法来触发和处理,用于发布和订阅事件。
-
有两个接口IA和IB,他们中有一个同名方法Test()
一个类同时继承这两个接口,应该如何处理他们的同名方法?
实现接口就好了。 -
请说明C#中的List是如何扩容的
本质是数组,重新分配内存空间,并将原来的数据拷贝到新地址上,然后销毁原来的内存区域。
Unity
1. Unity中点乘和叉乘对于我们来说的作用是什么?
点乘:判断两个物体之间的方位。计算两个向量之间的角度。
叉乘:计算两个向量平面的法向量,遵循右手螺旋定制,判断两向量之间的左右位置关系
2. Unity中多线程执行下面哪些代码会报错?
A. Application.persistentDataPath
B. File.Exists("文件名")
UnityEngine命名空间中相关类基本不能被Unity中多线程使用
C. transform.Translate
D. Object.Destroy(对象)
3. Application.streamingAssetsPath 只读
Application.persistentDataPath 可读可写
两个路径有何区别?对于我们的意义是什么?
4. 请简述Unity中协程的原理
协程由两部分构成,一个是协程函数本体,是一个迭代器函数,还有一个是协程调度器,是一个协程管理器,协程利用迭代器的分步执行的特点,以及协程调度器对迭代器函数进行统一管理,根据迭代器函数的返回值来决定下一次执行函数逻辑的时间点,最终实现逻辑分时分步执行的目的。
5. Unity底层如何处理C#代码?
Mono编辑器和IL2CPP脚本编译工具。
面试题二
C#
1. 请说说你认为C#中 == 和 Equals 的区别是什么?
== 是一个逻辑运算符,而Equs是Object父类的一个虚方法,可以被子类重写,用于判断两个对象的内容是否相等。
2. 浅拷贝和深拷贝的区别?可以举例说明
浅拷贝拷贝的是引用地址值,一个对象的值变了,那么另外一个对象的值也会跟着变
深拷贝拷贝的是对象和值,两个对象修改其中任意值都不会影响对方
3. 下面两种获10000个数的方式,哪种效率更高?为什么?
第二种效率最高,因为第一组会频繁地进行数组的扩容操作,不断地开辟新的内存,赋值,销毁数组,效率更低。
4. 请说出下方代码中A处和B处谁先打印?
B先
2.A、B出打印的i值分别是多少?
10 11
5. 请问A、B两处 i 的值为多少?
11 11
Unity
1. Unity中当一个细小高速物体撞击另一个较大物体时,会出现什么情况?如何避免?
出现细小物体被较大物体卡住的情况,可以根据细小物体距离较大物体的距离对碰撞检测的精度和频率进行调整,当细小物体离较大物体越近的时候,检测频率越快。
或者利用射线检测来替代细小物体的物理系统碰撞,修改Rigibody钢体中的Interpolate插值和CollisionDetection碰撞检测两个参数,提高碰撞检测的准确性。
2. 请简述一下Prefab(预制体)的本质是什么?
本质是一个配置文件,记录了挂载脚本的信息以及脚本信息中可配置的属性信息
3.Unity是否支持写成多线程程序?如果支持的话需要注意什么?
支持
只能从主线程访问Unity相关组件,对象和UnityEngine命名空间的绝大部分内容,如果有多线程要访问Unity主线程并同时修改一些数据,可以通过lock关键词加锁。
4. 请简述一下对象池,在游戏开发中我们什么时候会用到它?
对象池是为了避免频繁创建和销毁同类对象所造成的内存消耗,降低GC发生的概率,把不用的对象放入对象池中而不是让它直接变成垃圾,下次需要用到对象再从对象池中获取,通过占用内存来避免更多的内存消耗和GC的发生。
再游戏中频繁地创建对象,实例化对象的地方,都可以用到对象池,例如游戏中的子弹,伤害字体,特效等。
5. 什么是DrawCall?DrawCall为什么会影响游戏运行效率?如何减少DrawCall?
DrawCall是指CPU准备渲染相关数据并通知GPU的过程称为一次DrawCall。
如果DrawCall的次数较高,意味着CPU需要花更多的时间来准备渲染数据,会进行更多的计算,影响游戏的效率。
2D和UI层面:打图集,并注意面板中不同图集图片的层级不要穿插
3D模型层面:利用动态批处理和静态批处理,尽量不使用实时光影和实时阴影
面试题三
C#
1. 泛型的约束有哪几种?
值类型约束 T:struct
引用类型约束 T:class
公共无参构造约束 T:new()
类约束 T:类名
接口约束 T:接口名
2. 什么是闭包?可以举例说明
闭包是指有权访问另一个函数作用域中的变量的函数,例如匿名函数
3. 内存泄漏指什么?常见的内存泄漏有哪些?
指的是对象超过生命周期后而不能被GC回收,一般指不会再使用的引用对象由于某些操作而不能被GC垃圾回收,而一直占用着内存
常见的有:
不使用的引用对象没有置null,一直被引用
委托或事件注册后没有接触注册
文件操作时,没有使用using或者没有进行Dispose()
4. 序列化是什么?常见的序列化方式有哪些?什么时候我们会用到序列化?
序列化是将程序中的数据对象转换为可以存储和运输的形式的过程
比如常见的xml,json,2进制等。将内存中的数据按照自己定义的规则进行序列化,就可以进行存储和传输,当读取和接受数据时,只需要按照对应规则进行反序列化便可以得到原始数据
存储:数据持久化
传输:网络通讯
5. 请问A、B、C 三处打印结果分别为多少?为什么?
10 100 100
Unity
1. transform.forward和Vector3.forword的区别
一个是针对物体的本地坐标系的朝向位置
一个是世界坐标系的z轴位置
2. Unity中如何解决过多创建和删除对象带来的卡顿问题?
可以利用对象池技术,利用内存占用来减少频繁创建和删除对象带来的内存消耗和GC次数
使用协同程序,分时分步创建或删除,避免一帧中处理太多对象
3.游戏中的成就系统,我们一般会使用设计模式中的哪种模式来制作?为什么?
利用观察者模式,因为这可以很好地进行解耦。
4. 请简述热更新的流程
从资源服务器上下载对比文件
将本地文件和远端的对比文件进行对比,记录哪些需要更新或移除的资源
按照记录进行本地资源的更新
再次将本地文件和远端的对比文件进行比对,直到两者达成一致
5. 我们应该如何优化UI(基于UGUI)
利用图集合并的方式,来减少DrawCall
面试题四
C#
1. 请说明字符串中
string str = null 不会在堆上分配内存
string str = "" 在堆上分配内存,值为空字符串
string str = string.Empty 在堆上分配内存,值为一个只读变量
三者的区别
2. C#重载运算符,重载 == 和 != 以及 万物之父Object基类中的虚方法 virtual bool Equals(Object obj)对于我们的意义是什么?
符号= = 和 != 都是用于非引用类型变量的比较
而虚方法Equals常用于两个对象的比较
3. 在开发时,对string和StringBuilder我们应该如何选择
string是不可变的,在创建后不能修改。每次对string进行修改后,都会重新创建一个string对象,产生更多的内存开销。
stringBuilder是可变的,适用于频繁地进行字符串连接,拼接和修改,提高性能。
4. 请简要说明.Net跨语言原理
在使用.Net开发环境的时候,首先会编译为CIL中间代码,然后再由CLR将中间代码翻译为对应操作系统的原生代码,并在windows上运行。
Unity
1. Unity中的Destroy和DestroyImmediate的区别是什么?
Destroy可以指定删除的延迟时间,不填写的话最快也会在下一帧前完成删除,如果在Destroy下一句进行对象判断时,该对象不会为空
DestroyImmediate会立刻删除该对象
2.
请问最终打印的 s 的结果为?
AC
3. 第一次执行GameObject.Instantiate时可能出现明显的卡顿
如何解决该问题?
(1) 相关资源加载:如果是由于资源加载带来卡顿,那么可以通过预加载的形式将较大的资源提前或分帧加载。
(2)脚本初始化:实例化对象时,会同时执行它身上挂载所有脚本的初始化工作,我们可以将一些初始化逻辑,尽量不要在Awake和Start中做比较复杂的逻辑,或者将复杂逻辑提前或者分帧处理
(3)对于频繁使用的对象,可以使用缓存池
4. Lua如何实现面向对象的三大特性?
封装
继承
多态
5. Unity使用IL2CPP打包时,我们应该注意什么?如何避免(可以举例说明)
使用IL2CPP进行打包的时候,最可能出现的问题就是代码裁剪,IL2CPP会将它认为的不使用的代码裁剪掉。但是对于一些用于Lua进行开发的时候,这些代码不会直接在引擎里面使用,都是在lua中使用的,此时最容易发生的问题就是代码裁剪,导致打包后出现异常和报错。
避免的话可以在静态方法中显示不想被裁剪的内容。
面试题五
1.以下代码,谁的效率更高?为什么?
代码2的效率更高,因为list.Add的底层是一个数组,当往空数组中加新的元素的时候 ,会触发数组的扩容机制,增加了内存消耗。
2.数组和链表的区别是什么?
(1)访问效率
数组可以通过下标直接访问对应位置的元素,而链表只能从头节点开始往后遍历
数组元素的存取是连续的,而链表不连续。
(2)插入删除
数组在插入和删除时,需要整体移动数组中更多大部分元素,效率低
链表是链式存储,在插入和删除时,效率高
(3)越界
数组是顺序存储,在声明的时候就确定了容量,不处理扩容的话,会存在越界
链表是链式存储,无越界风险
3.C# 中的Action和Func是什么?
Unity 中的UnityAction是什么?
他们有什么区别?
4.请问最终的打印结果是什么?
0-Alice-7
5.网络游戏开发中,网络传输数据的基本流程是什么?
客户端将自定义的对象数据序列化为2进制数据发送给服务端
服务端将收到的2进制数据反序列化为对应的类对象进行逻辑处理
服务端发送给客户端的消息也是同理
Unity
1.两个四元数相乘有什么作用?
表示角度的叠加
四元数乘以向量有什么作用?
表示这个向量经过旋转后的位置
2.图中的小球是否被渲染了?是否会产生DrawCall?
没有,在应用阶段就进行了摄像机的视锥体剔除,在视锥体之外的游戏对象不会显示,也不会进行渲染,不会提交数据给GPU,也就不会产生Draw Call
3.在没有使用遮挡剔除的情况下,图中A和B都是默认标准材质
图中的小球最终是否会被渲染,是否会产生DrawCall
会产生DrawCall,但是小球在立方体后方,不会通过深度测试,所以不会被渲染。
4.如果不考虑IOS平台,只在Windows和Android平台上发布游戏,如何在不使用第三方热更新方案的前提下实现热更新功能
利用C#的反射和通过热更DLL文件的形式,加载程序集(dll),利用反射执行热更DLL包中的逻辑
面试题六
C#
1.C#中如何让自定义容器类能够使用for循环遍历?(通过 类对象[索引] 的形式遍历)
在下面的基础上实现 索引器this[int index]
2.C#中如何让自定义容器类能够使用foreach循环遍历?
继承IEnumerator,IEnumerable两个接口
实现其中的
(1)GetEnumerator方法
(2)Current属性
(3)MoveNext方法
语法糖:利用yield return语法糖,
3.C#中接口的作用是什么?说说你的理解
用于对行为的继承关系,而不是对象
当不同对象有相同的行为时,可以利用接口对不同对象的行为进行整合
4.Unity引擎中哪些功能使用了C#的反射功能?至少说出一点
属性面板
场景文件
5.请问这三行代码,运行后,在堆上会分配几个“房间”
在堆上会分配两个房间
Unity
1.Unity中Awake和Start两个生命周期函数,分别在什么时候被调用?
当脚本被动态添加到对象上的时候,以及脚本被实例化的时候会立刻调用Awake(),类似构造函数。
Start()在第一次Update之前被调用
2.Unity场景上有多个对象,都分别挂载了n个脚本。
我们如何控制不同脚本间生命周期函数Awake的执行先后顺序?
在Inspector窗口右上角处的Execution Order执行顺序按钮
3.想要在Unity中使用指针我们需要进行哪些操作?
在OtherSetting勾选Allow ‘unsafe’ code选项
使用指针必须在unsafe修饰的代码块中
4.Unity中的协同程序中yield return不同的内容,代表的含义不同
请说明下面这些yield return的含义
1.yield return 数字; 下一帧执行
2.yield return null; 下一帧执行
3.yield return new WaitForSeconds(数字); 代表过几秒执行后面的函数
4.yield return new WaitForFixedUpdate(); 等待下一个固定物理帧更新时执行
5.yield return new WaitForEndOfFrame(); 等待摄像机和GUI渲染完成后执行
6.yield break; 跳出协程
5.使用Unity协同程序进行异步加载时,底层是否会使用多线程?
可能
协同程序的原理是分时分布完成指定逻辑
在某一步骤,可以使用多线程,多线程加载完成后,再进入协同程序的下一步继续执行
面试题七
C#
-
C#中如何让一个类不能再被其他类所继承?
使用密封关键字sealed修饰该类 -
C#中使用泛型的好处是什么?
为不同类型对象的相同行为进行通用处理,提高代码复用率
避免拆箱装箱,提高性能 -
C#中元组对于我们的作用是什么?
-
请说明Thread、ThreadPool、Task分别是什么?并简单说明彼此的区别
Thread是线程
ThreadPool是线程池
Task是任务,是基于线程池的优化,可以更方便的控制线程 -
请简述GC(垃圾回收)
GC是为了防止堆内存溢出而产生的回收机制
避免:
(1)使用stringBuild 而不是string
(2) 避免频繁地new()对象,例如使用对象池
Unity
-
Unity中动态加载资源的方式有哪些?
Resource类中的相关方法加载Resource文件夹下的资源
AssetBundle类中的相关方法加载AB包中的资源 -
Unity中的光照贴图的作用是什么?
在移动端使用实时光源很耗性能,可以预先将环境光烘培到贴图上,节省渲染的一些开销,并想得到更精细的效果 -
Unity场景中有两个点连成了一条线,想要旋转这条线,应该怎么做?
将这两点的向量乘以四元数,然后将得到的新的向量和其中一个顶点相加,得到另一个点的位置 -
LOD(多细节层次)和 MipMap(纹理图)的作用是什么?
LOD是为了在离场景比较远的时候,通过对于模型的简化,节省渲染相关的开销。
MipMap是为了在不同距离下,对应于不同的纹理分辨率下进行纹理采样 -
游戏开发中,客户端和服务端交互数据,程序中常用方式是什么?
消息数据:Socket 或 HTTP
文件数据: FTP 或 HTTP
面试题八
C#
-
如果我们想为Unity中的Transform类添加一个自定义的方法,应该如何处理?
使用扩展方法,在TransformExtensions静态类中定义静态扩展方法 -
请说出using关键字的两个作用
引用命名空间
using关键字还可以用于自动释放IDisposale接口的资源,无需手动书写 try-finally块 -
C#中Dictionary不支持相同键存储 如果想要一个键对应多个值如何处理?
Dictionary<string, List> dic = new Dictionary<string, List>();
… -
请问下面代码的最终打印结果是什么?为什么?
10 10 10 10 10 10 10 10 10
- 上题中的代码,如果我们希望打印出0~9,应该如何修改代码?
int v = i;
Console.WriteLine(v);
Unity
-
Unity中如何将本地坐标转为世界坐标?
利用transform的TransformPoint()方法 -
Unity中如何计算出两个向量之间的夹角,请说出两种方式
利用点乘的结果除以两个向量的模,就得到了夹角的余弦值
private Transform m_transform;
Vector3 v1 = new Vector3(0, 0, 0);
Vector3 v2 = new Vector3(0, 1, 0);
float angle = Mathf.Acos(Vector3.Dot(v1.normalized, v2.normalized)) * Mathf.Rad2Deg;
-
请写出UGUI中两种处理异形按钮的具体方法
-
请说出Unity中如何进行数据持久化,至少说出5种方式
xml,json,二进制文件,PlayerPrefs -
在Unity中如何控制渲染优先级?(谁先渲染谁后渲染,分情况回答)
不同摄像机的时候 摄像机深度决定优先级
相同摄像机的时候 层级顺序决定优先级
相同层级的时候 层中的顺序决定优先级
相同摄像机 无排序优先级层级属性时 Shader中的RenderQueue渲染队列控制优先级
面试题九
Lua
-
请说出Lua中常用的数据类型(至少说出6种)
-
Lua中pairs和ipairs的区别
-
Lua中常用的元方法有哪些?至少说出3个原方法
-
Lua中元表的作用
-
Lua中__index和__newindex有什么作用
Unity
-
Unity中判断两个2D矩形是否相交,有几种方式?(请至少说出两种方式)
使用OnCollsionEnter2D方法来判断
使用OverlapArea检测矩形区域是否存在碰撞器 -
Unity中想要制作角色的连招功能,在制作状态机时我们一般如何处理?
状态机条件上可以添加一个Trigger类型和Int类型
Trigger条件主要用于触发动作,Int条件主要用于连招计算判断
游戏逻辑上,当攻击键输入的时候,我们需要触发动作,并且进行攻击计数,每次按键时应该重新进行攻击计数和延迟清零 -
Unity中如果想要在动作的某一时刻进行伤害检测,我们应该怎么做?(请说出两种做法)
(1)添加动画事件
(2)在切换动画一开始,进行延迟触发,延迟时间为想要触发伤害的时间(延迟可以用延迟函数,也可以用协程) -
Unity中想要制作自动寻路逻辑,我们应该怎么做?(请至少说出两种做法)
利用Unity的自动寻路组件
利用A星算法 -
游戏编辑器(比如 角色编辑器、关卡编辑器、地图编辑器等工具)的本质是什么?
数据的图形化编辑工具
面试题十
C#
8
-
内存中,堆和栈的区别是什么?
堆是由程序员手动分配释放的,如果不释放,会由操作系统进行回收。
栈是由操作系统自动分配释放的,存放函数的参数值,局部变量值。 -
TCP协议和UDP协议的区别
TCP是可靠的,一对一的,速度慢
UDP是不可靠的,无连接的,多对多的,速度快 -
TCP协议的可靠性是如何达到的?
三次握手和四次挥手 -
内存抖动指什么?如何避免内存抖动
内存抖动是指短时间内有大量的对象被创建和销毁,导致频繁地触发GC,降低运行效率。
可以利用对象池技术来减少gc的次数 -
buff 系统中,如何用一个 byte,记录多种buff状态标识
0000 0000一个buff状态占一位,总共可以记录八个buff
状态添加利用或运算,状态移除利用异或运算,判断是否存在对应的buff状态将byte和对应的状态位进行与操作,不等于0即表示有对应状态 -
Unity中使用的是左手还是右手坐标系?我们需要注意什么?
左手坐标系 -
Unity中鼠标、键盘、触屏、手柄等输入事件会在Update 之前、还是之后、还是同时执行?
之前 -
Unity中场景中一个处于激活状态的物体(场景上只有这一个物体),不能被摄像机渲染出来,可能有几种情况?(至少说出3种可能的情况)
在摄像机视口外
没有meshRender组件
摄像机不渲染其所在图层 -
Unity制作物理游戏相关功能时,我们采用哪种方式处理位移?为什么?
//transform.position
使用刚体相关API来处理位移,比如加力,改变刚体速度变量
这样在碰撞检测的时候能更准确无误 -
Unity热更新解决方案中,Lua和ILRuntime方案的本质是什么?
lua是解释性语言,无需编译,在运行时动态解释执行。lua热更新解决方案,是unity通过内置Lua虚拟机来执行Lua逻辑的。
面试题十一
C#
-
文件中保存了文本信息,但是打开后却是乱码,一般是什么原因造成的?
因为序列化和反序列化的字体格式不一样 -
C#中new关键字的作用(至少说出3种)
创建引用对象,在堆上开辟内存
泛型的约束 无参构造函数 T:new() -
同步方法和异步方法的区别是什么?
同步方法按照顺序依次执行,等该方法执行完再执行另外的方法
异步方法被调用的时候立刻返回,并返回一个新线程执行其内部逻辑,调用者不用等该方法执行完毕。异步编程是什么意思?
把一些不需要立刻返回的逻辑进行异步执行,避免线程阻塞对于我们来说,什么时候需要使用异步编程?(至少说出3种)
(1)复杂逻辑运算,寻路算法
(2)网络下载,网路通讯
(3)资源加载 -
回调函数指什么?一般在什么时候使用?(至少说出3种使用场景)
回调函数是指将函数作为参数传递给另外一个函数,当这个函数执行完毕后被调用的函数,一般以委托的形式出现 -
如何用一个int变量,记录32种状态?(注意:状态可以并存)
int在C#中占4个字节,共32位
可以按照位来记录状态,每一位表示一个状态,1表示有,0表示没有
Unity
-
请解释Unity中的Prefab是什么,以及它在游戏开发中的作用是什么?
Prefab是一种游戏预设体文件或模板,允许你在编辑器中创建一个游戏对象,并将它保存位Prefab
游戏中存在很多相似的游戏对象,在进行相关属性配置的时候,可以在Prefab的基础上进行实例化 -
在Unity中,什么是Shader?它有什么作用,以及如何编写一个基本的Shader?
shader是一种用于定义渲染对象的程序。描述了对象的外观,材质和光照效果。
(1)定义一个Pass和CGPROGRAM表示使用CG语言编写着色器代码
(2) 着色器有两个结构体,描述了输入和输出的数据格式
(3)在顶点着色器中将顶点坐标转换为裁剪空间坐标
(4)在像素着色器中,将输出固定为红色 -
在Unity中,什么是常见的性能优化技巧?请举例说明。
(1)内存
占用少,减少GC:
使用对象池,避免频繁地创建和销毁对象
减少资源占用,利用纹理压缩,减小纹理尺寸,简化模型
使用异步加载,减少游戏卡顿和内存占用
(2)CPU/GPU
减少DrawCall,通过合并网格,使用物体批处理和减少物体数量等方式来减少DrawCall
使用LOD技术,在物体离物体比较远的时候,使用低分辨率的网格
利用光照贴图来减少动态光照的数量。
-
在Unity中,什么是协程(Coroutine)?它有什么作用,以及如何使用它?
协程是可以控制代码的执行顺序,将代码分为多个阶段,可以在其中暂停和恢复代码运行,实现延迟执行,异步执行和任务管理功能。
IEnumerator DelayCoroutine(float delayTime)
{
yield return new WaitForSeconds(delayTime);
Debug.Log(" ");
} -
解释一下Unity中的渲染管线(Rendering Pipeline)是什么,如何使用它,并举例说明它在游戏开发中的应用场景。