36.总结
36.1 知识点

学习的主要内容

总结讲什么

学习Unity是学习什么

如何学好Unity

强调

36.2 核心要点速览
生命周期函数和打印信息
生命周期函数详解
| 函数名 | 调用时机 | 用途 |
|---|---|---|
| Awake | 对象(脚本类对象)被创建时调用 | 类似构造函数,可进行对象创建时的初始化操作 |
| OnEnable | 对象被激活时调用 | 可在此处编写对象激活时的逻辑处理代码,运行游戏时自动调用一次 |
| Start | 对象第一次帧更新前执行 | 用于编写初始化逻辑,执行时间比 Awake 晚 |
| FixedUpdate | 每一物理帧执行 | 主要用于物理更新,物理帧与游戏帧不同 |
| Update | 每一游戏帧执行 | 用于处理游戏核心逻辑更新 |
| LateUpdate | 比 Update 晚执行 | 一般用于处理摄像机位置更新相关内容 |
| OnDisable | 对象失活时调用 | 可在此处编写对象失活时的处理逻辑 |
| OnDestroy | 对象被销毁时调用 | 销毁前对象失活,会先调用 OnDisable |
在 Unity 中打印信息
| 类 | 方法 | 用途 |
|---|---|---|
| Debug 类 | Log 方法 | 打印普通信息,如 Debug.Log("Awake Debug.Log"); |
| Debug 类 | LogWarning 方法 | 打印警告信息,如 Debug.LogWarning("警告"); |
| Debug 类 | LogError 方法 | 打印报错信息,如 Debug.LogError("错误"); |
| MonoBehaviour 类 | print 方法 | 继承时可使用,如 print("Awake print"); |
Unity 特性
变量可见性控制
| 特性 | 作用 | 适用变量 | 语法 | 关键说明 |
|---|---|---|---|---|
| [SerializeField] | 强制私有/保护变量显示 | private/protected | [SerializeField] private int value; |
序列化字段,支持基本类型、数组、GameObject 等;运行时修改影响成员变量 |
| [HideInInspector] | 隐藏公共变量 | public 变量 | [HideInInspector] public int value; |
代码可访问,但 Inspector 不显示 |
| 默认公共变量 | 直接显示 | public 变量 | public int value; |
简单直接,但暴露所有公共字段 |
自定义类型序列化
| 类型 | 显示条件 | 示例 | 关键说明 |
|---|---|---|---|
| 枚举 | 直接显示(无需额外特性) | public E_TestEnum enumValue; |
下拉菜单选择枚举值 |
| 结构体 | 需 [System.Serializable] 特性 |
[Serializable] public struct MyStruct{...} |
序列化后可展开编辑字段 |
| 类 | 需 [System.Serializable] 特性 + 继承 UnityEngine.Object 或标记为 [CreateAssetMenu] |
[Serializable] public class MyClass{...} |
非 MonoBehaviour 需手动序列化;ScriptableObject 需 [CreateAssetMenu] |
| 集合 | 直接显示(数组、List |
public int[] array;public List<int> list; |
支持动态添加/删除元素(List 需初始化) |
辅助特性(提升可读性)
| 特性 | 作用 | 参数 | 示例 |
|---|---|---|---|
| [Header(“标题”)] | 分组标题(字段上方) | 字符串(如 “基础属性”) | [Header("战斗属性")] public int atk; |
| [Tooltip(“提示”)] | 悬停提示 | 字符串 | [Tooltip("闪避率")] public float miss; |
| [Space(像素)] | 字段间隔 | 可选像素值 | [Space(20)] public int crit; |
| [Range(Min, Max)] | 数值滑条 | 最小值,最大值 | [Range(0, 10)] public float luck; |
| [Multiline(行数)] | 多行文本框 | 可选行数 | [Multiline(5)] public string tips; |
| [TextArea(Min, Max)] | 滚动文本框 | 最小行数,最大行数 | [TextArea(3, 4)] public string desc; |
交互增强特性
| 特性 | 作用 | 参数 | 示例 |
|---|---|---|---|
| [ContextMenuItem(“按钮名”, “方法”)] | 为变量添加右键快捷方法 | 按钮文本,方法名 | [ContextMenuItem("重置", "ResetMoney")] public int money; |
| [ContextMenu(“按钮名”)] | 为方法添加 Inspector 执行按钮 | 按钮文本 | [ContextMenu("测试逻辑")] private void Test(); |
基础属性和组件的获取
重要成员
| 成员 | 说明 | 示例 |
|---|---|---|
| gameObject | 获取脚本依附的 GameObject | print(gameObject.name); |
| transform | 获取位置信息 | print(transform.position); |
| enabled | 获取或设置脚本激活状态 | this.enabled = false; |
重要方法
| 方法 | 作用 | 参数 | 示例 |
|---|---|---|---|
| GetComponent | 获取单个挂载脚本 | 可按脚本名、Type、泛型获取 | var script = GetComponent<MyScript>(); |
| GetComponents | 获取多个挂载脚本 | 泛型 | var scripts = GetComponents<MyScript>(); |
| GetComponentInChildren | 获取子对象挂载的单个脚本 | bool:是否查找失活子对象,默认 false | var childScript = GetComponentInChildren<MyScript>(true); |
| GetComponentsInChildren | 获取子对象挂载的多个脚本 | bool:是否查找失活子对象,默认 false;可传入 List 接收结果 | GetComponentsInChildren<MyScript>(true, list); |
| GetComponentInParent | 获取父对象挂载的单个脚本 | 无 | var parentScript = GetComponentInParent<MyScript>(); |
| GetComponentsInParent | 获取父对象挂载的多个脚本 | 无 | var parentScripts = GetComponentsInParent<MyScript>(); |
| TryGetComponent | 安全获取单个脚本 | 泛型,使用 out 输出结果 | if (TryGetComponent<MyScript>(out var script)) { ... } |
GameObject 游戏对象
GameObject 成员变量
| 变量 | 说明 | 示例 |
|---|---|---|
| name | 对象名称 | print(gameObject.name);gameObject.name = "新名称"; |
| activeSelf | 对象是否激活 | print(gameObject.activeSelf); |
| isStatic | 对象是否为静态对象 | print(gameObject.isStatic); |
| layer | 对象的层级 | print(gameObject.layer); |
| tag | 对象的标签 | print(gameObject.tag); |
| transform | 位置、旋转和缩放信息 | print(gameObject.transform.position); |
GameObject 静态方法
| 方法 | 作用 | 注意事项 | 示例 |
|---|---|---|---|
| CreatePrimitive | 创建带原始网格和碰撞体的对象 | GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); |
|
| Find | 通过对象名查找激活对象 | 效率低,找不到返回 null,无法找失活对象 | GameObject obj = GameObject.Find("对象名"); |
| FindWithTag | 通过标签查找单个激活对象 | 同上 | GameObject obj = GameObject.FindWithTag("标签名"); |
| FindGameObjectsWithTag | 通过标签查找多个激活对象 | 返回空数组 | GameObject[] objs = GameObject.FindGameObjectsWithTag("标签名"); |
| FindObjectOfType | 找到场景中挂载某脚本的对象 | 效率低,遍历对象和脚本 | var scriptObj = GameObject.FindObjectOfType<MyScript>(); |
| Instantiate | 实例化(克隆)对象 | GameObject clone = GameObject.Instantiate(original); |
|
| Destroy | 删除对象 | 异步,下一帧移除,建议优先使用 | GameObject.Destroy(obj, 5); // 5 秒后删除 |
| DestroyImmediate | 立即删除对象 | 可能造成卡顿 | GameObject.DestroyImmediate(obj); |
| DontDestroyOnLoad | 过场景不移除对象 | GameObject.DontDestroyOnLoad(obj); |
GameObject 成员方法
| 方法 | 作用 | 示例 |
|---|---|---|
| 构造函数 | 创建空物体 | GameObject obj = new GameObject("物体名", typeof(脚本1), typeof(脚本2)); |
| AddComponent | 为对象添加脚本 | var script = obj.AddComponent<MyScript>(); |
| GetComponent | 得到对象的脚本 | var script = obj.GetComponent<MyScript>(); |
| CompareTag | 标签比较 | if (gameObject.CompareTag("标签名")) { ... } |
| SetActive | 设置对象激活/失活 | obj.SetActive(false); |
次要成员方法(不建议使用,效率低)
| 方法 | 作用 | 示例 |
|---|---|---|
| SendMessage | 广播自己执行方法 | gameObject.SendMessage("方法名", 参数); |
| BroadcastMessage | 广播自己和所有子对象执行方法 | gameObject.BroadcastMessage("方法名"); |
| SendMessageUpwards | 广播自己和所有父对象执行方法 | gameObject.SendMessageUpwards("方法名"); |
Time 时间
| 分类 | 变量 | 说明 | 受 timeScale 影响 | 使用场景 | 示例代码 |
|---|---|---|---|---|---|
| 时间缩放比例 | timeScale | 控制时间流逝的标度,可用于慢动作、暂停等效果。默认值为 1 | - | 实现游戏暂停、倍速 | Time.timeScale = 0; // 时间停止Time.timeScale = 2; // 2 倍速 |
| 普通帧间隔时间 | deltaTime | 完成上一帧所用的时间(秒) | 是 | 游戏暂停时物体停止移动 | print("帧间隔时间: " + Time.deltaTime);transform.Translate(Vector3.forward * speed * Time.deltaTime); |
| (Update 内) | unscaledDeltaTime | 独立于 timeScale 的时间间隔(秒) | 否 | 不受暂停影响的位移计算 | print("不受 scale 影响的帧间隔时间: " + Time.unscaledDeltaTime);transform.Translate(Vector3.forward * speed * Time.unscaledDeltaTime); |
| 游戏开始时间 | time | 自游戏启动以来受 timeScale 影响的时间(秒) | 是 | 受时间缩放影响的计时 | print("受 scale 影响的游戏时间: " + Time.time); |
| (计时功能) | unscaledTime | 独立于 timeScale 的时间(秒) | 否 | 不受时间缩放影响的计时 | print("不受 scale 影响的游戏时间: " + Time.unscaledTime); |
| 物理帧间隔时间 | fixedDeltaTime | 执行物理更新的时间间隔(秒) | 是 | 物理模拟计算 | print("受 scale 影响的物理帧间隔: " + Time.fixedDeltaTime); |
| (FixedUpdate 内) | fixedUnscaledDeltaTime | 独立于 timeScale 的物理帧间隔(秒) | 否 | 不受时间缩放影响的物理模拟 | print("不受 scale 影响的物理帧间隔: " + Time.fixedUnscaledDeltaTime); |
| 帧数 | frameCount | 从游戏开始到现在运行的总帧数 | 否 | 帧同步、帧率统计 | print("总帧数: " + Time.frameCount); |
Transform
Transform 概述
Transform 主要用于游戏对象的位移、旋转、缩放、父子关系、坐标转换等操作。
Vector3 三维向量
| 分类 | 说明 | 示例 |
|---|---|---|
| 声明方式 | 无参构造后赋值;传 xy/z 坐标构造;结构体声明后赋值 | Vector3 v1 = new Vector3(10, 10, 10);Vector3 v2 = new Vector3(10, 10);(z=0) |
| 基本计算 | 支持 +、 -、 *、 / 运算 | Vector3 v1 = new Vector3(1, 1, 1);Vector3 v2 = new Vector3(2, 2, 2);print(v1 + v2); // (3.0, 3.0, 3.0) |
| 常用向量 | 提供如 Vector3.zero、Vector3.right 等 | print(Vector3.zero); // (0, 0, 0) |
| 距离计算 | Distance 方法返回向量之间的距离 | print(Vector3.Distance(v1, v2)); // 1.732051 |
位置
| 变量 | 说明 | 注意事项 | 示例 |
|---|---|---|---|
| position | 相对世界坐标系的坐标 | 不能单独修改 xyz,只能整体赋值 | print(this.transform.position);this.transform.position = new Vector3(10, 10, 10); |
| localPosition | 相对父对象的坐标 | 若想以面板坐标为准设置位置,使用此变量 | print(this.transform.localPosition);this.transform.localPosition = Vector3.up * 10; |
朝向
可通过 transform.forward、transform.up、transform.right 得到对象的面朝向、头顶朝向、右手边朝向。
位移
| 方式 | 说明 | 示例 |
|---|---|---|
| 手动计算 | 路程 = 方向 * 速度 * 时间,放在 Update 里 | this.transform.position += this.transform.forward * 1 * Time.deltaTime; |
| Translate 方法 | 根据方向和距离移动,可指定相对坐标系 | this.transform.Translate(Vector3.forward * 1 * Time.deltaTime, Space.World); |
角度和旋转
| 分类 | 变量/方法 | 说明 | 注意事项 | 示例 |
|---|---|---|---|---|
| 角度 | rotation | 返回旋转的四元数 | - | print(this.transform.rotation); |
| eulerAngles | 相对世界坐标角度 | 不能单独设置 xyz,用 Vector3 赋值 | print(this.transform.eulerAngles);this.transform.eulerAngles = new Vector3(10, 10, 10); |
|
| localEulerAngles | 相对父对象角度 | 若想改变面板显示角度,使用此变量 | print(this.transform.localEulerAngles);this.transform.localEulerAngles = new Vector3(10, 10, 10); |
|
| 旋转 | Rotate 方法 | 自转,有多种重载方式 | 一般放在 Update 内 | this.transform.Rotate(new Vector3(0, 100, 0) * Time.deltaTime);this.transform.Rotate(Vector3.right, 10 * Time.deltaTime, Space.World); |
| RotateAround 方法 | 相对于某一点转 | - | this.transform.RotateAround(Vector3.zero, Vector3.right, 10 * Time.deltaTime); |
缩放
| 变量 | 说明 | 注意事项 | 示例 |
|---|---|---|---|
| lossyScale | 相对世界坐标系的缩放大小(只读) | 只能获取,不能改变 | print(this.transform.lossyScale); |
| localScale | 相对本地坐标系的缩放大小 | 一般修改此变量,不能只改 xyz | print(this.transform.localScale);this.transform.localScale += Vector3.one * Time.deltaTime; |
看向
LookAt 方法可让对象面朝向某一点或对象,一般写在 Update 内持续更新:
this.transform.LookAt(Vector3.zero);看向世界坐标原点this.transform.LookAt(lookAtObj.transform);看向目标对象
父子关系
| 操作 | 变量/方法 | 说明 | 示例 |
|---|---|---|---|
| 获取父对象 | parent 变量 | 获取父对象,返回父对象的 transform | print(this.transform.parent);print(this.transform.parent.name); |
| 设置父对象 | parent 变量 | 断绝父子关系或设置新父对象 | this.transform.parent = null;this.transform.parent = GameObject.Find("Father2").transform; |
| SetParent 方法 | 可指定是否保留世界坐标信息 | this.transform.SetParent(null);this.transform.SetParent(fatherTransform, false);(不保留世界坐标) |
|
| 断绝子对象 | DetachChildren 方法 | 断绝与所有子对象的关系 | this.transform.DetachChildren(); |
| 获取子对象 | Find 方法 | 按名字查找儿子,能找到失活对象 | print(this.transform.Find("Cube (1)").name); |
| childCount 变量 | 得到儿子数量,失活儿子也算 | print(this.transform.childCount); |
|
| GetChild 方法 | 遍历所有儿子 | for (int i = 0; i < this.transform.childCount; i++) { print(this.transform.GetChild(i).name); } |
|
| 儿子操作 | IsChildOf 方法 | 判断是否为某对象的子项 | if (son.IsChildOf(this.transform)) { print("是儿子"); } |
| GetSiblingIndex 方法 | 得到自己的编号 | print(son.GetSiblingIndex()); |
|
| SetAsFirstSibling 方法 | 设置为第一个儿子 | son.SetAsFirstSibling(); |
|
| SetAsLastSibling 方法 | 设置为最后一个儿子 | son.SetAsLastSibling(); |
|
| SetSiblingIndex 方法 | 设置为指定编号的儿子 | son.SetSiblingIndex(1); |
坐标转换
| 转换类型 | 方法 | 说明 | 示例 |
|---|---|---|---|
| 世界坐标转本地坐标 | InverseTransformPoint 方法 | 世界坐标系的点转换为本地坐标的点 | print("转换后的点 " + this.transform.InverseTransformPoint(Vector3.forward)); |
| InverseTransformDirection 方法 | 世界坐标系的方向转换为本地方向(不受缩放影响) | print("转换后的方向(不受缩放影响)" + this.transform.InverseTransformDirection(Vector3.forward)); |
|
| InverseTransformVector 方法 | 世界坐标系的方向转换为本地方向(受缩放影响) | print("转换后的方向(受缩放影响)" + this.transform.InverseTransformVector(Vector3.forward)); |
|
| 本地坐标转世界坐标 | TransformPoint 方法 | 本地坐标系的点转换为世界坐标的点(受缩放影响) | print("本地 转 世界 点" + this.transform.TransformPoint(Vector3.forward)); |
| TransformDirection 方法 | 本地坐标系的方向转换为世界方向(不受缩放影响) | print("本地 转 世界 方向" + this.transform.TransformDirection(Vector3.forward)); |
|
| TransformVector 方法 | 本地坐标系的方向转换为世界方向(受缩放影响) | print("本地 转 世界 方向" + this.transform.TransformVector(Vector3.forward)); |
Input 组件
鼠标输入(Update 中使用)
| 方法/属性 | 说明 | 参数/返回值 | 示例 | 注意事项 |
|---|---|---|---|---|
| mousePosition | 鼠标屏幕坐标(左下角为原点) | Vector3(x,y 像素坐标) | print(Input.mousePosition); |
屏幕坐标系:x 右,y 上,z=0 |
| GetMouseButtonDown(0/1/2) | 鼠标按键按下瞬间(0=左,1=右,2=中) | int 键索引,返回 bool | if (Input.GetMouseButtonDown(0)) 射击(); |
仅按下帧触发一次 |
| GetMouseButtonUp(0/1/2) | 鼠标按键抬起瞬间 | int 键索引,返回 bool | if (Input.GetMouseButtonUp(1)) 取消(); |
仅抬起帧触发一次 |
| GetMouseButton(0/1/2) | 鼠标按键长按(持续触发) | int 键索引,返回 bool | if (Input.GetMouseButton(2)) 旋转(); |
按住期间每帧触发 |
| mouseScrollDelta | 鼠标滚轮滚动量(y 轴为主) | Vector2(y=-1 下滚,y=1 上滚) | float scroll = Input.mouseScrollDelta.y; |
滚轮滚动时触发,非持续 |
键盘输入(Update 中使用)
| 方法/属性 | 说明 | 参数/返回值 | 示例 | 注意事项 |
|---|---|---|---|---|
| GetKeyDown(KeyCode.W) | 键盘按下瞬间(枚举或小写字符串) | KeyCode 枚举或 “w” 字符串,返回 bool | if (Input.GetKeyDown(KeyCode.Space)) 跳跃(); |
枚举大写(如 KeyCode.W),字符串小写(如 “w”) |
| GetKeyUp(KeyCode.W) | 键盘抬起瞬间 | KeyCode,返回 bool | if (Input.GetKeyUp("q")) 换弹(); |
仅抬起帧触发一次 |
| GetKey(KeyCode.W) | 键盘长按(持续触发) | KeyCode,返回 bool | if (Input.GetKey(KeyCode.LeftShift)) 加速(); |
按住期间每帧触发 |
| inputString | 按下的键盘字符(如字母、数字) | string(只读) | if (Input.anyKeyDown) print(Input.inputString); |
仅在 anyKeyDown 帧有效 |
| anyKeyDown | 任意键/鼠标按下瞬间 | bool(只读) | if (Input.anyKeyDown) 开始游戏(); |
包括鼠标和键盘 |
虚拟轴输入(控制移动/旋转)
| 方法 | 说明 | 返回值范围 | 默认轴 | 示例 | 适用场景 |
|---|---|---|---|---|---|
| GetAxis(“Horizontal”) | 平滑轴(渐变值,-1~1) | float(如 -0.5, 0.8) | Horizontal(A/D 或 左摇杆) | transform.Translate(Vector3.right * Input.GetAxis("Horizontal")); |
角色移动、镜头旋转 |
| GetAxisRaw(“Vertical”) | 离散轴(仅 -1/0/1) | float(-1/0/1) | Vertical(W/S 或 上摇杆) | if (Input.GetAxisRaw("Fire1") > 0) 开火(); |
技能触发、跳跃 |
| GetAxis(“Mouse X/Y”) | 鼠标移动增量(屏幕像素速度) | float(-1~1) | Mouse X(鼠标左右) | camera.Rotate(0, Input.GetAxis("Mouse X"), 0); |
镜头旋转 |
触摸输入(移动设备)
| 方法/属性 | 说明 | 示例 | 注意事项 |
|---|---|---|---|
| touchCount | 当前触摸点数(只读) | if (Input.touchCount >= 2) 缩放(); |
需开启 Edit > Project Settings > Input Manager > Multi Touch |
| touches[0].position | 第 0 个触摸点屏幕坐标 | Vector2 pos = Input.touches[0].position; |
触摸点索引从 0 开始 |
| touches[0].deltaPosition | 触摸点相对上次位置的偏移量 | Vector2 delta = Input.touches[0].deltaPosition; |
用于滑动控制(如移动角色) |
| multiTouchEnabled | 是否启用多点触控 | Input.multiTouchEnabled = true; |
默认为 true,禁用后仅识别第一个触摸点 |
手柄/虚拟按钮
| 方法 | 说明 | 参数 | 示例 | 配置 |
|---|---|---|---|---|
| GetButtonDown(“Jump”) | 手柄按钮按下瞬间 | string 按钮名(如 “Jump”) | if (Input.GetButtonDown("Jump")) 跳跃(); |
在 Edit > Project Settings > Input Manager 中配置 |
| GetButton(“Fire”) | 手柄按钮长按 | string 按钮名 | if (Input.GetButton("Fire")) 持续射击(); |
支持键盘/手柄映射 |
重力感应(陀螺仪)
| 属性 | 说明 | 示例 | 注意事项 |
|---|---|---|---|
| gyro.enabled | 启用陀螺仪(需先开启) | Input.gyro.enabled = true; |
移动设备专属,PC 需模拟 |
| gyro.gravity | 重力加速度向量(世界坐标系) | Vector3 gravity = Input.gyro.gravity; |
手机倾斜时返回重力方向(如平放时 (0, -1, 0)) |
| gyro.rotationRate | 旋转角速度(弧度/秒) | float rotation = Input.gyro.rotationRate.z; |
手机旋转时的角速度 |
Screen 屏幕
| 分类 | 变量/方法 | 说明 | 示例 | 注意事项 |
|---|---|---|---|---|
| 静态属性(常用) | currentResolution | 当前屏幕分辨率,返回 Resolution 类对象 | Resolution resolution = Screen.currentResolution; |
得到的是显示器分辨率,不是 Game 窗口的 |
| width、height | Game 窗口当前宽高 | print("Game窗口宽: " + Screen.width);print("Game窗口高: " + Screen.height); |
用于代码中用 Game 窗口宽高做计算 | |
| sleepTimeout | 屏幕休眠模式 | Screen.sleepTimeout = SleepTimeout.NeverSleep;Screen.sleepTimeout = SleepTimeout.SystemSetting; |
- | |
| 静态属性(不常用) | fullScreen | 运行时是否全屏模式 | Screen.fullScreen = true; |
- |
| fullScreenMode | 窗口模式(FullScreenMode 枚举) | Screen.fullScreenMode = FullScreenMode.Windowed; |
可选择独占全屏、全屏窗口等 | |
| 移动设备旋转屏幕 | autorotateToLandscapeLeft | 允许自动旋转为左横向 | Screen.autorotateToLandscapeLeft = true; |
- |
| autorotateToLandscapeRight | 允许自动旋转为右横向 | Screen.autorotateToLandscapeRight = true; |
- | |
| autorotateToPortrait | 允许自动旋转到纵向 | Screen.autorotateToPortrait = true; |
- | |
| autorotateToPortraitUpsideDown | 允许自动旋转到纵向倒置 | Screen.autorotateToPortraitUpsideDown = true; |
- | |
| orientation | 指定屏幕显示方向 | Screen.orientation = ScreenOrientation.Landscape; |
- | |
| 静态方法 | SetResolution | 设置分辨率(一般移动设备不使用) | Screen.SetResolution(1920, 1080, false); |
- |
Camera 摄像机
Camera 组件参数
| 分类 | 参数 | 说明 | 用途 | 示例或注意事项 |
|---|---|---|---|---|
| Clear Flags | Skybox(天空盒) | 3D游戏适用,渲染天空盒 | 3D游戏开发 | - |
| (清除标志) | Solid Color(纯色) | 2D游戏适用,可修改背景颜色 | 2D游戏开发 | 在 Background 中选择颜色 |
| Depth only(仅深度) | 只画该层,背景透明,用于多摄像机叠加 | 多摄像机渲染 | 高深度摄像机常选此模式,配合层级选择 | |
| Don’t Clear(不清除) | 覆盖渲染,较少使用 | 特殊渲染需求 | - | |
| Background | - | 没有天空盒时的背景颜色 | 设置背景颜色 | 选择合适的背景颜色 |
| Culling Mask | - | 选择性渲染对象层级 | 控制渲染对象层级 | 在检视面板中将层分配到对象 |
| Projection | Perspective(透视模式) | 3D游戏适用,有近大远小效果 | 3D游戏开发 | FOV Axis 控制视野轴,Field of view 控制视角 |
| (投影) | Orthographic(正交模式) | 2D游戏适用,无近大远小效果 | 2D游戏开发 | Size 控制视野范围 |
| Clipping Planes | - | 确定对象渲染的距离区间 | 控制对象渲染范围 | 对象渲染异常可能与此参数有关 |
| Viewport Rect | - | 摄像机视图在屏幕的位置和大小 | 多摄像机游戏 | XY 为起始位置,宽高为比例 |
| Depth | - | 摄像机渲染顺序,值越大越靠上 | 控制摄像机渲染顺序 | 低深度先渲染,高深度后渲染 |
| Rendering path | - | 定义渲染方法 | 选择渲染方式 | - |
| Target Texture | - | 渲染到纹理(如小地图) | 制作小地图等 | 创建 Render Texture 并拖拽到该属性 |
| Occlusion Culling | - | 启用遮挡剔除,优化渲染性能 | 优化渲染性能 | 默认勾选,减少不必要渲染 |
| HDR | - | 启用高动态范围渲染 | 提升画面质量 | - |
| MSAA | - | 启用多重采样抗锯齿 | 提升画面平滑度 | - |
| Allow Dynamic Resolution | - | 启用动态分辨率渲染 | 优化性能 | - |
| Target Display | - | 定义渲染到的外部设备 | 多屏幕平台游戏开发 | - |
| Target Eye | - | VR游戏中选择渲染的眼睛 | VR游戏开发 | - |
Camera 参数和方法
获取摄像机相关
| 静态成员 | 说明 | 示例代码 | 注意事项 |
|---|---|---|---|
| main | 获取主摄像机(tag 为 “MainCamera”) | print(Camera.main); |
场景必须有 tag 为 “MainCamera” 的摄像机,否则报错 |
| allCamerasCount | 获取场景中摄像机数量 | print(Camera.allCamerasCount); |
- |
| allCameras | 得到场景中所有已启用的摄像机 | Camera[] allCameras = Camera.allCameras;print(allCameras.Length); |
- |
渲染相关委托
| 静态变量 | 说明 | 示例代码 |
|---|---|---|
| onPreCull | 摄像机开始剔除前触发的事件 | Camera.onPreCull += (c) => { /* 处理逻辑 */ }; |
| onPreRender | 摄像机开始渲染前触发的事件 | Camera.onPreRender += (c) => { /* 处理逻辑 */ }; |
| onPostRender | 摄像机完成渲染后触发的事件 | Camera.onPostRender += (c) => { /* 处理逻辑 */ }; |
重要成员
| 成员 | 说明 | 示例代码 | 注意事项 |
|---|---|---|---|
| depth | 设置摄像机渲染深度 | Camera.main.depth = 10; |
- |
| WorldToScreenPoint | 世界坐标转屏幕坐标(血条功能) | Vector3 v = Camera.main.WorldToScreenPoint(this.transform.position);print(v); |
- |
| ScreenToWorldPoint | 屏幕坐标转世界坐标(需设置 Z 值) | Vector3 v = Input.mousePosition;v.z = 5;sphere.position = Camera.main.ScreenToWorldPoint(v); |
不设置 Z 值时默认为 0,设置后是摄像机前方对应单位的坐标 |
Light 光面板
光源组件
| 分类 | 参数 | 说明 | 示例或注意事项 |
|---|---|---|---|
| 光源类型 | Directional(方向光) | 模拟太阳,光照方向一致 | - |
| Spot(聚光灯) | 类似手电筒,需设置 Range 和 Spot Angle | 需设置 Range(范围)和 Spot Angle(光锥角度) | |
| Point(点光源) | 类似小灯泡,向四周发光 | - | |
| Area(面光源) | - | - | |
| 光源颜色和模式 | Color(光源颜色) | 设置光源颜色 | - |
| Mode(光源模式) | Realtime(实时光源,性能消耗大);Baked(烘焙光源,无法动态变化);Mixed(混合光源) | 根据性能和效果需求选择模式 | |
| 光源强度和间接系数 | Intensity(光源强度) | 控制光源亮度 | - |
| Indirect Multiplier(间接系数) | 改变间接光强度,低于 1 每次反弹光更暗,大于 1 更亮 | - | |
| 阴影类型 | Shadow Type | None(关闭);Hard(生硬);Soft(柔和);Realtime Shadows(实时) | 选择阴影类型提升视觉效果 |
| Strength(强度) | 阴影暗度(0-1,越大越黑) | - | |
| Resolution(分辨率) | 阴影贴图分辨率,越高越逼真 | - | |
| Bias(偏离) | 阴影推离光源的距离 | - | |
| Normal Bias(法线偏离) | 阴影投射面沿法线收缩距离 | - | |
| Near Panel(近平面) | 渲染阴影的近裁剪面 | - | |
| 剪影和效果 | Cookie(剪影) | 投影遮罩,一般用于聚光灯显示图案 | - |
| Cookie Size(剪影大小) | 控制剪影大小 | - | |
| Draw Halo(绘制光晕) | 球形光环开关,用于蜡烛等效果 | 需在摄像机上加 FlareLayer 脚本才能在 Game 窗口看到 | |
| Flare(眩光) | 耀斑效果,类似太阳耀斑 | 需在摄像机上加 FlareLayer 脚本才能在 Game 窗口看到 | |
| 渲染模式和剔除遮罩 | Render Mode | Auto(自动);Important(重要,效果好消耗大);Not Important(非重要) | 根据性能和效果需求选择 |
| Culling Mask(剔除遮罩) | 决定哪些层的对象受到该光源影响 | - | |
| 代码控制 | intensity | 控制光的强度乘以光颜色 | light.intensity = 0.5f; |
光面板(照明设置)
| 分类 | 参数 | 说明 | 示例或注意事项 |
|---|---|---|---|
| 环境相关设置 | Skybox Material(天空盒材质) | 改变天空盒,需创建天空盒着色器材质 | - |
| Sun Source(太阳来源) | 不设置会默认使用场景中最亮的方向光 | - | |
| 环境光设置 | Source(环境光光源颜色) | Skybox(天空盒);Gradient(渐变,可单独设置天空、地平线、地面颜色) | - |
| Intensity Multiplier(环境光亮度) | 控制环境光亮度 | - | |
| Ambient Mode(环境模式) | 全局光照模式,Realtime(已弃用)、Baked(烘焙) | 需启用实时全局和全局烘焙 | |
| 其它设置 | Fog(雾开关) | 控制雾的显示 | - |
| Color(雾颜色) | 设置雾的颜色 | - | |
| Mode(雾计算模式) | Linear(线性);Exponential(指数);Exponential Square(指数平方) | 不同模式需设置不同参数(如 Linear 需设置 Start 和 End 距离) | |
| Halo Texture(光晕材质) | 光源周围光环的纹理 | - | |
| Halo Strength(光晕强度) | 光环可见性 | - | |
| Flare Fade Speed(炫光交叉淡化速度) | 耀斑淡出时间 | - | |
| Flare Strength(炫光强度) | 耀斑可见性 | - | |
| Spot Cookie(聚光灯剪影) | 聚光灯剪影纹理 | - |
物理系统 - 刚体
刚体组件参数
| 英文参数 | 说明 | 示例或注意事项 |
|---|---|---|
| Mass(质量) | 默认为千克,质量越大惯性越大 | - |
| Drag(空气阻力) | 根据力移动对象时的空气阻力大小,0 表示无阻力 | - |
| Angular Drag(角阻力) | 阻碍对象旋转的阻力,越大越不容易旋转 | - |
| Use Gravity(重力开关) | 是否受重力影响 | - |
| Is Kinematic | 开启后不受力影响,只能通过 Transform 移动 | - |
| Interpolate(插值运算) | 让刚体移动更平滑 | 可更改 FixTime,开启插值运算可平滑移动 |
| None(无插值) | 不应用插值运算 | |
| Interpolate(插值模式) | 根据前一帧变换平滑变换 | |
| Extrapolate(外推模式) | 根据当前速度预测刚体位置 | |
| Collision Detection(碰撞检测) | 防止快速移动对象穿过其它对象 | - |
| Discrete(离散检测) | 默认模式,适合一般速度物体,性能最优 | |
| Continuous(连续检测) | 适合高速物体,性能消耗较高 | |
| Continuous Dynamic(连续动态检测) | 适合快速移动物体与静止物体碰撞,性能消耗高 | |
| Continuous Speculative(连续推测检测) | 预测物体位置进行碰撞检测,性能较高 | |
| Constraints(刚体约束) | 对刚体运动的限制 | - |
| Freeze Position(冻结位置) | 停止刚体沿 X、Y、Z 轴的移动 | |
| Freeze Rotation(冻结旋转) | 停止刚体围绕 X、Y、Z 轴旋转 | |
| Info(当前刚体信息) | - | - |
刚体参数和方法
- 获取刚体组件:
rigidBody = this.GetComponent<Rigidbody>(); - AddForce 方法:相对世界坐标添加力,如
rigidBody.AddForce(Vector3.forward * 10);;相对自身朝向移动用rigidBody.AddForce(this.transform.forward * 10);。 - AddRelativeForce 方法:相对本地坐标添加力,如
rigidBody.AddRelativeForce(Vector3.forward * 10);。 - AddTorque 方法:相对世界坐标添加扭矩力,如
rigidBody.AddTorque(Vector3.up * 10);。 - AddRelativeTorque 方法:相对本地坐标添加扭矩力,如
rigidBody.AddRelativeTorque(Vector3.up * 10);。 - velocity 变量:设置刚体速度,如
rigidBody.velocity = Vector3.forward * 5;。 - AddExplosionForce 方法:模拟爆炸效果,参数为(力的大小,中心,半径),如
rigidBody.AddExplosionForce(100, Vector3.zero, 10);。 力的模式(ForceMode):Acceleration:加速度模式,忽略质量。Force:力模式,与质量有关。Impulse:冲击力模式,瞬间力。VelocityChange:瞬时速度模式,忽略质量。
- 恒力场组件:搜索
Constant Force添加到物体,无需代码即可保持力。 - 刚体的休眠:使用
IsSleeping()判断是否休眠,WakeUp()唤醒刚体。
物理系统 - 碰撞体
3D 碰撞器种类
- 盒状碰撞器 (Box Collider)
- 球状碰撞器 (Sphere Collider)
- 胶囊碰撞器 (Capsule Collider)
- 网格碰撞器 (Mesh Collider)
- 轮胎碰撞器 (Wheel Collider)
- 地形碰撞器 (Terrain Collider)
3D 碰撞器共同参数
| 英文参数 | 说明 | 示例或注意事项 |
|---|---|---|
| Edit Collider | 改变碰撞体大小 | - |
| Is Trigger | 启用后为触发器,忽略物理碰撞 | - |
| Material | 确定碰撞时的交互方式 | - |
| Center | 碰撞体在对象局部空间的中心点 | - |
常用碰撞器
| 碰撞器类型 | 英文参数 | 说明 |
|---|---|---|
| Box Collider | Size(大小) | 碰撞体在 X、Y、Z 方向的大小 |
| Sphere Collider | Radius(半径) | 球形碰撞体的半径大小 |
| Capsule Collider | Radius(半径) | 胶囊体的半径 |
| Height(高度) | 胶囊体的高度 | |
| Direction(轴向) | 胶囊体在对象局部空间的轴向 |
说明:异形物体可使用多种碰撞器组合。刚体对象的子对象碰撞器会参与碰撞检测(如金字塔预设体,仅在最高层级对象添加刚体,子对象的碰撞器仍生效)。
不常用碰撞器
| 碰撞器类型 | 英文参数 | 说明 | 示例或注意事项 |
|---|---|---|---|
| Mesh Collider | - | 性能消耗较高,默认不显示碰撞器边框,开启 Convex 才显示 | 加了刚体的网格碰撞器必须开启 Convex 才能碰撞 |
| Convex(凸面) | - | - | |
| Cooking Options | 网格烹制选项 | - | |
| Mesh(网格模型) | 引用用于碰撞的网格 | - | |
| Wheel Collider | - | 国内赛车游戏公司较少使用 | 给车的父对象加刚体,子对象加车轮碰撞器模拟汽车 |
| Mass(车轮质量) | - | - | |
| Radius(车轮半径) | - | - | |
| Wheel Damping Rate | 车轮阻尼值 | - | |
| Suspension Distance | 车轮悬架最大延伸距离 | - | |
| Force App Point Distance | 车轮受力点 | - | |
| Suspension Spring | 悬架弹簧力和阻尼力 | - | |
| Terrain Collider | - | 性能消耗较高 | - |
| Terrain Data | 地形数据 | - | |
| Enable Tree Colliders | 启用树碰撞体 | - |
物理系统 - 物理材质
| 英文参数 | 说明 |
|---|---|
| Dynamic Friction(动摩擦力) | 已移动时使用的摩擦力,0 到 1 之间,0 像冰,1 使对象迅速静止 |
| Static Friction(静摩擦力) | 静止时使用的摩擦力,0 到 1 之间,0 像冰,1 难以移动 |
| Bounciness(弹性) | 表面弹性,0 不反弹,1 无能量损失 |
| Friction Combine(摩擦力组合) | 两个碰撞对象摩擦力的组合方式:Average(平均值)、Minimum(最小值)、Maximum(最大值)、Multiply(相乘) |
| Bounce Combine(反弹组合) | 两个碰撞对象弹性的组合方式,模式与摩擦力组合相同 |
碰撞检测函数
碰撞器与触发器的核心条件对比
碰撞发生的必要条件
| 条件 | 说明 | 示例 |
|---|---|---|
| 至少1个刚体 | 提供物理模拟动力源 | 下落的立方体(带刚体)碰撞地面(无刚体但有碰撞器) |
| 双方均有碰撞器 | 定义碰撞体积 | 子弹(刚体+碰撞器) vs 敌人(碰撞器) |
| 碰撞器未开启 Is Trigger | 必须为物理碰撞模式 | 关闭触发器的胶囊体碰撞 |
触发器触发的必要条件
| 条件 | 说明 | 示例 |
|---|---|---|
| 至少1个碰撞器开启 Is Trigger | 标记为触发器,禁用物理碰撞 | 门的触发区域(碰撞器+Is Trigger) |
| 至少1个刚体(可选) | 非必须,但刚体存在时事件更稳定 | 玩家(刚体)进入触发器区域(无刚体的碰撞器) |
| 双方碰撞器重叠 | 空间上产生交集 | 球形触发器包裹玩家 |
物理碰撞检测响应函数
// 碰撞触发接触时执行
private void OnCollisionEnter(Collision collision)
{
Collider collider = collision.collider; // 碰撞对象的碰撞器
GameObject gameObject = collision.gameObject; // 碰撞对象
Transform transform = collision.transform; // 碰撞对象的位置
int contactCount = collision.contactCount;
ContactPoint[] pos = collision.contacts; // 接触点数组
print(this.name + "被" + collision.gameObject.name + "撞到了");
}
// 碰撞结束分离时执行
private void OnCollisionExit(Collision collision)
{
print(this.name + "被" + collision.gameObject.name + "结束碰撞了");
}
// 碰撞持续接触时执行(静止时不调用)
private void OnCollisionStay(Collision collision)
{
print(this.name + "一直在和" + collision.gameObject.name + "接触");
}
触发器检测响应函数
// 触发开始时执行
protected virtual void OnTriggerEnter(Collider other)
{
print(this.name + "被" + other.gameObject.name + "触发了");
}
// 触发结束时执行
private void OnTriggerExit(Collider other)
{
print(this.name + "被" + other.gameObject.name + "结束触发状态了");
}
// 触发持续时执行
private void OnTriggerStay(Collider other)
{
print(this.name + "和" + other.gameObject.name + "正在触发");
}
Unity 音效系统
音频文件导入
| 分类 | 知识点 | 说明/示例 |
|---|---|---|
| 支持格式 | WAV、MP3、OGG、AIFF | 推荐 WAV(无损)用于短音效,MP3/OGG 用于长音频 |
| 导入操作 | 直接拖拽到 Project 窗口 | - |
| 属性设置 | Force To Mono(强制单声道) | 多声道转单声道,适合语音类音频 |
| LoadType(加载类型) | Decompress On Load(小音效,内存高);Compress in Memory(大音效,内存低);Streaming(极长音频) | |
| Preload Audio Data(预加载) | 勾选:进入场景预加载;不勾选:首次播放时加载 | |
| 预览与控制 | 底部工具栏播放/暂停/循环按钮 | 支持拖拽进度条预览音频 |
音频源(AudioSource)
音频源组件参数
| 分类 | 参数 | 说明 | 适用场景 | 注意事项 |
|---|---|---|---|---|
| 基础设置 | AudioClip | 拖拽音频文件,指定播放内容 | 背景音乐、UI 音效、技能特效 | - |
| Play On Awake | 启用时对象创建即播放(默认√) | 背景音乐(√)、UI 点击音效(×) | 动态生成对象需关闭此选项 | |
| Loop | 循环播放(默认×) | 背景音乐、环境音 | 短音效禁用循环 | |
| 音量控制 | Volume | 音量(0-1,支持负数) | 主音量调节、角色音量衰减 | 结合 PlayerPrefs 存储用户设置 |
| Priority | 优先级(0-255,0 最高,默认 128) | 多音效冲突时高优先级优先播放 | 超过 32 个音效可能爆音 | |
| 2D 音效 | Stereo Pan | 左右声道平衡(-1左 ~ 1右) | UI 按钮立体声、2D 游戏音效 | 与 Spatial Blend=0 配合使用 |
| 3D 音效 | Spatial Blend | 空间混合(0=2D,1=3D) | 3D 游戏脚步声、NPC 语音 | 设为 1 时需配置 Min/Max Distance 和衰减曲线 |
| Doppler Level | 多普勒效果(0-1,模拟运动音调变化) | 车辆引擎声、飞行物音效 | 角色移动时生效,静态对象设为 0 | |
| Volume Rolloff | 衰减模式:Logarithmic(真实)、Linear(线性)、Custom(自定义) | 脚步声(Logarithmic)、环境音(Custom) | Custom 需手动绘制曲线 | |
| Min/Max Distance | 3D 音效生效范围 | 脚步声(Min=1,Max=5)、BOSS 语音(Min=2,Max=10) | Min 过小、Max 过大影响性能 | |
| 性能控制 | Bypass Effects | 跳过音效滤镜(如混响、延迟) | 性能优化,禁用非必要特效 | 仅在低端设备或复杂场景使用 |
| Output | 输出到混音器(高级功能) | 多声道混音、分组控制 | 需配合 AudioMixer 组件使用 |
音频源参数和方法
| 方法/属性 | 说明 | 示例代码 |
|---|---|---|
| Play() | 播放音频(从开头) | audioSource.Play(); |
| PlayDelayed(5f) | 延迟播放(秒为单位) | audioSource.PlayDelayed(2f); |
| Pause()/UnPause() | 暂停/恢复(保留播放位置) | audioSource.Pause(); |
| Stop() | 停止并重置位置 | audioSource.Stop(); |
| isPlaying | 检测是否播放(只读) | if (!audioSource.isPlaying) 播放完毕逻辑; |
| 多 AudioSource | 一个物体挂多个音频源,独立控制 | AudioSource[] audios = GetComponents<AudioSource>(); audios[1].Play(); |
麦克风录制
| 功能 | 方法/属性 | 示例代码 |
|---|---|---|
| 获取设备 | Microphone.devices | string[] mics = Microphone.devices; |
| 开始录制 | Microphone.Start(设备名, 循环, 时长, 采样率) | audioClip = Microphone.Start(null, false, 10, 44100); |
| 停止录制 | Microphone.End(设备名) | Microphone.End(null); |
| 获取音频数据 | AudioClip.GetData(数组, 起始样本) | float[] data = new float[clip.channels * clip.samples];clip.GetData(data, 0); |
| 播放录音 | 绑定 AudioSource 播放 | audioSource.clip = 录音Clip; audioSource.Play(); |
36.3 面试题精选
基础题
1. Unity 工程中 Assets 与 ProjectSettings 各自存什么?删错会怎样?
题目
请说明 Assets 与 ProjectSettings 在项目目录里的大致职责;若只删其中一类文件夹,工程通常还能否打开,可能有什么后果?
深入解析
- Assets:工程内的资源与脚本入口,是你在 Project 窗口中管理的主要内容;删除后需重新导入或还原,否则场景与资源引用会丢失。
- ProjectSettings:项目级设置(如部分标签、图层、Player/Quality 等配置)的落盘位置;与
Assets同级。正文强调删除可能导致部分设置丢失。 - 正文还提到:除 Assets 外的生成文件夹删掉后工程往往仍能打开,但会触发重新生成/重新加载;ProjectSettings 被删则自定义标签、图层等可能丢失。
答题示例
Assets放资源与脚本,是编辑器主要编辑内容;ProjectSettings存项目级配置。只删部分缓存类文件夹有时还能打开工程但会重算;删
ProjectSettings容易导致标签、图层等设置丢失,需谨慎。
参考文章
- 2.Unity环境搭建
- 6.Unity界面基础-工具栏和父子关系
2. 为什么 Inspector 能显示脚本字段?SerializeField 和 public 各解决什么问题?
题目
Unity 如何在 Inspector 中显示挂载脚本上的字段?[SerializeField] 与 public 在「能在 Inspector 上编辑」这件事上各是什么角色?
深入解析
- 引擎通过 反射 读取脚本类型上的可序列化字段,再在 Inspector 中生成控件;类名与文件名一致等约定便于定位类型信息(见
7.Unity工作原理-反射机制和游戏场景)。 - public:默认序列化并在 Inspector 显示(除非
[HideInInspector])。 [SerializeField]:让 private/protected 等本不对外暴露的字段仍参与序列化并在 Inspector 显示,兼顾封装与调试/策划可调参。
答题示例
Inspector 依赖序列化与反射把字段变成可编辑控件。
public默认可序列化显示;[SerializeField]用来让私有/保护字段也能序列化并显示,常用在不想对外公开字段名的同时要在面板上调参。
参考文章
- 7.Unity工作原理-反射机制和游戏场景
- 11.Unity脚本基础-Inspector窗口可编辑的变量
3. 预设体实例上的 Overrides、Apply、Unpack 分别是什么操作?
题目
场景里的 Prefab 实例被改到与磁盘上的 Prefab 不一致时,Inspector 会出现覆盖相关 UI。请说明 Apply(应用到 Prefab)、Unpack、Unpack Completely 与「仅改当前实例」之间的典型区别。
深入解析
- 实例覆盖:只影响当前场景中的实例,不写入源 Prefab。
- Apply / 覆盖到 Project:把当前实例上的改动写回 Prefab 资源,通常会影响所有引用该 Prefab 的实例(除非走变体等进阶流程,入门篇以「同步到所有实例」心智为主)。
- Unpack:实例与 Prefab 断开关联,层级上父级变为普通对象(子级若仍是 Prefab 可保留子 Prefab)。
- Unpack Completely:递归解除,整棵子树都不再与各自 Prefab 关联。
答题示例
实例改动可先只留在场景里;点 Apply 会把改动写回磁盘上的 Prefab,影响其它引用。
Unpack 是解除一层关联,Unpack Completely 是整棵都拆成普通对象,适合不再做 Prefab 管理的物体。
参考文章
- 8.Unity工作原理-预设体和资源包的导入导出
4. GameObject.Find 类 API 的共性限制与性能风险?
题目
GameObject.Find、FindWithTag、FindObjectOfType 等查找方法有哪些共同限制?为何不建议在 Update 里频繁调用?
深入解析
- 正文强调:只能找到激活对象,找不到失活对象。
- 多个对象同名或同 Tag 时,无法保证找到哪一个(单对象查找)。
Find遍历场景对象;FindObjectOfType还要遍历组件,成本更高。- 因此适合在初始化时缓存引用,避免每帧全场景搜索。
答题示例
这些查找基本都只扫激活对象,且多匹配时结果不确定。
Find / FindObjectOfType 代价高,应缓存引用,别在 Update 里每帧 Find。
参考文章
- 14.Unity重要组件和API-GameObject
5. Time.deltaTime 与 Time.unscaledDeltaTime 何时选用?
题目
游戏暂停(timeScale = 0)时,若仍希望某段位移或 UI 动画继续按真实时间推进,应使用哪个帧间隔?与 deltaTime 的差别是什么?
深入解析
deltaTime受timeScale影响,暂停时为 0,适合「随游戏时间暂停」的移动。unscaledDeltaTime不受timeScale影响,适合暂停菜单动画、不受暂停影响的相机/UI 等。- 正文示例:
timeScale改变时,deltaTime与unscaledDeltaTime的比值关系可用于理解二者差异。
答题示例
暂停后还想动,用
unscaledDeltaTime;希望跟游戏时间一起停,用deltaTime。
参考文章
- 15.Unity重要组件和API-Time
6. Screen.currentResolution 与 Screen.width / Screen.height 有何区别?
题目
做 UI 或按像素布局时,应优先用哪一组尺寸?currentResolution 与 Game 窗口宽高分别对应什么?
深入解析
- currentResolution:当前显示器分辨率(
Resolution),不是 Game 视图画布大小。 - Screen.width / height:当前 Game 窗口的像素宽高,代码里按「玩家看到的游戏画面」计算时通常用它们。
答题示例
currentResolution 是显示器;Screen.width/height 是 Game 窗口。跟游戏画面相关的布局用后一组。
参考文章
- 22.Unity重要组件和API-Screen
7. 使用 Camera.main 的前提与常见坑?
题目
Camera.main 在什么条件下可用?场景里多个带 MainCamera 标签的相机会怎样?
深入解析
- 需要场景中存在 tag 为 MainCamera 且启用的摄像机,否则为 null 或报错(正文)。
- 多个 MainCamera 时,获取结果不确定是哪一个,应避免。
答题示例
必须有一个启用的主摄且 tag 正确;多个 MainCamera 时 main 指代不稳定,应改显式引用。
参考文章
- 24.Unity重要组件和API-Camera代码控制
进阶题
1. 场景文件 .unity 本质上是什么?把别的场景拖进当前场景一般做什么?
题目
请说明 .unity 场景文件在磁盘上的本质;编辑器里把另一个场景拖入当前场景,常见用途是什么?
深入解析
- 正文:
.unity是 特殊格式的配置文件,保存场景设置、场景中的对象及组件等信息。 - 多场景叠加:把别的场景拖进当前场景,主要用于 把其它场景里的对象拿到当前场景 使用,一般不常用。
答题示例
.unity本质是文本/序列化配置,描述场景与对象状态。拖入其它场景多半是为了把其中的对象合并到当前场景继续编辑,属于临时搬运,不是常规关卡流程的唯一方式。
参考文章
- 7.Unity工作原理-反射机制和游戏场景
2. Awake、OnEnable、Start 的调用时机与大致顺序?
题目
同一对象上挂载的脚本里,Awake、OnEnable、Start 分别在什么时机被调用?对象从失活到激活时,哪些会再次触发?
深入解析
- Awake:脚本/对象创建时,类似「初始化入口」,早于首次帧更新前的 Start。
- OnEnable:对象激活时调用;运行开始时若默认激活会调用一次。
- Start:第一次帧更新之前执行,比 Awake 晚。
- OnDisable / OnDestroy:失活、销毁时;销毁前会先失活,故先 OnDisable 再 OnDestroy(与正文一致)。
答题示例
同一对象上通常是 Awake → OnEnable(若激活)→ Start(首次帧前)。
对象被 SetActive(false) 再打开会再走 OnEnable,Awake 只来一次。
参考文章
- 10.Unity脚本基础-生命周期函数
3. GetComponentInChildren 的 includeInactive 何时要设为 true?
题目
GetComponentInChildren<T>(bool includeInactive = false) 中第二个参数为 true 时与默认 false 有何区别?什么业务场景需要传 true?
深入解析
- false(默认):子对象未激活时不会在该子对象上查找组件。
- true:即使子对象 inactive 也会参与查找。
- 适用:逻辑里常把子物体先隐藏,但仍需通过代码取到其上脚本(如池、预置隐藏的特效节点)。
答题示例
默认 false 会跳过失活子物体上的组件;需要操作「隐藏但仍存在」的子节点时传 true。
参考文章
- 12.Unity脚本基础-MonoBehaviour中的重要内容-基础属性和组件的获取
4. Destroy 与 DestroyImmediate 有何区别?为何优先 Destroy?
题目
删除游戏对象时,Destroy 与 DestroyImmediate 在执行时机和对卡顿的影响上有何不同?正文推荐默认用哪一个?
深入解析
Destroy:异步,本帧打标记,通常下一帧真正移除,降低单帧尖刺。DestroyImmediate:同步立即从内存移除,可能造成卡顿;文档亦建议优先Destroy。- 仅在编辑器脚本或特殊必须立即释放的场景再考虑
DestroyImmediate。
答题示例
Destroy 是延迟销毁,更平滑;DestroyImmediate 立刻删,容易卡帧。
游戏运行时一般优先 Destroy。
参考文章
- 14.Unity重要组件和API-GameObject
5. position 与 localPosition 何时与 Inspector 上看到的「一致」?
题目
什么情况下 transform.position 与面板上的位置读数会一致?何时应以 localPosition 为准?
深入解析
- 无父对象或父对象在世界原点时,
position与localPosition往往与面板局部读数对齐关系更简单。 - 有父对象且父已偏移/旋转时,世界坐标
position与 本地坐标localPosition一般不同;若要以面板局部坐标改物体,应改 localPosition(正文强调)。
答题示例
无父或父在原点时二者关系简单;有父物体时世界坐标和本地坐标分离,对齐 Inspector 局部应用 localPosition。
参考文章
- 16.Unity重要组件和API-Transform位置和位移
6. Transform.Find 与 GameObject.Find 在范围与失活对象上的差异?
题目
transform.Find("子物体名") 与 GameObject.Find("名字") 相比,各自搜索范围是什么?谁能找到失活对象?
深入解析
GameObject.Find:全场景按名字,仅激活对象。Transform.Find:仅在当前 Transform 的直接子级按名字查找,找不到孙子;但可以找到失活的子对象(正文多次强调与 GameObject 查找的对比)。
答题示例
GameObject.Find 扫全场景只要激活的;Transform.Find 只查直接子节点,但能查到失活子物体。
参考文章
- 19.Unity重要组件和API-Transform父子关系
7. 多摄像机叠加时,Depth 与 Clear Flags(如 Depth only)如何配合?
题目
两个摄像机要同时贡献画面(例如一个画 3D、一个画 UI 或不同层)时,渲染顺序与 Clear Flags、Culling Mask 通常怎么配?
深入解析
- Depth 越大越后渲染,后渲染的会叠在先渲染的之上(正文)。
- 上层摄像机常选 Depth only,只清深度、背景透明,再配合 Culling Mask 只渲染需要的层,实现叠加。
- Viewport Rect 可做分屏(0~1 比例划分 Game 视图)。
答题示例
低 Depth 先画全屏或底图,高 Depth 用 Depth only 只画自己那一层,避免把底下全清掉。
参考文章
- 23.Unity重要组件和API-Camera参数
8. 物理「碰撞回调」与「触发器回调」分别在什么条件下触发?
题目
OnCollisionEnter 与 OnTriggerEnter 各自要求碰撞器、刚体、Is Trigger 处于什么状态?表现上有何不同?
深入解析
- 物理碰撞:双方有碰撞器、至少一方有刚体,且 Is Trigger 关闭,会有力的响应;回调为 OnCollision*,参数为
Collision。 - 触发器:至少一方碰撞器 Is Trigger 开启,通常无物理阻挡,只走 OnTrigger*,参数为
Collider。 - 回调挂在带刚体的父物体上才能正确收到(正文强调子物体挂脚本收不到的情况)。
答题示例
真撞用 Collision,要穿过去只检测用 Trigger;触发器不产生常规物理挤压。
参考文章
- 30.核心系统-物理系统-碰撞检测函数
9. Rigidbody.isKinematic 开启后行为如何?
题目
勾选 Is Kinematic 后,刚体还受力和重力吗?位移应通过什么方式驱动?常见用途?
深入解析
- 不受物理引擎力/重力驱动,只能用 Transform(或动画)去动(正文)。
- 适合移动平台、由程序完全控制轨迹的物体,或需动画驱动又参与某些物理查询的场景。
答题示例
Kinematic 等于告诉物理「别推我」,位置自己用 Transform 改;仍可有碰撞器参与检测,但不按动力学积分。
参考文章
- 27.核心系统-物理系统-刚体
深度题
1. 脚本执行顺序(Script Execution Order)解决什么问题?滥用会怎样?
题目
Unity 中同一帧内多个脚本都可能写 Awake/Start/Update,默认顺序不确定时可能产生依赖问题。请说明 Script Execution Order 如何工作,以及项目中应如何克制使用。
深入解析
- 在 Edit → Project Settings → Script Execution Order(或等价入口)中为脚本指定 order 数值,越小越先执行(见
9.Unity脚本基础-脚本基本规则)。 - 用途:在无法通过显式引用、事件拆分解决的初始化顺序问题时,作为最后手段固定顺序。
- 风险:全局顺序表难维护,跨模块耦合加重;更好的做法是减少跨脚本初始化依赖、用显式调用、事件或单入口初始化。
答题示例
执行顺序表给脚本一个全局优先级,数越小越早跑 Awake/Start 等。
适合救急依赖顺序问题,但长期依赖会让模块耦合变差,应配合架构拆分减少硬顺序。
参考文章
- 9.Unity脚本基础-脚本基本规则
2. FixedUpdate 与 Update、物理帧之间是什么关系?
题目
请说明 FixedUpdate 与 Update 的区别;为什么刚体受力通常写在 FixedUpdate 里;Time.fixedDeltaTime 与游戏帧 deltaTime 为何可能不一致?
深入解析
- Update:每游戏帧调用,帧率波动时调用间隔变化。
- FixedUpdate:每 固定时间步调用,用于与物理模拟同步;步长可在 Project Settings → Time 中配置(正文与示例代码注释)。
- 刚体模拟在固定步长上积分,故对刚体施加力、改
velocity等通常放 FixedUpdate,避免与帧率绑定导致不稳定。 Time.deltaTime受timeScale等影响;物理步长由fixedDeltaTime等控制,与渲染帧不同步。
答题示例
Update 跟显示器帧走,FixedUpdate 跟物理固定步长走。
施力、改刚体速度用 FixedUpdate;deltaTime 是帧间隔,fixedDeltaTime 是物理步长,二者不必相等。
参考文章
- 10.Unity脚本基础-生命周期函数
3. SetParent 的 worldPositionStays 为 true 或 false 时分别发生什么?
题目
transform.SetParent(新父, worldPositionStays) 第二个参数为 true 与 false 时,子物体在场景中的世界位置与 Inspector 本地坐标分别如何变化?
深入解析
- true:尽量保持世界空间下的位置、旋转、缩放不变,重新计算相对新父的本地变换。
- false:不保留世界状态,把当前世界坐标下的变换直接写入本地坐标(正文描述为「粗暴」赋到面板上),物体在世界里可能跳变。
答题示例
true 保持世界里看起来不动,只改父子关系;false 可能让本地坐标被直接覆盖,视觉上容易跳。
参考文章
- 19.Unity重要组件和API-Transform父子关系
4. TransformPoint 与 TransformDirection、TransformVector 在「是否受缩放影响」上如何区分?
题目
本地转世界时,何时用 TransformPoint?方向类 API 中 Direction 与 Vector 为何一个不受缩放影响、一个受缩放影响?
深入解析
- TransformPoint:处理本地空间中的点(位置),受缩放影响,用于「面前 N 米生成物体」等。
- TransformDirection:方向向量,不受缩放(只旋转)。
- TransformVector:表示带缩放意义的向量变换,受缩放影响。
- 世界转本地有对应的
InverseTransform*三组(正文表格与示例)。
答题示例
点用 Point,带缩放;纯朝向用 Direction,不受缩放;需要缩放进度的位移用 Vector。
参考文章
- 20.Unity重要组件和API-Transform坐标转换
5. Input.GetAxis 与 Input.GetAxisRaw 的区别与典型用法?
题目
虚拟轴输入中,GetAxis 与 GetAxisRaw 返回值有何不同?分别适合什么操作手感?
深入解析
- GetAxis:带平滑/渐变,可在 -1~1 间出现小数,适合需要加速缓冲的相机、载具移动。
- GetAxisRaw:无平滑,仅 -1、0、1,适合需要立即响应的离散操作(如技能是否按下,原文以 Fire1 为例)。
答题示例
GetAxis 有过渡更顺滑;GetAxisRaw 只有三态,适合要明确开关感的操作。
参考文章
- 21.Unity重要组件和API-Input
6. ScreenToWorldPoint 里为什么要改 z?和 Physics.Raycast 选点的思路差异?
题目
用 Camera.main.ScreenToWorldPoint(Input.mousePosition) 前为何常要给 z 赋非零值?z 的几何含义是什么?
深入解析
- 鼠标
Screen坐标z默认为 0;直接转换会得到近平面附近的一点,深度信息不足。 - 设置
z为到摄像机的距离(世界单位),得到的是沿摄像机前方、该深度处的世界坐标点(正文「横截面」心智)。 - 精确贴地/贴物更常用 Raycast;ScreenToWorldPoint 适合固定深度平面上的跟随等。
答题示例
z 表示离相机多远,不设就都在近裁平面附近一团。要贴场景表面一般用射线。
参考文章
- 24.Unity重要组件和API-Camera代码控制
7. ForceMode.Acceleration、Force、Impulse、VelocityChange 如何选用?
题目
Rigidbody.AddForce 的第二个参数几种模式,与质量、是否按时间步累积的关系大致怎样?各举一个适用场景。
深入解析
- Acceleration:加加速度,忽略质量,同等力下轻重物体更易获得相近线加速度。
- Force:持续力,与质量有关(F=ma 心智)。
- Impulse:瞬时冲量,与质量有关,适合爆炸、单次击打。
- VelocityChange:瞬时改速度,忽略质量。
- 具体数值与 FixedUpdate 步长结合(正文动量示例)。
答题示例
持续推用 Force;要无视质量用 Acceleration;一下撞飞用 Impulse;直接设速度差用 VelocityChange。
参考文章
- 31.核心系统-物理系统-刚体加力
8. 场景中 AudioListener 与 Spatial Blend 分别要解决什么问题?
题目
AudioListener 一般挂在哪里、同一活跃场景允许多个吗?Spatial Blend 从 0 到 1 变化代表什么?
深入解析
- AudioListener 相当于耳朵,通常挂在主摄像机;工程里一般只保留一个启用的 Listener(多摄像机时新建相机会自带组件,需避免重复,正文以主摄为默认挂载点)。
- Spatial Blend:0 为 2D(不受空间距离衰减),1 为 3D(受距离与 3D 设置影响),中间为混合。
答题示例
Listener 一个场景一个耳朵,常挂主摄。Spatial Blend 控制从纯 2D UI 声到 3D 空间声的过渡。
参考文章
- 33.核心系统-音效系统-音频源和音频监听脚本
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com