Unity 部分

Unity协程(Coroutine)、线程(Thread)和进程(Process)三者的区别

  • 一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程
  • 线程是由操作系统内核进行调度,可以实现真正的并行
  • Unity只使用了一个线程协程是一种”伪线程“。不用考虑同步和锁的问题
    (例如:当你调用 StartCoroutine(MyCoroutine()) 时,Unity会记住这个方法的当前状态。执行到 yield return XXX 时,它会暂停执行并将控制权交还给主线程。Unity会在 yield return 指定的条件满足后(如下一帧、多少秒后、某个异步操作完成),在主线程的特定时间点继续执行这个方法后续的代码。)
  • 每次只有一个协程工作,而其他协成处于休眠状态。协程实际上是在一个线程中,协程适合对某个任务进行分时处理,协程可以访问和使用unity的所有方法和component。
  • 简单记:协程用于“等待”和“分帧”,线程用于“干活”(重活、慢活)。
  • 线程的典型使用场景:
    进行复杂的数学计算(如路径规划、网格处理)。
    从网络下载资源(使用 System.Net.Http.HttpClient 等)。
    加载或保存大型文件到磁盘。

本地坐标系 世界坐标系分别是什么

  • 世界坐标系:世界坐标是指物体在场景中的坐标,当某个物体没有父物体时,它的position即为世界坐标的position,rotation同理;本地坐标是物体相对于它的父物体的坐标而言。
  • 本地坐标系:当某个物体有父物体时,它的inspector栏transform中的position实际是localposition,即本地坐标。

U3D中用于记录节点空间几何信息的组件名称,及其父类名称

  • Transform 父类是 Component

OnEnable、Awake、Start运行时的发生顺序 哪些可能在同一个对象周期中反复的发生

  • Awake –>OnEnable->Start。
  • OnEnable在同一周期中可以反复地发生。

简述prefab的用处

  • 在游戏运行时实例化,prefab相当于一个模板,对你已经有的素材、脚本、参数做一个默认的配置,以便于以后的修改,同时prefab打包的内容简化了导出的操作,便于团队的交流。

unity的摄像机有几种投射方式,区别是什么

  • 有透视和正交,
    • 透视投影:与现实世界观察物体一样, 有近大远小的效果, 这种投影更加真实,所视范围是一个椎体
    • 正交投影:正交投影属于平行投影, 投影的内容不会出现近大远小的效果,所视范围是一个长方体的

简述刚体的作用

  • 刚体是激活物体物理属性的主要组件。绑定刚体的游戏对象,在unity中将受到物理作用

继承monoBehavior的脚本有哪几种更新函数,区别是什么

  • 按照执行顺序来:
    • :FixedUpdate() 固定更新函数,游戏运行的过程中,每一帧的处理时间是不固定的,可以在在unity中设置(Edit -> Project Settings -> Time 菜单中打开时间管理器,然后调整"Fixed Timestep"的值。)
    • :update(), 游戏运行每一帧都会调用的函数,用于游戏正常的更新逻辑
    • :LateUpdate() 延迟更新函数,它是在所有Update结束后才调,比较适合用于命令脚本的执行

在场景中放置多个Camera并同时处于活动状态会发生什么

  • 游戏界面可以看到很多摄像机的混合,各个摄像机的画面按照预先设定的层级和位置关系叠加起来

在物体发生碰撞的整个过程中,有几个阶段,分别列出对应的函数

  • 三个阶段
    • 1.OnCollisionEnter
    • 2.OnCollisionStay
    • 3.OnCollisionExit

Unity3d脚本从唤醒到销毁有着一套比较完整的生命周期,请列出系统自带的几个重要的方法。

  • Awake——>OnEnable——>Start——>FixedUpdate——>Update——>LateUpdate——>OnGUI——>OnDisable——>OnDestroy

请描述游戏动画有哪几种,以及其原理

  • 主要有关节动画、骨骼动画、单一网格模型动画(关键帧动画)。
  • 关节动画:把角色分成若干独立部分,一个部分对应一个网格模型,部分的动画连接成一个整体的动画,角色比较灵活;
  • 单一网格模型动画由一个完整的网格模型构成,在动画序列的关键帧里记录各个顶点的原位置及其改变量,然后插值运算实现动画效果,角色动画较真实。
  • 骨骼动画,广泛应用的动画方式,骨骼按角色特点组成一定的层次结构,有关节相连,可做相对运动,皮肤作为单一网格蒙在骨骼之外,决定角色的外观;

碰撞器有哪几种类型:

  • 网格碰撞器,盒子碰撞器,胶囊碰撞器,球型碰撞器,地形碰撞器

如何销毁一个UnityEngine.Object及其子类

  • 使用Object.Destroy()方法;

MipMap是什么,有什么作用

  • MipMapping:在三维计算机图形的贴图渲染中有常用的技术,为加快渲染进度和减少图像锯齿,贴图被处理成由一系列被预先计算和优化过的图片组成的文件,这样的贴图被称为MipMap。

StreamingAssets和Resources的功能异同。

  • 相同点:

    1. 都是Unity3d保留的目录。
  • 不同点:

    1. Resources里的内容在打包的时候会加密和压缩。而streamingassets里的资源会直接打包,不做处理。
    2. Resource里的内容需要用Resources.Load来加载而Streamingassets则需要用UnityWebRequest来加载。Assetbundle文件通常也是放在这个文件夹来进行加载。
  • 注意

    • 首选 UnityWebRequest + async/await 模式
    • 避免使用已废弃的 WWW 类
    • 注意 Android 平台的路径特殊处理
    • 对于复杂资源,使用 AssetBundle

简述ScriptableObject的作用。

  • scriptableobject是Unity3d提供的一种可序列化脚本。通过ScriptableObject.CreateInstance()可以序列化出一个资源文件。多用来存储数据,方便快捷。

GameObject.Find()和Transform.Find的区别

  • GameObject.Find()是在场景中所有预设中查找。Transform.Find只在子节点中查找。

C# 部分

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

  • public: 对任何类和成员都公开,无限制访问
  • private: 仅对该类公开
  • protected:对该类和其派生类公开
  • internal: 只能在包含该类的程序集中访问该类

C#的委托是什么 有何用处

  • 委托类似于一种安全的指针引用,在使用它时是当做类来看待而不是一个方法,相当于对一组方法的列表的引用。用处:使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与C或C++中的函数指针不同,委托是面向对象,而且是类型安全的。

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

  • HTTP请求:使用UnityWebRequest或WWW类进行HTTP通信,通常用于RESTful API调用。
  • Socket编程:使用TCP或UDP协议进行底层网络通信,适用于实时性要求高的应用。
  • WebSocket:提供全双工通信通道,适合实时交互,如游戏中的聊天或实时更新。
  • 网络库:如MLAPI(Mid-Level API)或第三方库如Mirror、Photon等,这些库提供了更高级的网络抽象。
  • RPC(远程过程调用):在一些网络库中,如UNet或Photon,允许直接调用远程服务器上的函数。

abstract class(抽象类)和interface(接口)有什么区别

  • 答: 相同点
    1. 都不能被直接实例化,都可以通过继承实现其抽象方法。
    2. 都是面向抽象编程的技术基础,实现了诸多的设计模式。
  • 不同点
    1. 接口支持多继承,一个类可以实现多个接口。抽象类不能实现多继承,一个类只能继承一个抽象类。
    2. 接口只能定义抽象规则;抽象类既可以定义规则,还可能提供已实现的成员。
    3. 接口是一组行为规范,让不同的类能够被统一对待,减少代码间的依赖关系;抽象类是一个不完全的类,着重族的概念。
    4. 接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法;抽象类可以定义字段、属性、包含有实现的方法。
    5. 接口可以作用于值类型和引用类型;抽象类只能作用于引用类型。

C# String类型比stringBuilder类型的优势是什么

  • 如果是处理字符串的话,用string中的方法每次都需要创建一个新的字符串对象并且分配新的内存地址,而stringBuilder是在原来的内存里对字符串进行修改,所以在字符串处理方面还是建议用stringBuilder这样比较节约内存。但是string 类的方法和功能仍然还是比stringBuilder类要强。

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

  • 对象池就存放需要被反复调用资源的一个空间,比如游戏中要常被大量复制的对象,子弹,敌人,以及任何重复出现的对象。

你怎么样能迅速找到某一个UI控件

  • 分情况处理,这是一个优化策略的题目。首先如果这个控件我在awake的时候能知道,那么我会把它存成一个private变量,代码中使用的时候直接使用即可。如果不行,这个子物体是动态生成的(他可能有或者没有),那尽量使用FindChild,得到之后加以判断。如果不得不全局找一个东西(比如找到角色物体),才会用Find。

Unity3d与android交互的方式是什么

  • u3d调android 采用AndroidJavaClass 和AndroidJavaObject来实现。(Android调Unity3d 采用UnityPlayer.UnitySendMessage(“Main Camera”,“messgae”,edit.getText().toString());)

Unity3d中如何在编辑器中增加一个自定义menu

  • 答:
    1. 创建c#脚本,引入unityeditor命名空间
    2. 实现一个静态函数,在函数上加[menuitem]的属性。
    3. 脚本放在Editor文件夹下。

Unity3d中shader主要有几种类型

类型 编写方式 灵活性 性能 学习难度 适用场景
表面着色器 代码(抽象) 标准PBR物体,多光源场景
顶点/片段着色器 代码(直接) 极高 自定义特效,NPR,性能优化
固定函数着色器 配置命令 极低 低(但兼容老硬件) 中(已过时) 兼容古老硬件(已淘汰)
Shader Graph 可视化节点 中-高 高(依赖生成代码) 非常容易 URP/HDRP项目,快速迭代,美术创作

请述说你对渲染管线的流程及理解
概念层面:渲染管线流程主要 分为三步
应用阶段(Application Stage)——>几何阶段(Geometry Stage)——> 光栅化阶段(Rasterization Stage)

阶段 执行位置 主要任务
1️⃣ 应用阶段(Application Stage) CPU 场景逻辑、剔除、排序、提交绘制命令
2️⃣ 几何阶段(Geometry Stage) GPU 顶点变换、图元装配、裁剪、投影变换
3️⃣ 光栅化阶段(Rasterization Stage) GPU 三角形到片元转换、片元着色、测试与混合、输出到帧缓冲
三步法对应的是概念层面的“管线阶段”,是 GPU 的硬件抽象。

Unity / 实际游戏引擎的五步细化(工程模型)

Unity 工程阶段 对应图形学阶段 说明
1️⃣ 应用阶段 应用阶段 CPU执行剔除、排序、渲染队列提交
2️⃣ 几何阶段 几何阶段 顶点处理 + 图元装配(Vertex Shader)
3️⃣ 光栅化阶段 光栅化阶段(前半段) 将图元转为片元
4️⃣ 片元处理阶段 光栅化阶段(后半段) 执行片元着色器 + 深度模板测试 + 混合
5️⃣ 后处理 + 显示阶段 光栅化阶段之后的额外步骤 Bloom、色调映射、AA、最终输出屏幕
这样理解:
“五步法” = “三步法”的工程展开版,
尤其把 光栅化阶段 拆成了两个部分(光栅化 + 片元处理),再单独加上后处理与显示。

对照表
阶段编号 图形学三步渲染管线(理论模型) Unity 五步渲染管线(工程模型) 阶段主要任务说明
应用阶段(Application Stage) 应用阶段 在 CPU 上执行。进行场景管理、视锥体剔除、遮挡剔除、渲染排序、Draw Call 提交。决定哪些物体需要交给 GPU 渲染。
几何阶段(Geometry Stage) 几何阶段 在 GPU 顶点着色器阶段执行。将顶点从物体空间变换到屏幕空间,计算法线、切线、UV、骨骼动画变换等。
光栅化阶段(Rasterization Stage) 光栅化阶段 将几何图元(三角形等)转换成屏幕上的片元(Fragments)。执行背面剔除、插值计算、视口变换等。
——(包含在光栅化阶段中)—— 片元处理阶段(Fragment / Pixel Stage) 执行片元着色器(Pixel Shader)。进行纹理采样、光照计算、阴影映射、环境光遮蔽、深度测试、模板测试、混合等。
——(不属于传统管线核心部分)—— 后处理与显示阶段(Post-Processing & Display Stage) 对渲染结果执行后期特效:Bloom、Tone Mapping、Color Grading、AA、景深、Vignette 等。最后将图像提交至显示缓冲区(BackBuffer)显示到屏幕。
一、应用阶段(Application Stage)

	主要运行在 CPU,负责确定场景中需要提交给 GPU 渲染的数据。

	视椎体剔除(Frustum Culling)

		根据摄像机的视锥体,只保留在可见范围内的物体。
	
		Unity 内部使用 层级包围体剔除(如Octree或BVH) 来加速判断。

	遮挡剔除(Occlusion Culling)

		过滤掉被其他物体完全遮挡的对象。
	
		减少 GPU 的绘制调用(Draw Calls),提升渲染效率。

	渲染排序(Render Sorting)

		不透明物体:按从近到远排序(Front to Back),减少被后面物体覆盖的片元计算。
	
		透明物体:按从远到近排序(Back to Front),确保正确的混合(Blending)效果。
	
		同时可根据 渲染队列(Render Queue) 或 Shader Pass 进一步划分绘制顺序。

二、几何阶段(Geometry Stage)

	主要在 GPU 的顶点处理部分 执行。

	顶点处理(Vertex Processing)

		将每个顶点从 物体空间(Object Space)→ 世界空间(World Space)→ 观察空间(View Space)→ 裁剪空间(Clip Space)→ 屏幕空间(Screen Space)。
	
		计算或传递法线、切线、UV、颜色、骨骼动画变换(如有SkinnedMesh)等。

		执行顶点着色器(Vertex Shader)。

	图元装配(Primitive Assembly)

		根据拓扑类型(Triangles、Lines、Points)将顶点组合成完整的几何图元。
	
		形成可以进行光栅化的基础单位(通常是三角形)。

三、光栅化阶段(Rasterization Stage)

	负责把几何图元转换为屏幕上的 片元(Fragments)。

	光栅化(Rasterization)
	
		将三角形离散化为像素级别的片元。
	
		插值计算每个片元的深度(Z值)、颜色、法线、UV 等。
	
		可进行背面剔除(Back-Face Culling)和视口变换。

四、片元处理阶段(Fragment/Pixel Processing Stage)

	每个片元都会执行片元着色器,生成最终颜色信息。

	片元处理(Fragment Shading)	

		执行片元着色器(Fragment Shader / Pixel Shader)。
	
		进行纹理采样(Texture Sampling)、光照计算(Lighting)、阴影映射(Shadow Mapping)、反射探针、法线贴图、环境光遮蔽(AO)等。
	
		输出每个片元的颜色和深度。

	合成与输出(Output Merger / Blending Stage)

		执行深度测试(Depth Test)、模板测试(Stencil Test)、Alpha 测试(Alpha Test)。
	
		与帧缓冲中已有像素进行混合(Blending)。
	
		确定最终哪些像素会写入渲染目标(Render Target)。

五、后处理阶段(Post-Processing Stage)

	在整个场景渲染完成后,对生成的帧缓冲图像进行效果叠加。

	常见后处理效果:

		Bloom(泛光)
	
		Tone Mapping(色调映射)
	
		Color Grading(色彩校正)
	
		Depth of Field(景深)
	
		Motion Blur(动态模糊)
	
		Vignette(暗角)
	
		Anti-Aliasing(抗锯齿,如TAA、FXAA)

	六、显示到屏幕(Display / Present Stage)

	最终将渲染完成的帧缓冲提交给显示设备(GPU → Framebuffer → Swapchain → Monitor)。

	Unity 在此阶段使用 RenderTexture 或 Backbuffer 输出最终画面。

	在多平台上会通过不同的图形API实现(如 DirectX、Vulkan、Metal、OpenGL)。

简述Unity3d动画中avator的用处并举例。

  • 在动画中有选择性地启用或禁用特定身体部位。比如一个跑的动画和一个开枪动画。如果要实现跑动中设计就需要使用avator。实现上身播放开枪动画,下身播放跑步动画。

请简述GC(垃圾回收)产生的原因,并描述如何避免

  • GC回收堆上未有引用到的内存。
    1. 减少new产生对象的次数
    2. 使用公用的对象(静态成员)
    3. 将String换为StringBuilder

你对屏幕适配有什么好办法
在这里插入图片描述

你对资源加载有什么看法

  • 对于比较耗费的加载,属于IO操作,在游戏刚登陆的时候,进行初始化加载。强制加载一些重要模块,比如登陆窗口(用户输入用户名密码的地方)等等。选择性加载一些必要的模块:如果这人需要新手引导,则加载新手引导模块,否则不加载,反之亦然。这些用户一定会用(或者一定不用)的模块,在登陆时候处理好。对于应用模块:比如我打开一个很庞大的UI,这个怎么加载,如果这个模块很吸金,用户用得概率很大,那么必须在登陆时加载,否则那只能用户打开的时候加载了,加载的时候要转圈提示用户。

如何优化内存

  • 资源:

    1. 压缩自带类库;
    2. 将暂时不用的以后还需要使用的物体隐藏起来而不是直接Destroy掉;
    3. 释放AssetBundle占用的资源;
    4. 降低模型的片面数,降低模型的骨骼数量,降低贴图的大小;
    5. 使用光照贴图,使用多层次细节(LOD),使用着色器(Shader),使用预设(Prefab)。
    6. 纹理格式的压缩(可看下方包体优化方案)
    7. 播放较大音频资源的时候使用流加载,边加载边播放,而不是一次性加载到内存里再播放
  • 代码:

    1. 使用StringBuilder;
    2. 代码中少产生临时变量

列举包体优化的方法

  • 纹理格式的压缩(其实是纹理的压缩算法)

    1. 采用RGB Crunched DXT1,RGBA Crunched DXT5的纹理格式,此压缩方式会比RGB Compressed DXT1和RGBA Crunched DXT5小一半还要多(官方未推荐在android平台上使用, 但是在Oppo机型上测试没有问题,其他机型暂时没有测试,采用 RGB Crunched DXT要求图片为2的n次幂)
    2. 去掉纹理格式的mip选项,mipmap会生成多张小图来避免缩小图片时没必要的GPU采样消耗。但使用mipmap的图片会比不使用的图片多占用约三分之一的外存和内存。
  • 模型文件的压缩,

    1. 模型文件(玩家模型除外)下仅选择了压缩类型为中度,
  • 声音文件的压缩

    1. 改变声音文件的比特率
    2. 改变声音文件的压缩格式(目前测试采用Vorbis压缩格式声音文件最小,也可以使用mp3格式)
  • 场景的压缩

    1. 场景中静态物体对包体大小的影响(经测试打包时关卡的大小跟场景内静态物体的数量有很大关系,因此当打包时 关卡占用过大时,减小游戏内静态物体的数量可以减小包体的大小,但是游戏内对静态批处理和Culling等自动化处理都会都是依据static标记进行的,因此此方法减小包体,会对游戏的帧率有所影响)
Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐