一:什么是协同程序?
答:在主线程运行时同时开启另一段逻辑处理,来协助当前程序的执行。换句话说,开启协程就是开启一个可以与程序并行的逻辑。可以用来控制运动、序列以及对象的行为。
二:Unity3d中的碰撞器和触发器的区别?
答:碰撞器是触发器的载体,而触发器只是碰撞器身上的一个属性。
- 当
Is Trigger=false
时,碰撞器根据物理引擎引发碰撞,产生碰撞的效果,可以调用OnCollisionEnter
/Stay
/Exit
函数; - 当
Is Trigger=true
时,碰撞器被物理引擎所忽略,没有碰撞效果,可以调用OnTriggerEnter
/Stay
/Exit
函数。
如果既要检测到物体的接触又不想让碰撞检测影响物体移动或要检测一个物件是否经过空间中的某个区域这时就可以用到触发器。
三:物体发生碰撞的必要条件
答:两个物体都必须带有碰撞器Collider,其中一个物体还必须带有Rigidbody刚体。
四:请简述ArrayList
和List
的主要区别
答:
ArrayList
存在不安全类型ArrayList
会把所有插入其中的数据都当做Object来处理,而List
使用泛型,能够确保类型安全。ArrayList
的性能相对较低,因为需要进行装箱拆箱的操作,而List
由于使用泛型,避免了装箱拆箱的开销。- List是接口,
ArrayList
是一个实现了该接口的类,可以被实例化。
五:请简述GC(垃圾回收)产生的原因,并描述如何避免?
答:GC回收堆上的内存。
避免:
- 减少new产生对象的次数。
- 使用公用的对象(静态成员)。
- 将String换为StringBuilder。
六:反射的实现原理?
答:审查元数据并收集关于它的类型信息的能力。
实现步骤:
-
导入
using System.Reflection
; -
Assembly.Load("程序集")
; //加载程序集,返回类型是一个Assembly
-
得到程序集中所有类的名称
luaCopy codeforeach (Type type in assembly.GetTypes()) { string t = type.Name; }
-
获取当前类的类型
graphqlCopy code Type type = assembly.GetType("程序集.类名");
-
创建此类型实例
bashCopy code Activator.CreateInstance(type);
-
获取当前方法
goCopy code MethodInfo mInfo = type.GetMethod("方法名");
-
调用方法
csharpCopy code mInfo.Invoke(null, 方法参数);
七:简述四元数Quaternion的作用,四元数对欧拉角的优点?
答:四元数用于表示旋转。
相对欧拉角的优点:
- 能进行增量旋转。
- 避免万向锁。
- 给定方位的表达方式有两种,互为负(欧拉角有无数种表达方式)。
八:如何安全的在不同工程间安全地迁移asset数据?三种方法
答:
- 将Assets和Library一起迁移。
- 导出包package。
- 使用Unity自带的Assets Server功能。
九:OnEnable、Awake、Start运行时的发生顺序?哪些可能在同一个对象周期中反复的发生?
答:Awake–>OnEnable–>Start
OnEnable在同一周期中可以反复地发生!
十:MeshRender中material和sharedmaterial的区别?
答:
修改sharedMaterial将改变所有物体使用这个材质的外观,并且也改变储存在工程里的材质设置。
不推荐修改由sharedMaterial返回的材质。如果你想修改渲染器的材质,使用material替代。
十一:请简述ArrayList和List之间的主要区别。
(已回答在问题四的答案中)
十二:TCP/IP协议栈各个层次及分别的功能
答:(内容较长,请在Markdown编辑器中查看)
十三题:Unity提供了几种光源,分别是什么
答:四种。
- 平行光:Directional Light
- 点光源:Point Light
- 聚光灯:Spot Light
- 区域光源:Area Light
十四:简述一下对象池,你觉得在FPS里哪些东西适合使用对象池?
答:对象池就存放需要被反复调用资源的一个空间,比如游戏中要常被大量复制的对象,子弹,敌人,以及任何重复出现的对象。
在FPS游戏中,适合使用对象池的东西包括:
- 子弹
- 敌人
- 爆炸效果
- 拾取物品
十五:CharacterController和Rigidbody的区别?
答:CharacterController具有较为简单的碰撞检测,主要用于控制角色在场景中的移动。不受物理引擎影响,适合处理角色的第一人称或第三人称控制。
Rigidbody具有完全真实物理的特性,会受到物理引擎的影响。适合处理更真实的角色物理行为,如跳跃、受力等。
十六:移动相机动作在哪个函数里,为什么在这个函数里?
答:移动相机通常在LateUpdate函数中进行,因为LateUpdate是在所有的Update结束后才调用,这样可以确保相机在其他物体移动后再进行自己的移动,避免相机跟随目标物体的抖动。
十七:简述prefab的用处
答:Prefab是Unity中用于预制对象的概念。它允许你创建一个模板对象并在场景中多次复用,可以大大简化场景的创建和管理。Prefab可以包含游戏对象的组件和属性,可以在编辑器中进行设置,然后在场景中实例化多个相同或相似的对象,方便团队的协作和修改。
十八:请简述sealed关键字用在类声明时与函数声明时的作用。
答:在类声明时,sealed关键字用于防止其他类继承此类,即被标记为sealed的类不能被其他类所继承。
在函数声明时,sealed关键字用于防止派生类重写此方法,即被标记为sealed的方法不能在派生类中进行重写。
十九:请简述private,public,protected,internal的区别。
答:
- public:对任何类和成员都公开,无限制访问。
- private:仅对该类公开,在类的内部可见,对外部不可见。
- protected:对该类和其派生类公开,在类的内部和派生类中可见,对外部不可见。
- internal:只能在包含该类的程序集中访问该类,在同一个程序集中可见。
- protected internal:protected + internal,对于包含该类的程序集和其派生类可见。
二十:简述SkinnedMesh的实现原理
二十一:GPU的工作原理
简而言之,GPU的图形(处理)流水线完成如下的工作:(并不一定是按照如下顺序)
- 顶点处理:将顶点数据转换为3D图形的形状及位置关系。
- 光栅化计算:将图形上的点和线转换为像素点,形成阶梯状的连续像素点。
- 纹理帖图:将纹理映射到多边形表面,贴上相应的图片。
- 像素处理:确定每个像素的最终属性,进行光照计算等。
- 最终输出:将像素输出到显存帧缓冲区。
GPU的工作流水线是高度并行化的,能够在短时间内完成大量图形计算任务。
二十二:什么是渲染管道?
答:渲染管道是指在显示器上为了显示出图像而经过的一系列必要操作。渲染管道中的许多步骤都要将几何物体从一个坐标系中变换到另一个坐标系中,最终输出像素点。
渲染管道的主要步骤包括顶点处理、光栅化计算、纹理帖图、像素处理和最终输出。
二十三:如何优化内存?
答:有很多种方式,例如:
- 压缩自带类库;
- 将暂时不用的但以后还需要使用的物体隐藏起来而不是直接Destroy掉;
- 释放AssetBundle占用的资源;
- 降低模型的面数,降低模型的骨骼数量,降低贴图的大小;
- 使用光照贴图,使用多层次细节(LOD),使用着色器(Shader),使用预设(Prefab)。
二十四:动态加载资源的方式?
- 使用Resources.Load()方法加载资源,该方法会从Resources文件夹中加载指定的资源。
- 使用AssetBundle来动态加载资源,AssetBundle是Unity中的资源打包方式,可以根据需要从AssetBundle中加载资源。
- Unity5.1版本后可以选择使用UnityWebRequest来下载和加载远程资源。
二十五:你用过哪些插件?
这部分回答不够具体,无法提供准确的回答。
二十六:使用Unity3d实现2d游戏,有几种方式?
答:使用Unity3D实现2D游戏可以有以下几种方式:
- 使用Unity自带的GUI、UGUI系统,利用2D Sprite来实现游戏的界面和UI。
- 使用2D插件,如2DToolKit、NGUI等,这些插件提供了更丰富的2D游戏开发工具和功能。
- 将摄像机的Projection(投影)值调为Orthographic(正交投影),不考虑Z轴,这样就可以将3D场景作为2D游戏场景来实现。
二十七:在物体发生碰撞的整个过程中,有几个阶段,分别列出对应的函数
答:物体发生碰撞的整个过程中,有三个阶段:
OnCollisionEnter
:在物体发生碰撞并初次接触时调用的函数。OnCollisionStay
:在物体持续接触(碰撞)时调用的函数。OnCollisionExit
:在物体离开碰撞状态时调用的函数。
二十八:Unity3D的物理引擎中,有几种施加力的方式,分别描述出来
答:Unity3D的物理引擎中有两种常见的施加力的方式:
rigidbody.AddForce
:在刚体上施加一个持续的力。rigidbody.AddForceAtPosition
:在刚体上的指定位置施加一个持续的力。
这些方法可以用于模拟物体的运动和受力效果。
二十九:什么叫做链条关节?
答:链条关节(Hinge Joint)是Unity中的一种物理关节,它模拟了两个物体之间用链条连接在一起的情况。链条关节可以保持两个物体在一个固定距离内相互移动而不产生作用力,但在达到固定距离后就会产生拉力,从而模拟类似铰链的效果。
三十:物体自身旋转使用的函数?
答:物体自身旋转使用Transform类的Rotate方法。例如:transform.Rotate(Vector3.up, angle);
三十一:Unity3d提供了一个用于保存和读取数据的类(PlayerPrefs),请列出保存和读取整数数据的函数
答:
- 保存整数数据:
PlayerPrefs.SetInt(key, value)
; - 读取整数数据:
PlayerPrefs.GetInt(key)
;
三十二:Unity3D脚本从唤醒到销毁有着一套比较完整的生命周期,请列出系统自带的几个重要的方法。
答:Unity3D脚本从唤醒到销毁的生命周期方法包括:
Awake
:在脚本对象被创建时调用,用于初始化一些数据和组件。Start
:在脚本对象被启用时调用,用于开始一些初始化操作。Update
:在每一帧更新时调用,用于处理逻辑和更新游戏状态。FixedUpdate
:在固定时间间隔更新时调用,用于处理物理相关的计算。LateUpdate
:在所有Update方法执行完后调用,用于处理相机跟随和其他延迟逻辑。OnGUI
:在渲染GUI时调用,用于绘制GUI元素。OnDisable
:在脚本对象被禁用时调用,用于做一些清理工作。OnDestroy
:在脚本对象被销毁时调用,用于释放资源和做一些善后工作。
三十三:物理更新一般放在哪个系统函数里?
答:物理更新一般放在FixedUpdate
函数里。FixedUpdate
在固定的时间间隔内执行物理更新,与帧率无关,适合用于处理刚体物体的物理运算。在FixedUpdate
中进行物理更新可以确保物体在不同帧率下表现一致。
三十四:在场景中放置多个Camera并同时处于活动状态会发生什么?
答:在场景中放置多个Camera并同时处于活动状态,会产生多个相机的混合效果。每个相机都会根据其设置的视野范围和位置来渲染场景,最终的显示效果是多个相机的图像叠加在一起,可能会产生复杂的效果。
三十五:如何销毁一个UnityEngine.Object及其子类?
答:可以使用Unity提供的Destroy
方法来销毁一个UnityEngine.Object
及其子类。例如:Destroy(gameObject)
。
三十六:请描述游戏动画有哪几种,以及其原理?
答:游戏动画有三种常见的类型:
- 关节动画:将角色分成若干独立的部件,通过对每个部件进行关键帧动画的播放来实现整个角色的动画效果。
- 蒙皮动画:通过对角色的网格进行蒙皮绑定,将角色的骨骼与网格绑定在一起,通过对骨骼进行变换来实现角色的动画效果。
- 顶点动画:直接对角色的网格的顶点进行变换,通过改变顶点的位置来实现角色的动画效果。
三十七:Unity3D中常用的优化技巧有哪些?
答:常用的Unity3D优化技巧包括:
- 使用静态Batching和动态Batching来减少Draw Call。
- 使用Level of Detail(LOD)技术来管理不同距离的模型细节。
- 使用对象池来重复利用对象,避免频繁的创建和销毁对象。
- 使用Unity Profiler来分析性能瓶颈。
- 使用纹理压缩和图集来减小纹理内存占用。
- 限制资源的使用,避免过多的贴图和模型加载。
- 使用合适的碰撞器和避免复杂的碰撞检测。
- 合理使用动态光源和阴影,避免过多的实时光照计算。
三十八:Unity中常用的事件函数有哪些?
答:常用的Unity事件函数有:
Awake
:在对象被创建时调用。Start
:在对象第一次更新前调用。Update
:在每一帧更新时调用。FixedUpdate
:在固定时间间隔更新时调用。LateUpdate
:在所有Update方法执行完后调用。OnEnable
:在对象被启用时调用。OnDisable
:在对象被禁用时调用。OnDestroy
:在对象被销毁时调用。OnTriggerEnter
:在对象进入触发器时调用。OnTriggerExit
:在对象离开触发器时调用。OnCollisionEnter
:在对象发生碰撞并初次接触时调用。OnCollisionExit
:在对象离开碰撞状态时调用。
三十九:使用Unity开发安卓游戏时的性能优化有哪些?
答:在使用Unity开发安卓游戏时,常见的性能优化包括:
- 使用Texture Compression:选择适当的纹理压缩格式,减小纹理内存占用。
- 减少Draw Call:合并网格、使用静态Batching和动态Batching,减少绘制调用。
- 使用Level of Detail(LOD)技术:根据距离显示不同精度的模型,减少多边形数量。
- 使用Object Pooling:重复利用对象,避免频繁的创建和销毁对象。
- 简化碰撞器:合理使用简单的碰撞器,避免复杂的碰撞检测。
- 减小纹理大小:优化贴图大小,减少内存占用。
- 优化脚本:减少脚本的运算量,避免使用不必要的资源。
- 使用适当的特效:减少过多的粒子效果和动画,合理使用光照和阴影。
- 使用Unity Profiler:分析性能瓶颈,找到优化的重点。
四十:LOD是什么,优缺点是什么?
答:LOD(Level of detail)多层次细节,是最常用的游戏优化技术。它按照模型的位置和重要程度决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。
四十一:两种阴影判断的方法、工作原理。
本影和半影:
本影:景物表面上那些没有被光源直接照射的区域(全黑的轮廓分明的区域)。
半影:景物表面上那些被某些特定光源直接照射但并非被所有特定光源直接照射的区域(半明半暗区域)
工作原理:从光源处向物体的所有可见面投射光线,将这些面投影到场景中得到投影面,再将这些投影面与场景中的其他平面求交得出阴影多边形,保存这些阴影多边形信息,然后再按视点位置对场景进行相应处理得到所要求的视图(利用空间换时间,每次只需依据视点位置进行一次阴影计算即可,省去了一次消隐过程)
四十二: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几个过程。
四十三:MipMap是什么,作用?
答:MipMapping:在三维计算机图形的贴图渲染中有常用的技术,为加快渲染进度和减少图像锯齿,贴图被处理成由一系列被预先计算和优化过的图片组成的文件,这样的贴图被称为MipMap。
四十四:请描述Interface与抽象类之间的不同
答:抽象类表示该类中可能已经有一些方法的具体定义,但接口就是公共只能定义各个方法的界面 ,不能具体的实现代码在成员方法中。
类是子类用来继承的,当父类已经有实际功能的方法时该方法在子类中可以不必实现,直接引用父类的方法,子类也可以重写该父类的方法。
实现接口的时候必须要实现接口中所有的方法,不能遗漏任何一个。
四十五:下列代码在运行中会产生几个临时对象?
csharpCopy codestring a = new string("abc");
a = (a.ToUpper() + "123").Substring(0, 2);
答:其实在C#中第一行是会出错的(Java中倒是可行)。应该这样初始化:string b = new string(new char[]{'a','b','c'});
四十六:下列代码在运行中会发生什么问题?如何避免?
csharpCopy codeList ls = new List(new int[] { 1, 2, 3, 4, 5 });
foreach (int item in ls)
{
Console.WriteLine(item * item);
ls.Remove(item);
}
答:会产生运行时错误,因为foreach是只读的。不能一边遍历一边修改。
四十七:.Net与Mono的关系?
答:Mono是.NET的一个开源跨平台工具,类似于Java虚拟机,可以让.NET程序在跨平台上运行。.NET主要运行在Windows平台上,而Mono可以运行在Linux、Unix、Mac OS等平台。
四十八:简述Unity3D支持的作为脚本的语言的名称
答:Unity的脚本语言基于Mono的.NET平台上运行,可以使用.NET库,支持以下语言:
- C#
- JavaScript(UnityScript)
- Boo
四十九:Unity3D是否支持写成多线程程序?如果支持的话需要注意什么?
答:Unity3D并不直接支持多线程编程。除主线程之外的线程无法访问Unity3D的对象、组件和方法。如果需要进行多线程编程,可以使用C#中的Thread类等相关API,但需要注意以下几点:
- 确保线程安全,避免多个线程同时访问相同资源而导致竞态条件。
- 使用锁(lock)关键字来保护临界区,避免线程冲突。
- 尽量避免在多线程中直接访问Unity3D对象和组件,可以通过主线程来进行交互,例如使用Unity的协程(Coroutine)来在主线程中执行需要访问Unity对象的操作。
五十:Unity3D的协程和C#线程之间的区别是什么?
答:协程(Coroutine)和C#线程有以下区别:
- 多线程程序同时运行多个线程,而在任一指定时刻只有一个协程在运行,并且这个正在运行的协程只在必要时才被挂起。
- 除主线程之外的线程无法访问Unity3D的对象、组件、方法等,而协程可以在主线程中执行需要访问Unity对象的操作。
- 在多线程中需要使用锁来保护临界区,避免竞态条件,而协程不需要显式地处理锁,更加方便和简洁。
- 协程适用于需要在指定时间间隔执行任务,例如延时操作、动画序列控制等。
五十一:U3D中用于记录节点空间几何信息的组件名称,及其父类名称
答:节点空间几何信息的组件名称是Transform,其父类名称是Component。
五十二:向量的点乘、叉乘以及归一化的意义?
答:
- 点乘:描述了两个向量的相似程度,结果越大两向量越相似,还可表示投影。
- 叉乘:得到的向量垂直于原来的两个向量。
- 归一化:用在只关系方向,不关心大小的时候,将向量的长度缩放为1,方便处理方向信息。
五十三:矩阵相乘的意义及注意点
答:矩阵相乘用于表示线性变换,如旋转、缩放、投影、平移、仿射等操作。
注意点:
- 矩阵相乘的顺序非常重要,AB ≠ BA,通常要根据具体应用场景确定相乘顺序。
- 矩阵相乘的结果不满足交换律,即A * B ≠ B * A,要注意顺序的正确性。
- 矩阵相乘的维度必须满足规则,即A的列数等于B的行数才能相乘。
五十四:为何大家都在移动设备上寻求U3D原生GUI的替代方案
答:U3D原生GUI在移动设备上存在一些问题,包括不美观、OnGUI的性能开销较大以及使用不方便等。为了提升用户体验和性能,很多开发者寻求使用其他替代方案,如:
- 使用Unity的UI系统(Canvas、UGUI)来构建更美观和灵活的用户界面。
- 使用第三方GUI库或自定义GUI系统来满足特定需求,提供更高效、易用的GUI解决方案。
五十五:请简述如何在不同分辨率下保持UI的一致性
答:保持UI在不同分辨率下的一致性需要考虑以下几点:
- 使用Canvas Scaler组件:Unity的Canvas Scaler组件可以帮助实现UI的自适应,通过调整UI元素的缩放、位置和大小来适应不同的屏幕分辨率。
- 使用Anchors和Layout Groups:使用Anchors可以锚定UI元素,确保它们在不同屏幕分辨率下位置的正确性。同时,使用Layout Groups来自动调整UI元素的大小和位置,保持UI的布局一致性。
- 使用Resolution Independence单位:避免使用像素作为UI元素的单位,而是使用Resolution Independence单位,如百分比或参考分辨率的比例,以便在不同分辨率下保持UI的一致性。
五十六:为什么dynamic font在unicode环境下优于static font
答:在Unicode环境下,使用dynamic font相对于static font的优势在于:
- dynamic font支持更多的字符集和文字样式,能够正确显示不同语言的文字和特殊字符。
- static font需要预先生成所有字符的纹理,当需要支持亚洲语言或较大的字体时,其纹理会非常大,导致内存消耗过多。
- dynamic font可以根据需要动态生成字符的纹理,只生成当前需要显示的字符的纹理,从而减少内存占用。
五十七:当一个细小的高速物体撞向另一个较大的物体时,会出现什么情况?如何避免?
答:当一个细小的高速物体撞向另一个较大的物体时,可能会出现穿透(collision penetration)的情况,即细小的物体会穿过较大的物体并继续运动,而不是正确地停止在撞击的表面。
为了避免穿透问题,可以采取以下措施:
- 使用合适的物理引擎:选择支持连续碰撞检测的物理引擎,例如Unity中的PhysX引擎。连续碰撞检测可以减少穿透问题的发生。
- 增加碰撞检测频率:通过增加碰撞检测的频率,可以更精确地检测碰撞,减少穿透的可能性。
- 调整物体的形状和质量:合理调整物体的碰撞形状和质量,使其更符合实际情况,减少穿透问题的发生。
五十八:请简述OnBecameVisible
及OnBecameInvisible
的发生时机,以及这一对回调函数的意义?
答:OnBecameVisible
和OnBecameInvisible
是MonoBehaviour
类中的回调函数,在Unity中用于处理物体的可见性变化。
OnBecameVisible
:当物体的渲染器进入摄像机的可见范围时,该函数被调用。它通常用于处理物体进入视野时需要执行的操作,比如开始播放动画或加载一些资源。OnBecameInvisible
:当物体的渲染器从摄像机的可见范围中消失时,该函数被调用。它通常用于处理物体离开视野时需要执行的操作,比如停止播放动画或释放一些资源。
这对回调函数的意义在于,它们提供了一种在物体可见性发生变化时执行相应操作的机制,帮助开发者优化游戏性能和资源管理。例如,当一个物体不可见时,可以停止其不必要的渲染和计算,从而提高游戏的性能。
五十九:什么叫动态合批?跟静态合批有什么区别?
- 答: 如果动态物体共用着相同的材质,那么Unity会自动对这些物体进行批处理。动态批处理操作是自动完成的,并不需要你进行额外的操作。
- 区别:
- 动态批处理是自动完成的,不需要额外操作,而且物体是可以移动的,但是有一些限制。
- 静态批处理:具有更高的自由度,限制较少,但可能会占用更多内存,经过静态批处理后的所有物体都不可以再移动了。
六十:简述StringBuilder和String的区别?
-
答:
String
是字符串常量。StringBuilder
是字符串变量,线程安全。String
是字符串变量,线程不安全。
String
类型是不可变的对象,每次对String
进行改变时都需要生成一个新的String
对象,指针指向新对象,这可能导致效率较低。而StringBuilder
对象在做字符串连接操作时是在原字符串上进行修改,改善了性能,特别在连接操作频繁的情况下,建议使用StringBuilder
对象。
六十一:什么是LightMap?
- 答:
LightMap
是指在三维软件中实现预先设置好光照,并将场景中各表面的光照输出到贴图上,然后通过引擎贴到场景上,使物体具有逼真的光照效果。
六十二:Unity和cocos2d的区别
- 答:
- Unity3D支持C#、JavaScript等编程语言,cocos2d-x支持C++、HTML5、Lua等。
- cocos2d是开源且免费的,而Unity3D有免费版本,但也有收费版本。
- Unity3D支持多平台游戏开发,包括iOS、Android、Flash、Windows、Mac、Wii等,cocos2d-x支持iOS、Android、Windows Phone等。
六十三:C#和C++的区别?
- 答: 简单来说,C#与C++最重要的区别在于它们的面向对象特性。C#是一种完全面向对象的语言,而C++不是。此外,C#是基于IL中间语言和.NET Framework CLR,在可移植性、可维护性和健壮性方面都有很大的改进。C#的设计目标是开发快速稳定可扩展的应用程序。同时,C#通过Interop和P/Invoke等机制可以完成一些底层操作。
六十四:Unity3D Shader分哪几种,有什么区别?
-
答: Unity3D中的Shader主要分为以下几种:
- 表面着色器(Surface Shader)
- 顶点片段着色器(Vertex/Fragment Shader)
- 固定功能管线着色器(Fixed Function Shader)
区别:
- 表面着色器是较高级的抽象层次,使用简洁的方式实现复杂的着色效果,并能在前向渲染和延迟渲染模式下正常工作。
- 顶点片段着色器提供更灵活的实现方式,但需要编写更多的代码,并且不太容易与Unity渲染管线完美集成。
- 固定功能管线着色器可作为前两种着色器的备用选择,当硬件无法支持高级着色效果时,可以使用固定功能管线着色器绘制基本内容。
六十五:strcpy函数的原型和实现的细节?
-
原型:
cCopy code char * strcpy(char * strDest, const char * strSrc);
-
实现:
cCopy codechar * strcpy(char * strDest, const char * strSrc) { if ((strDest == NULL) || (strSrc == NULL)) // 检查指针的有效性 throw "Invalid argument(s)"; // 抛出异常 char * strDestCopy = strDest; // 保存strDest的原始值 while ((*strDest++ = *strSrc++) != '\0'); // 复制字符串,直到遇到'\0' return strDestCopy; // 返回strDest的原始值 }
说明:
- 错误的做法:没有检查指针的有效性、直接返回错误的类型、使用
new
创建字符串而忽略了内存管理等问题。 - 正确的实现应该检查指针的有效性、使用
const
限定输入参数、抛出异常而不是返回错误类型、保存strDest
的原始值以支持链式表达式,并在循环结束时为strDest
添加终止符\0
。
- 错误的做法:没有检查指针的有效性、直接返回错误的类型、使用
六十六:C#中四种访问修饰符是哪些?各有什么区别?
在C#中,有四种访问修饰符:
public
: 存取不受限制,可以在任何位置访问。private
: 只有包含该成员的类可以访问,对外部不可见。internal
: 只有当前命名空间可以访问。protected
: 只有包含该成员的类以及派生类可以访问,对外部不可见。
访问修饰符的区别在于它们控制了成员的访问范围。public
允许在任何位置访问,而private
和protected
限制了访问范围,只有包含该成员的类及其派生类才能访问。internal
允许在当前命名空间内访问,对其他命名空间是不可见的。
六十七:Heap与Stack有何区别?
- Heap是堆,Stack是栈。
- Stack的空间由操作系统自动分配和释放,Heap的空间是手动申请和释放的,常用
new
关键字来分配。 - Stack空间有限,Heap的空间相对较大。
Stack用于存储方法调用时的局部变量和方法返回地址,由于栈空间有限,所以大小有限制。而Heap用于存储动态分配的内存,大小较大,需要手动管理内存的申请和释放。
六十八:值类型和引用类型有何区别?
答:
- 值类型的数据存储在内存的栈中;引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的地址。
- 值类型存取速度快,引用类型存取速度慢。
- 值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用。
- 值类型继承自System.ValueType,引用类型继承自System.Object。
- 栈的内存分配是自动释放;而堆在.NET中会有GC来释放。
- 值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。
- 值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中。
六十九:结构体和类有何区别?
答:结构体是一种值类型,而类是引用类型。值类型用于存储数据的值,引用类型用于存储对实际数据的引用。结构体适用于较小的数据结构,而类适用于复杂的对象和行为。结构体在栈上分配内存,而类在堆上分配内存。结构体是通过复制进行传递,类是通过引用进行传递。
七十:请写出求斐波那契数列任意一位的值得算法
csharpCopy codestatic int Fn(int n)
{
if (n <= 0)
{
throw new ArgumentOutOfRangeException();
}
if (n == 1 || n == 2)
{
return 1;
}
return checked(Fn(n - 1) + Fn(n - 2)); // when n>46 memory will overflow
}
七十一:ref参数和out参数是什么?有什么区别?
答:ref参数和out参数都用于方法的参数传递,允许将参数按引用传递而不是按值传递。区别在于:
- ref参数在传递给方法之前必须初始化,而out参数无需初始化,方法内部必须为out参数赋值。
- ref参数在传递给方法时需要先初始化,而out参数在方法内部必须赋值。
- ref参数传递的是变量的引用,所以方法内部对ref参数的修改会反映在调用方法的地方;而out参数也是传递引用,但是调用方法之前out参数不需要初始化,所以常用于返回多个值的情况。
七十二:C#的委托是什么?有何用处?
答:委托是一种类型安全的函数指针,允许将方法作为参数传递、存储和调用。它允许将方法的引用封装在委托对象内,然后可以在运行时动态地调用这些方法,而不需要在编译时确定要调用的具体方法。委托用于实现事件、回调函数、多播委托等,提供了更加灵活和松耦合的编程方式。
七十三:协同程序的执行代码是什么?有何用处,有何缺点?
答:
csharpCopy codefunction Start() {
print("Starting " + Time.time);
StartCoroutine(WaitAndPrint(2.0));
print("Before WaitAndPrint Finishes " + Time.time);
}
function WaitAndPrint(waitTime : float) {
yield WaitForSeconds (waitTime);
print ("WaitAndPrint "+ Time.time );
}
协同程序用于异步执行代码,可以在任意位置使用yield语句暂停协程的执行,并在一定条件下恢复协程向下执行。它适用于需要延迟执行或需要分阶段执行的操作。然而,协程并非真正的线程,可能会在执行过程中发生堵塞。同时,如果协程嵌套过多,可读性和维护性可能会受影响。
七十四:什么是里氏代换原则?
答:里氏代换原则(Liskov Substitution Principle,LSP)是面向对象设计的基本原则之一。它指出,任何基类可以出现的地方,子类一定可以出现,即子类应该能够替换其基类而不影响程序的正确性。这样的设计使得代码更加灵活、可扩展,增强了代码的可维护性。
七十五:Mock和Stub有何区别?
答:Mock和Stub都是测试中常用的测试辅助工具。
- Mock关注行为验证,用于验证被测试对象与其他对象之间的交互情况。Mock通常用于单元测试中,通过模拟对象的行为来验证代码的正确性。
- Stub关注状态验证,用于模拟外部依赖的状态。Stub用于在测试环境中提供虚拟的、固定的数据,使得被测试对象能够在不依赖真实外部依赖的情况下进行测试。
七十六:概述序列化:
答:序列化是将对象转换为可存储或传输的格式的过程,使得对象可以在不同的平台或程序之间进行数据交换。在C#中,可以使用.NET框架提供的序列化机制,如BinaryFormatter
、XMLSerializer
、JSONSerializer
等,来实现对象的序列化和反序列化操作。
七十七:堆和栈的区别?
答:栈和堆都是用来存储数据的内存区域,它们在内存分配和管理上有一些不同:
- 栈:存储局部变量和方法调用的上下文信息。栈是自动分配和释放内存的,变量的生命周期由它们的作用域决定,当作用域结束时,栈上的数据会被自动销毁。栈的内存管理效率高,但容量有限。
- 堆:存储动态分配的对象和数据,对象的生命周期由程序员控制。堆的内存管理相对复杂,需要显式地分配内存,并在不再使用时进行手动释放。堆的容量较大,但内存分配和释放的开销相对较大。
七十八:概述c#中代理和事件?
答:代理(Delegate)是一种类型安全的函数指针,可以将方法引用封装在委托对象内,并在运行时动态地调用这些方法。代理常用于实现回调机制、事件处理等。
事件(Event)是委托的特殊用法,用于实现发布-订阅模式。事件允许对象发布某个特定动作或状态的更改,并允许其他对象订阅并对其做出响应,实现了松耦合的事件驱动编程。
七十九:C#中的排序方式有哪些?
答:C#中常用的排序方式有:
- 选择排序(Selection Sort)
- 冒泡排序(Bubble Sort)
- 快速排序(Quick Sort)
- 插入排序(Insertion Sort)
- 希尔排序(Shell Sort)
- 归并排序(Merge Sort)
八十:射线检测碰撞物的原理是?
答:射线检测是一种在3D世界中用于检测碰撞的方法。它通过从一个点向一个方向发射一条无限长的线(射线),并检查这条射线是否与其他物体发生碰撞。当射线与物体相交时,可以获取碰撞点的信息,从而实现碰撞检测和响应。
八十一:客户端与服务器交互方式有几种?
答:客户端与服务器交互方式主要有两种:
- Socket通常也称作"套接字",实现了服务器和客户端之间的物理连接,并进行数据传输。主要有UDP和TCP两个协议,处于网络协议的传输层。
- HTTP协议用于在客户端和服务器之间传输数据。常见的方式有HTTP的POST和GET请求,以及基于HTTP协议的SOAP协议(Web服务)。
八十二:Unity和Android与iOS如何交互?
Unity与Android和iOS的交互主要通过使用Native插件(Native Plugin)来实现。Unity中可以编写C#代码调用Native插件中的原生方法,从而实现Unity与Android/iOS的交互。同时,Android和iOS平台上的原生代码也可以调用Unity中的C#方法。
八十三:Unity中,照相机的Clipping Planes的作用是什么?调整Near、Far两个值时,应该注意什么?
答:照相机的Clipping Planes用于指定相机视锥体的远近裁剪平面。Near表示近裁剪平面,Far表示远裁剪平面。只有在这个视锥体内的物体才会被渲染。
调整Near和Far两个值时应该注意:
- Near和Far的值应合理设置,确保视锥体包含场景中的所有物体,避免物体在相机视野内消失或出现裁剪异常。
- 过大或过小的Near和Far值可能导致深度精度问题,影响渲染的效果和准确性。
八十四:如何在Unity3D中查看场景的面数、顶点数和Draw Call数?如何降低Draw Call数?
答:在Unity中查看场景的面数、顶点数和Draw Call数,可以在Game视图右上角点击Stats来查看性能统计信息。
降低Draw Call数的技术是Draw Call Batching,具体方法包括:
- 合并网格:将多个静态物体合并成一个网格,减少Draw Call数。
- 使用Atlas纹理:将多个小纹理合并到一个大纹理中,减少纹理切换导致的Draw Call数。
- 避免使用大量透明物体:大量透明物体会增加Draw Call数,应尽量避免过多的透明物体。
- 使用GPU Instancing:对于相同的材质和网格,开启GPU Instancing可以减少Draw Call数。
八十五:请问alpha test在何时使用?能达到什么效果?
答:alpha test通常用于对透明度(alpha值)进行测试,根据测试结果决定是否丢弃像素。当alpha test的测试条件满足时,保留像素;否则丢弃像素。alpha test能够实现一些特殊效果,如裁剪或形状的特定区域,创建透明贴图的透明效果等。使用alpha test可以减少透明物体的绘制开销,提高渲染性能。
八十六:UNITY3d在移动设备上的一些优化资源的方法
答:
- 使用assetbundle,实现资源分离和共享,将内存控制到200m之内,同时也可以实现资源的在线更新
- 降低顶点数到8万以下,fps稳定到了30帧左右,顶点数对渲染无论是cpu还是gpu都是压力最大的贡献者
- 只使用一盏动态光,不使用阴影,不使用光照探头
- 粒子系统是CPU上的大头,剪裁粒子系统
- 合并同时出现的粒子系统
- 自己实现轻量级的粒子系统
- animator也是一个效率奇差的地方,把不需要跟骨骼动画和动作过渡的地方全部使用animation,控制骨骼数量在30根以下
- animator出视野不更新,删除无意义的animator
- animator的初始化很耗时,粒子上能不能尽量不用animator
- 除主角外都不要跟骨骼运动apply root motion
- 绝对禁止掉那些不带刚体带包围盒的物体(static collider )运动
- NUGI的代码效率很差,基本上runtime的时候对CPU的贡献和render不相上下,每帧递归的计算finalalpha改为只有初始化和变动时计算,去掉法线计算,不要每帧计算viewsize和windowsize,filldrawcall时构建顶点缓存使用array.copy,代码剪裁:使用strip level,使用.net2.0 subset
- 尽量减少smooth group
- 给美术定一个严格的经过科学验证的美术标准,并在U3D里面配以相应的检查工具
八十七:四元数有什么作用?
答:四元数用于对旋转角度进行计算。
八十八:将Camera组件的ClearFlags选项选成Depth only是什么意思?有何用处?
答:ClearFlags选项选成Depth only表示只清除深度缓冲区,不清除颜色缓冲区。这在对象不被裁剪且只需要绘制深度信息时使用。
八十九:如何让已经存在的GameObject在LoadLevel后不被卸载掉?
答:在GameObject的脚本中使用Awake()函数,并添加以下代码:
csharpCopy codevoid Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
九十:在编辑场景时将GameObject设置为Static有何作用?
答:将GameObject设置为Static将会剔除(或禁用)网格对象当这些部分被静态物体挡住而不可见时。因此,在你的场景中所有不会动的物体都应该标记为Static。
九十一:有A和B两组物体,有什么办法能够保证A组物体永远比B组物体先渲染?
答:将A组物体的渲染队列设置大于B组物体的渲染队列。
九十二:将图片的TextureType选项分别选为Texture和Sprite有什么区别?
答:Texture用于模型贴图使用,Sprite用作UI精灵使用。
九十三:问一个Terrain,分别贴3张、4张、5张地表贴图,渲染速度有什么区别?为什么?
答:渲染速度没有区别,因为不管贴图数量多少,Terrain只进行一次渲染。
九十四:什么是DrawCall?DrawCall高了又什么影响?如何降低DrawCall?
答:在Unity中,每次引擎准备数据并通知GPU的过程称为一次Draw Call。Draw Call越高对显卡的消耗就越大。
降低DrawCall的方法:
- 使用Dynamic Batching
- 使用Static Batching
- 将高级特性的Shader降级为统一的低级特性的Shader
九十五:实时点光源的优缺点是什么?
答:实时点光源可以有cookies(带有alpha通道的立方图Cubemap纹理),但是是最耗费资源的光源。
九十六:Unity的Shader中,Blend SrcAlpha OneMinusSrcAlpha这句话是什么意思?
答:这是Alpha混合的设置。公式为:最终颜色 = 源颜色 * 源透明值 + 目标颜色 * (1 - 源透明值)。
九十七:简述水面倒影的渲染原理
答:水面倒影通过对水面的贴图纹理进行扰动产生波光玲珑的效果。使用Shader可以在像素级别上对水面进行扰动,效果细腻,需要的顶点少,速度快。
九十八:简述NGUI中Grid和Table的作用?
答:Grid和Table都用于对子物体进行排序和定位,但是有些不同。Grid会自动将子物体按照指定的规则排列,而Table则可以在两个方向上进行自由排列。
九十九:请简述NGUI中Panel和Anchor的作用
答:
- Panel可以看作是收集和管理其下所有widget组件的容器,通过widget的geometry创建实际的draw call。没有Panel,所有东西都不能够被渲染出来,可以把UIPanel当做Renderer。
- Anchor用于将UI控件依附到屏幕的角落或边缘,并提供一个half-pixel偏移量,确保在Windows系统上精确显示位置。只有挂载到一个对象上,它会影响该对象的子控件。
一百:
能用 foreach
遍历访问的对象需要实现 IEnumerable
接口或声明 GetEnumerator
方法的类型。
答案: IEnumerable
;GetEnumerator