Appearance
游戏客户端基础 共100题
说明:
- Unity 生命周期、协程、对象池、资源加载、UGUI、性能优化、同步方式等题点。
题型统计
| 题型 | 数量 |
|---|---|
| 单选题 | 35 |
| 多选题 | 20 |
| 判断题 | 10 |
| 填空题 | 10 |
| 代码阅读题 | 10 |
| 代码填空题 | 5 |
| 简答题 | 7 |
| 场景分析题 | 3 |
| 合计 | 100 |
一、单选题
1. Unity 中 GameObject 和 Component 的关系,下列说法正确的是?
A. GameObject 本身承载场景对象,具体行为主要由 Component 提供
B. Component 可以脱离 GameObject 单独存在于场景中
C. Transform 不是 Component
D. GameObject 只能挂一个 Component
答案:A
解析:GameObject 是场景对象的容器,Transform、Renderer、Collider、脚本等都属于组件。
2. 同一个激活对象第一次进入场景时,常见生命周期顺序是?
A. Start -> Awake -> OnEnable
B. Awake -> OnEnable -> Start
C. OnEnable -> Start -> Awake
D. Update -> Start -> Awake
答案:B
解析:Awake 用于初始化,OnEnable 在对象启用时调用,Start 在第一次 Update 前调用。

3. Awake 更适合放哪类逻辑?
A. 初始化自身字段和缓存组件引用
B. 每帧移动角色
C. 固定频率物理模拟
D. UI 点击回调里的业务提交
答案:A
解析:Awake 通常用于自身初始化,依赖其他对象启动顺序的逻辑要谨慎。
4. Start 的调用特点是?
A. 每帧都会调用
B. 对象启用后,在第一次 Update 前调用一次
C. 只在编辑器里调用
D. 只在物理帧调用
答案:B
解析:Start 不是每次启用都重复执行,一般作为首次启动逻辑。
5. FixedUpdate 通常用于?
A. 物理相关更新
B. UI 排版
C. 资源打包
D. 网络 DNS 解析
答案:A
解析:FixedUpdate 按固定游戏时间间隔执行,适合 Rigidbody 相关逻辑。
6. LateUpdate 常见用途是?
A. 在角色 Update 后处理相机跟随
B. 打包 AssetBundle
C. 加载配置表
D. 执行静态构造函数
答案:A
解析:相机跟随放在 LateUpdate 可以减少跟随目标先后顺序造成的抖动。
7. 实现帧率无关移动通常使用?
A. Time.deltaTime
B. Time.frameCount
C. Application.version
D. Screen.width
答案:A
解析:速度乘以 deltaTime 可以让单位时间位移更稳定。

8. 当 Time.timeScale 设置为 0 时,Time.deltaTime 通常会?
A. 变为 0
B. 变为 1
C. 不受影响
D. 变成负数
答案:A
解析:deltaTime 会受 timeScale 缩放,暂停时通常为 0。
9. 暂停界面倒计时仍希望按真实时间走,应优先使用?
A. Time.unscaledDeltaTime
B. Time.deltaTime
C. Time.fixedTime
D. Time.time
答案:A
解析:unscaledDeltaTime 不受 timeScale 影响,适合暂停界面、真实时间动画。
10. 父物体 SetActive(false) 后,子物体的 activeSelf 与 activeInHierarchy 通常如何?
A. 子物体 activeSelf 可能仍为 true,但 activeInHierarchy 为 false
B. 二者一定都为 true
C. 二者一定都为 false
D. activeInHierarchy 不受父物体影响
答案:A
解析:activeSelf 表示自身开关,activeInHierarchy 表示层级中最终是否激活。
11. activeInHierarchy 表示?
A. 对象在层级中最终是否处于激活状态
B. 对象自身 Inspector 勾选状态
C. 对象是否有 Rigidbody
D. 对象是否是 Prefab
答案:A
解析:父节点关闭会让子节点 activeInHierarchy 为 false。
12. Unity 协程方法常见返回类型是?
A. IEnumerator
B. Thread
C. Task<int>
D. Action
答案:A
解析:协程通过 IEnumerator/yield 将执行切分到多帧。
13. 关于 Unity 协程,下列说法正确的是?
A. 协程默认仍在主线程上分段执行
B. StartCoroutine 一定创建新线程
C. 协程一定比 Update 快
D. 协程不能访问 Unity API
答案:A
解析:协程不是线程,适合拆分等待流程,但 CPU 重活仍会阻塞主线程。
14. 将 MonoBehaviour.enabled 设置为 false,对该脚本已启动的协程通常会怎样?
A. 不会因为 enabled=false 自动停止
B. 一定立即停止
C. 一定切到后台线程
D. 一定重新从头开始
答案:A
解析:禁用组件本身不会自动停止协程;关闭 GameObject 或销毁对象则会影响协程。
15. GameObject SetActive(false) 对挂在其上的协程通常会怎样?
A. 会停止该对象上的协程
B. 完全没有影响
C. 只影响 FixedUpdate 不影响协程
D. 只在编辑器停止
答案:A
解析:关闭 GameObject 会停止附着在它上面的协程。
16. 对象池最主要减少的是?
A. 频繁 Instantiate/Destroy 带来的分配和回收开销
B. 所有算法复杂度
C. 网络协议数量
D. 源代码行数
答案:A
解析:对象池复用短生命周期对象,减少 CPU 与 GC 压力。

17. 大量子弹每帧创建销毁,最可能造成?
A. GC/CPU 抖动和内存碎片
B. 纹理自动压缩
C. 帧率一定提升
D. 服务器延迟归零
答案:A
解析:频繁分配和销毁会带来管理成本,移动端尤其容易出现卡顿。
18. Resources.Load 的资源通常需要放在?
A. 名为 Resources 的目录下
B. 任意 Editor 目录下
C. StreamingAssets 之外任意位置都行
D. Library 目录下
答案:A
解析:Resources API 按 Resources 目录下的路径加载,不写文件扩展名。
19. AssetBundle 的基本作用是?
A. 把非代码资源打成运行时可加载的资源包
B. 替代所有 C# 代码
C. 只用于保存 PlayerPrefs
D. 只用于网络握手
答案:A
解析:AssetBundle 常用于 DLC、热更资源、减小首包、平台资源拆分。
20. Addressables 相比手写 AssetBundle 管理,主要优势是?
A. 通过地址加载并管理依赖和引用计数
B. 让所有资源永远不占内存
C. 让代码自动热更新
D. 让所有平台包通用
答案:A
解析:Addressables 封装了地址、依赖、异步加载和释放流程。

21. Addressables 显式加载资源后,用完通常需要?
A. Release 对应句柄或资源
B. Delete ProjectSettings
C. 调用 GC.Collect 就够了
D. 重新启动编辑器
答案:A
解析:引用计数归零后,系统才有机会卸载相关资源。
22. DestroyImmediate 更推荐用于?
A. 编辑器工具中的立即销毁
B. 普通运行时每帧销毁对象
C. 网络收包
D. 播放动画
答案:A
解析:运行时通常使用 Destroy,DestroyImmediate 容易破坏运行时对象生命周期。
23. Transform.position 表示?
A. 世界坐标
B. 局部坐标
C. 屏幕像素坐标
D. 纹理 UV 坐标
答案:A
解析:localPosition 才是相对父节点的局部坐标。
24. Prefab 的主要意义是?
A. 复用一套对象层级和组件配置
B. 只保存脚本源码
C. 只保存运行日志
D. 替代场景文件
答案:A
解析:Prefab 适合复用角色、UI、特效、子弹等对象模板。
25. Rigidbody 物体的物理移动更推荐放在?
A. FixedUpdate 中配合 Rigidbody API
B. OnGUI
C. 静态构造函数
D. 资源导入回调
答案:A
解析:物理系统按固定步长模拟,直接改 Transform 可能绕开物理求解。
26. 3D 碰撞回调 OnCollisionEnter 一般要求?
A. 双方有 Collider,且至少一方有 Rigidbody
B. 双方都必须没有 Collider
C. 只要有 MeshRenderer
D. 只要对象名字相同
答案:A
解析:碰撞检测依赖 Collider/Rigidbody 组合。
27. Trigger 触发器常见要求是?
A. Collider 勾选 Is Trigger,且至少一方有 Rigidbody
B. 必须没有 Rigidbody
C. 只能用于 UI
D. 必须放在 Resources 下
答案:A
解析:Trigger 不做刚体碰撞响应,但会产生触发回调。
28. 频繁改变同一大 Canvas 下的 UI,可能导致?
A. Canvas rebuild 开销增加
B. 磁盘空间减少
C. DNS 查询变慢
D. 脚本无法编译
答案:A
解析:动态 UI 与静态 UI 混在一个 Canvas 中,容易扩大重建范围。
29. UGUI 中不需要响应点击的 Image/Text,通常可以关闭?
A. Raycast Target
B. Transform
C. GameObject 名称
D. CanvasScaler
答案:A
解析:关闭无用 Raycast Target 可以减少射线检测候选。
30. 长列表 ScrollRect 的常见优化是?
A. 只创建可见范围附近的 item 并复用
B. 一次生成所有 item
C. 每帧重建所有布局
D. 给每个 item 加一个独立相机
答案:A
解析:虚拟列表/循环列表能显著减少对象数和布局开销。
31. Animator Controller 中常用来驱动状态切换的是?
A. 参数和 Transition
B. DNS 记录
C. 端口号
D. 纹理 mipmap
答案:A
解析:Animator 通过状态机、参数、过渡条件控制动画流转。
32. Particle System 的 Simulation Space 主要决定?
A. 粒子按本地空间还是世界空间模拟
B. C# 是否装箱
C. 网络是否重传
D. 是否生成 AssetBundle
答案:A
解析:Local/World 会影响父节点移动缩放时粒子的表现。
33. C# 中 class 与 struct 的核心区别之一是?
A. class 是引用类型,struct 是值类型
B. class 一定在栈上,struct 一定在堆上
C. struct 可以继承任意类
D. 二者完全相同
答案:A
解析:值类型复制值,引用类型复制引用。
34. 事件订阅后不取消,常见风险是?
A. 对象被事件源持有导致无法释放或回调到失效对象
B. 代码自动变快
C. 资源自动打包
D. 协程自动停止
答案:A
解析:事件源生命周期更长时,订阅者可能被间接持有。
35. 定位 Unity 客户端卡顿,首先应该使用哪类工具?
A. Profiler/Frame Debugger/Memory Profiler 等分析工具
B. 随机删除代码
C. 只看 FPS 数字
D. 只改美术资源名字
答案:A
解析:优化前先定位瓶颈,区分 CPU、GPU、GC、IO、网络等问题。
二、多选题
1. 以下属于 Unity 常见生命周期函数的有?
A. Awake
B. OnEnable
C. Start
D. Update
答案:A、B、C、D
解析:四个都是 MonoBehaviour 常见生命周期函数。
2. 关于 Update、FixedUpdate、LateUpdate,下列正确的有?
A. Update 每帧调用
B. FixedUpdate 按固定游戏时间间隔调用
C. LateUpdate 在 Update 后调用
D. FixedUpdate 一定每渲染帧只调用一次
答案:A、B、C
解析:帧率波动时,一个渲染帧内可能没有 FixedUpdate,也可能有多个。
3. 对象池适合复用哪些对象?
A. 子弹
B. 飘字
C. 短生命周期特效
D. 永远只有一个的配置表
答案:A、B、C
解析:高频创建销毁对象适合池化,唯一配置表通常不需要对象池。

4. 对象从池中取出前,通常需要重置哪些状态?
A. 位置/旋转/缩放
B. 生命周期计时
C. 特效播放状态
D. 上一次绑定的事件或目标引用
答案:A、B、C、D
解析:池化对象最大风险是脏状态复用。
5. 资源管理中容易导致内存问题的做法有?
A. 重复加载同一资源
B. 引用计数没有释放
C. 切场景前不处理常驻对象
D. 加载后永远不卸载临时资源
答案:A、B、C、D
解析:资源生命周期不清晰会导致重复占用和峰值内存过高。
6. 使用 Addressables 时,较合理的做法有?
A. 异步加载
B. 保存并释放 handle
C. 按场景或系统规划 group
D. 加载完后完全不释放
答案:A、B、C
解析:不释放 handle 会让引用计数长期不归零。
7. Unity C# 中常见隐藏 GC 来源有?
A. 频繁 new 临时对象
B. 装箱
C. 闭包捕获
D. 每帧字符串拼接
答案:A、B、C、D
解析:这些都会产生托管堆分配或额外对象。
8. UGUI 常见优化方式有?
A. 动静分离 Canvas
B. 关闭无用 Raycast Target
C. ScrollRect item 复用
D. 避免频繁触发布局重建
答案:A、B、C、D
解析:UI 优化核心是减少重建、射线检测和过量对象。

9. 降低 DrawCall/渲染开销的常见方式有?
A. 合批/图集
B. 减少材质切换
C. GPU Instancing
D. 给每个物体单独材质实例
答案:A、B、C
解析:单独材质实例通常会破坏批处理。
10. 数据驱动配置的好处有?
A. 减少硬编码
B. 方便策划调参
C. 支持热更新配置
D. 让所有 Bug 自动消失
答案:A、B、C
解析:数据驱动提高迭代效率,但不能替代测试和约束。
11. 有限状态机 FSM 通常包含?
A. 状态
B. 状态切换条件
C. 进入/退出逻辑
D. 状态更新逻辑
答案:A、B、C、D
解析:FSM 常用于角色、AI、技能、UI 流程。
12. 游戏网络同步中常见技术有?
A. 客户端预测
B. 服务器校正
C. 插值平滑
D. 每帧阻塞等待服务器
答案:A、B、C
解析:阻塞等待会严重损害操作手感。

13. 实现帧率无关移动时,需要注意?
A. 速度乘 deltaTime
B. 避免把 deltaTime 重复乘两次
C. 暂停时区分 scaled/unscaled 时间
D. 物理对象尽量配合 FixedUpdate
答案:A、B、C、D
解析:时间来源和更新阶段要匹配。
14. 移动端适配常见内容包括?
A. 安全区
B. 分辨率与宽高比
C. 字体与本地化长度
D. 触控区域
答案:A、B、C、D
解析:游戏 UI 要兼顾刘海屏、平板、不同语言和可点击性。
15. 热更新/动态更新中,通常可以更新的内容有?
A. 配置表
B. AssetBundle/Addressables 资源
C. Lua/脚本热修逻辑
D. 客户端权限外的系统库
答案:A、B、C
解析:底层系统库和平台受限内容不能随意动态替换。
16. 切场景时需要考虑?
A. 异步加载进度
B. 旧场景资源释放
C. 常驻管理器是否重复创建
D. 峰值内存
答案:A、B、C、D
解析:场景切换是加载、卸载和常驻对象管理的集中点。
17. 输入系统处理中常见注意点有?
A. 点击穿透
B. 多点触控
C. UI 与场景点击优先级
D. 不同平台输入差异
答案:A、B、C、D
解析:输入要同时考虑 UI、场景和设备差异。
18. 客户端日志系统通常需要记录?
A. 关键错误
B. 资源加载失败
C. 网络断线/重连
D. 重要状态切换
答案:A、B、C、D
解析:日志要能帮助复现和定位线上问题。
19. 异步加载界面应关注?
A. 进度反馈
B. 错误兜底
C. 取消或超时处理
D. 加载完成后的引用管理
答案:A、B、C、D
解析:加载流程要处理失败和退出,不只是显示进度条。
20. 低端机性能分级常见策略有?
A. 降低特效等级
B. 降低阴影/后处理
C. 限制同屏对象数量
D. 强制开启最高画质
答案:A、B、C
解析:分级策略要按设备能力提供可接受体验。
三、判断题
1. Awake 一定在 Start 之前调用。
答案:对
解析:同一脚本生命周期中 Awake 早于 Start。
2. 禁用 MonoBehaviour.enabled 一定会停止该脚本已启动的协程。
答案:错
解析:enabled=false 不会自动停止协程。
3. 父物体关闭后,子物体 activeInHierarchy 会受到影响。
答案:对
解析:层级最终激活状态取决于父链路。
4. Time.deltaTime 表示上一帧到当前帧的时间间隔,并受 timeScale 影响。
答案:对
解析:暂停时 deltaTime 通常变为 0。
5. FixedUpdate 在每个渲染帧中一定只执行一次。
答案:错
解析:可能 0 次,也可能多次。
6. 对象池一定提升性能,没有任何副作用。
答案:错
解析:对象池可能带来脏状态、常驻内存、池大小策略等问题。
7. Resources 目录适合无限制放入所有项目资源。
答案:错
解析:大量使用 Resources 会增加管理和内存风险。
8. Addressables 引用计数归零后,相关资源才有机会卸载。
答案:对
解析:显式加载后需要配套释放。
9. UGUI LayoutGroup 频繁变化可能导致布局重建开销。
答案:对
解析:动态列表和复杂布局尤其明显。
10. 为防作弊,客户端计算结果都可以被服务器完全信任。
答案:错
解析:关键状态应由服务器校验或裁决。
四、填空题
1. 实现每秒移动 speed 单位,位移通常写作 speed * ______。
答案:Time.deltaTime
解析:deltaTime 表示帧间隔。
2. 暂停界面仍按真实时间播放动画,可使用 Time.______。
答案:unscaledDeltaTime
解析:它不受 timeScale 影响。
3. 物理相关更新通常写在 ______ 函数中。
答案:FixedUpdate
解析:FixedUpdate 按固定游戏时间间隔执行。
4. 相机跟随通常可以放在 ______,让目标先完成本帧移动。
答案:LateUpdate
解析:LateUpdate 在 Update 后调用。
5. 判断对象在层级中最终是否激活,应读取 ______。
答案:activeInHierarchy
解析:activeSelf 只表示自身开关。
6. Unity 协程常见返回类型是 ______。
答案:IEnumerator
解析:协程通过 yield 分段执行。
7. 关闭一个 GameObject 通常调用 gameObject.______(false)。
答案:SetActive
解析:SetActive 控制对象激活状态。
8. 切场景后仍保留全局管理器,可调用 ______。
答案:DontDestroyOnLoad
解析:常用于 AudioManager、GameManager 等常驻对象。
9. Addressables 显式加载资源后,用完应调用 ______ 释放。
答案:Release
解析:释放 handle 或资源,减少引用计数。
10. 定位 CPU、GC、内存等问题,Unity 常用工具是 ______。
答案:Profiler
解析:先定位瓶颈再优化。
五、代码阅读题
1. 生命周期输出顺序
阅读代码,写出输出或说明问题:
csharp
using UnityEngine;
public class Life : MonoBehaviour
{
private void Awake()
{
Debug.Log("Awake");
}
private void OnEnable()
{
Debug.Log("OnEnable");
}
private void Start()
{
Debug.Log("Start");
}
}答案:Awake、OnEnable、Start
解析:激活对象首次进入场景时,Awake 先于 OnEnable,Start 在第一次 Update 前执行。
2. 值类型复制
阅读代码,写出输出或说明问题:
csharp
using UnityEngine;
public struct Data
{
public int Value;
}
public class Test : MonoBehaviour
{
private void Start()
{
Data a = new Data { Value = 1 };
Data b = a;
b.Value = 2;
Debug.Log(a.Value);
}
}答案:1
解析:struct 是值类型,赋值会复制一份数据。
3. 引用类型复制
阅读代码,写出输出或说明问题:
csharp
using UnityEngine;
public class Data
{
public int Value;
}
public class Test : MonoBehaviour
{
private void Start()
{
Data a = new Data { Value = 1 };
Data b = a;
b.Value = 2;
Debug.Log(a.Value);
}
}答案:2
解析:class 是引用类型,a 和 b 指向同一个对象。
4. for 闭包捕获
阅读代码,写出输出或说明问题:
csharp
using System;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
private void Start()
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
actions.Add(() => Debug.Log(i));
}
foreach (Action action in actions)
{
action();
}
}
}答案:3、3、3
解析:for 循环变量被闭包捕获的是同一个变量。需要在循环体内复制局部变量。
5. Transform.position 的值拷贝
阅读代码,写出输出或说明问题:
csharp
using UnityEngine;
public class Test : MonoBehaviour
{
private void Start()
{
transform.position = Vector3.zero;
Vector3 p = transform.position;
p.x = 10;
Debug.Log(transform.position.x);
}
}答案:0
解析:Vector3 是值类型,修改临时变量 p 不会自动写回 transform.position。
6. activeSelf 与 activeInHierarchy
阅读代码,写出输出或说明问题:
csharp
using UnityEngine;
public class Test : MonoBehaviour
{
public GameObject parent;
public GameObject child;
private void Start()
{
child.SetActive(true);
parent.SetActive(false);
Debug.Log(child.activeSelf);
Debug.Log(child.activeInHierarchy);
}
}答案:True、False
解析:child 自身是激活的,但父物体关闭后,它在层级中不激活。
7. timeScale 对 deltaTime 的影响
阅读代码,写出输出或说明问题:
csharp
using UnityEngine;
public class Test : MonoBehaviour
{
private void Update()
{
Time.timeScale = 0f;
Debug.Log(Time.deltaTime == 0f);
}
}答案:通常输出 True
解析:deltaTime 受 timeScale 缩放;真实项目中不建议每帧反复设置 timeScale。
8. StringBuilder 引用复用
阅读代码,写出输出或说明问题:
csharp
using System.Text;
using UnityEngine;
public class Test : MonoBehaviour
{
private void Start()
{
StringBuilder builder = new StringBuilder();
builder.Append("A");
StringBuilder other = builder;
other.Append("B");
Debug.Log(builder.ToString());
}
}答案:AB
解析:StringBuilder 是引用类型,other 和 builder 指向同一个对象。
9. 事件订阅未解绑
阅读代码,写出输出或说明问题:
csharp
using System;
using UnityEngine;
public class Bus
{
public static event Action OnFire;
public static void Fire()
{
OnFire?.Invoke();
}
}
public class Listener : MonoBehaviour
{
private void OnEnable()
{
Bus.OnFire += Handle;
}
private void Handle()
{
Debug.Log("Fire");
}
}答案:风险:重复启用可能重复订阅,销毁后也可能残留引用
解析:应在 OnDisable 或 OnDestroy 中取消订阅,避免重复回调和生命周期问题。
10. 对象池脏状态
阅读代码,写出输出或说明问题:
csharp
using UnityEngine;
public class Bullet : MonoBehaviour
{
public int Damage = 10;
public void Init(int damage)
{
Damage = damage;
}
}
// 某次使用把 Damage 改成 100,回收时没有重置;
// 下次从池里取出后也没有调用 Init。答案:下次可能仍然是 100
解析:池化对象不会自动恢复默认状态,取出/回收时要明确重置。
六、代码填空题
1. 缓存 Transform,避免每帧重复查找
补全横线处代码:
csharp
using UnityEngine;
public class Mover : MonoBehaviour
{
private Transform cachedTransform;
private void Awake()
{
cachedTransform = ______;
}
private void Update()
{
cachedTransform.position += Vector3.forward * Time.deltaTime;
}
}答案:transform
解析:常用组件引用可以提前缓存。
2. 对象池取出对象时激活
补全横线处代码:
csharp
public GameObject Spawn()
{
GameObject obj = pool.Pop();
obj.______(true);
return obj;
}答案:SetActive
解析:从池中取出后通常激活对象。
3. 协程等待 1 秒
补全横线处代码:
csharp
using System.Collections;
using UnityEngine;
public class Test : MonoBehaviour
{
private IEnumerator Delay()
{
yield return new ______(1f);
Debug.Log("Done");
}
}答案:WaitForSeconds
解析:WaitForSeconds 受 timeScale 影响。
4. 字典读取配置,避免重复查找
补全横线处代码:
csharp
if (configs.______(id, out Config cfg))
{
Apply(cfg);
}答案:TryGetValue
解析:TryGetValue 可以一次完成查询和取值。
5. Addressables 释放句柄
补全横线处代码:
csharp
private AsyncOperationHandle<GameObject> handle;
private void OnDestroy()
{
Addressables.______(handle);
}答案:Release
解析:显式加载的资源要配套释放。
七、简答题
1. 简述对象池的作用和可能问题。
参考答案:对象池通过复用短生命周期对象,减少 Instantiate/Destroy、托管分配、GC 和初始化开销。常见问题是对象状态未重置、池过大导致常驻内存升高、回收时事件/协程/引用未清理、不同类型对象混池造成管理复杂。
2. 简述 Unity 常见生命周期顺序。
参考答案:常见顺序是 Awake、OnEnable、Start、Update、LateUpdate;FixedUpdate 按固定游戏时间间隔执行,不等同于每帧一次;OnDisable 在组件或对象禁用时调用,OnDestroy 在销毁时调用。
3. 协程和线程有什么区别?
参考答案:Unity 协程默认在主线程分段执行,不适合承载耗时 CPU 任务;线程是真正并行执行单元,但不能随意访问 UnityEngine 对象。协程适合等待、流程编排、分帧执行;线程适合纯数据计算或 IO,但要处理同步和回主线程。

4. Unity 中动态加载资源有哪些常见方式?
参考答案:常见方式包括 Resources.Load、AssetBundle、Addressables。Resources 使用简单但不适合大规模资源管理;AssetBundle 灵活但依赖和卸载需要自己处理;Addressables 封装地址、依赖、异步加载和引用释放,项目规模大时更常用。
5. UGUI 性能优化可以从哪些方面入手?
参考答案:可以动静分离 Canvas,减少 LayoutGroup 和 ContentSizeFitter 的频繁重建,关闭无用 Raycast Target,长列表使用复用,合并图集和材质,避免每帧刷新所有文本和图片,使用 Profiler/Frame Debugger 定位 CPU 与渲染开销。
6. 游戏客户端模块化设计通常怎么做?
参考答案:可以把资源、UI、网络、配置、音频、战斗、输入、存档等拆成边界清晰的模块。模块之间通过接口、事件或消息通信,避免互相直接拿内部对象。配置和运行时状态分离,方便测试、热更新和问题定位。
7. 遇到移动端偶发卡顿,你会如何分析?
参考答案:先用 Profiler 记录卡顿帧,区分 CPU、GPU、GC、IO、资源加载、Shader 编译、网络回调等原因;再看具体热点函数、GC Alloc、Canvas rebuild、Instantiate/Destroy、纹理上传和同步等待;最后做针对性优化并对比前后数据。
八、场景分析题
1. 场景:战斗中每秒生成大量子弹和命中特效,运行一分钟后开始明显卡顿。请分析原因并给出优化方案。
参考答案:可能原因是频繁 Instantiate/Destroy、粒子和脚本对象分配、GC、特效资源重复加载。优化上应建立子弹池和特效池,回收时停止粒子、解绑事件、清理目标引用,取出时重置位置、伤害、生命周期;资源提前预加载或按波次加载;用 Profiler 对比 GC Alloc、CPU Timeline 和对象数量。

2. 场景:背包界面有 3000 个道具格,打开界面卡顿,滑动时掉帧。如何优化?
参考答案:不要一次生成全部格子,应使用虚拟列表,只创建可见区域和少量缓冲 item;动静分离 Canvas,减少 LayoutGroup 重建;图标异步加载并缓存;关闭不需要点击的 Raycast Target;文本和红点只在数据变化时刷新;用 Profiler 观察 Canvas.BuildBatch、Layout、GC Alloc。
3. 场景:切换场景后内存没有下降,甚至越切越高。请列出排查步骤。
参考答案:先确认旧场景对象是否被 DontDestroyOnLoad 或静态引用持有;检查 Addressables/AssetBundle handle 是否 Release;检查对象池是否无限扩张;检查事件订阅、单例、缓存字典是否持有场景对象;使用 Memory Profiler 对比快照,查看纹理、Mesh、AudioClip、GameObject 和托管对象引用链。