1. List 是链表还是数组?
- C# 的 List本质是动态数组,底层维护一个 T[]。它支持通过下标快速随机访问,所以访问某个索引通常是 O(1)。
- 它不是链表,中间插入和删除时需要移动后面的元素,时间复杂度通常是 O(n)。当容量不足时,List 会申请更大的数组并复制旧数据,因此扩容时会有一次额外开销。
2. List 满了以后再 Add,内部发生什么?
- 当 List 的元素数量达到 Capacity 后,再 Add 一个元素会触发扩容。它会创建一个更大的内部数组,把旧数组元素复制过去,再把新元素写入。
- 所以单次扩容的成本比较高,包含数组分配和数据复制;但因为不是每次 Add 都扩容,整体追加的均摊复杂度仍然接近 O(1)。
- 如果业务上能预估数量,例如一波最多生成 100 个敌人,我会提前传入容量,减少运行时扩容和 GC 压力
3. IDisposable 的作用是什么?
- IDisposable 用于确定性释放资源。GC 主要负责托管堆对象的内存回收,但文件句柄、Socket、数据库连接、原生内存这类资源通常不能只等待 GC,所以需要在不再使用时主动调用 Dispose。
- C# 中通常通过 using 或 using var 保证即使发生异常,也能在 finally 中自动调用 Dispose。
- 在 Unity 中,像 NativeArray、文件流、网络连接等资源,也需要根据其生命周期显式释放。
4. GC、设为 null、Dispose、析构之间的关系
- GC 和 Dispose 解决的问题不同。GC 负责回收不再被引用的托管堆内存,但对象设为 null 只是断开当前引用,不代表立刻触发 GC。
- IDisposable.Dispose 用于主动释放文件句柄、Socket、原生内存等外部资源,因为这些资源往往数量有限,也不能接受不确定的回收时机。
- 所以两者不是重复的“析构机制”:GC 解决托管内存自动管理,Dispose 解决资源的确定性释放。终结器可以作为兜底,但不应作为正常释放资源的主要方式。
5. Unity 生命周期为什么是消息回调,而不是要求重写虚函数?
- Unity 生命周期更像引擎调度的消息回调,而不是普通继承体系中的虚函数。这样设计可以避免子类忘记调用 base.Update() 导致父类逻辑丢失,也更符合 Unity 以组件组合为主的开发方式。
- 开发者只需要声明自己实际需要的 Awake、Start、Update 等回调,引擎会在 PlayerLoop 的对应阶段统一调度它们,并允许通过 Script Execution Order 控制不同脚本的相对顺序。
- 我不会把它简单理解成“每帧靠反射硬调用”,更准确地说是 Unity 的原生引擎识别和调度这些预定义消息。
6. CPU 和 GPU 分别是什么?主要工作是什么?
- CPU 是通用处理器,擅长逻辑判断、任务调度和复杂分支。在游戏中通常负责脚本逻辑、AI、物理调度、网络处理,以及向 GPU 提交渲染命令。
- GPU 擅长大规模并行计算,主要负责顶点变换、光栅化、像素着色、纹理采样、光照和后处理。
- 性能优化时要先判断瓶颈是在 CPU 还是 GPU:CPU 瓶颈更多关注脚本、Draw Call 和逻辑更新;GPU 瓶颈更多关注像素填充、Shader、Overdraw、阴影和后处理。
7. 渲染管线中的几何阶段由谁主导?做什么?
- 几何阶段不能简单只说 CPU 或 GPU。CPU 负责准备可见物体并提交绘制命令;真正的顶点变换、裁剪和光栅化主要由 GPU 执行。
- 这个阶段会把模型顶点从模型空间经过世界空间、观察空间和投影变换,得到裁剪空间坐标,再进行视锥裁剪和三角形光栅化,最终生成后续片元着色需要处理的片元。
- 所以顶点数过多、模型过复杂,通常会增加 GPU 的几何处理压力;Draw Call 太多则更容易增加 CPU 提交压力。
8. 所有矩阵都有逆矩阵吗?如何计算?
- 不是所有矩阵都有逆矩阵。矩阵要可逆,首先必须是方阵,并且行列式不能等于 0。行列式为 0 表示变换过程中丢失了信息,例如某个轴缩放为 0,就不能再从结果恢复原始坐标。
- 小矩阵可以用伴随矩阵和行列式公式求逆,更通用的方法是高斯-约旦消元。图形开发中常见的是对变换矩阵求逆,例如平移取反、旋转取逆、缩放取倒数,用于世界坐标和局部坐标之间的转换。
9. 齐次坐标是什么?有什么作用?
- 齐次坐标是在三维坐标基础上增加一个 w 分量,用四维向量表示点和方向。一般点写作 (x,y,z,1),方向向量写作 (x,y,z,0)。
- 它最大的作用是让平移、旋转、缩放都可以统一用 4×4 矩阵表示;同时通过 w 的区别,让方向不受平移影响。
- 在渲染中,顶点经过投影矩阵后会得到齐次裁剪空间坐标,之后通过 xyz 除以 w 完成透视除法,形成近大远小的透视效果。
10. Shader 中主要经历哪些坐标空间?顺序是什么?
- Shader 中顶点位置通常依次经历模型空间、世界空间、观察空间、裁剪空间、NDC 和屏幕空间。
- 模型空间是顶点相对模型原点的位置;世界空间用于场景中的统一位置关系;观察空间是相对于摄像机的位置;裁剪空间是乘投影矩阵后的结果;随后通过透视除法得到 NDC,再映射到屏幕。
- 常见关系可以概括为 MVP 变换,也就是模型矩阵、观察矩阵和投影矩阵共同把对象空间顶点转换到裁剪空间。实际 Unity Shader 开发中,我会优先使用 URP 或 Unity 提供的空间转换函数,避免不同图形 API 下矩阵、深度范围和坐标约定带来的问题。
Comments
评论区
欢迎在这里留言交流。