25.总结
25.1 知识点

学习的主要内容

总结讲什么

Unity基础中知识点的重要性

如何学好Unity

强调

25.2 核心要点速览
Mathf
常用基础方法(一般计算一次)
| 方法 | 说明 | 参数/返回值 | 示例代码 | 游戏场景 |
|---|---|---|---|---|
| Mathf.PI | 圆周率(3.14159265358979) | float(只读) | print(Mathf.PI); // 3.141593 |
圆周长、弧度计算 |
| Mathf.Abs(float f) | 取绝对值 | f:数值 返回:绝对值 |
Mathf.Abs(-5f); // 5 |
距离计算、伤害绝对值处理 |
| Mathf.Clamp(float val, float min, float max) | 钳制数值(超出范围取边界值) | val:原始值 min/max:边界 |
Mathf.Clamp(HP, 0, 100); |
血量、进度条限制 |
| Mathf.Lerp(float a, float b, float t) | 线性插值(t 0→1时a→b平滑过渡) | a:起始值 b:目标值 t:插值系数 |
pos = Mathf.Lerp(startPos, endPos, 0.5f); |
平滑移动、渐变动画 |
| Mathf.RoundToInt(float f) | 四舍五入取整 | f:浮点数 返回:整数 |
Mathf.RoundToInt(3.6f); // 4 |
分数显示、帧数统计 |
| Mathf.Sqrt(float f) | 平方根 | f:数值 返回:平方根 |
Mathf.Sqrt(25f); // 5 |
距离计算(替代 Vector3.Distance) |
| Mathf.CeilToInt(float f) | 向上取整(≥f的最小整数) | f:浮点数 返回:整数 |
Mathf.CeilToInt(1.3f); // 2 |
资源计数(购买数量向上取整) |
| Mathf.FloorToInt(float f) | 向下取整(≤f的最大整数) | f:浮点数 返回:整数 |
Mathf.FloorToInt(9.6f); // 9 |
资源计数(可用资源整数量) |
| Mathf.Max(params float[] values) | 返回多个值中的最大值 | values:可变长参数列表 返回:最大值 |
Mathf.Max(1, 2, 3, 4, 5); // 5 |
比较角色属性(如攻击力最大值) |
| Mathf.Min(params float[] values) | 返回多个值中的最小值 | values:可变长参数列表 返回:最小值 |
Mathf.Min(1, 2, 3, 4, 5); // 1 |
比较敌人防御(找最小防御敌人) |
| Mathf.Pow(float f, float p) | 返回f的p次幂 | f:底数 p:指数 返回:幂值 |
Mathf.Pow(2, 3); // 8 |
伤害计算(技能伤害随等级增长) |
| Mathf.IsPowerOfTwo(int value) | 判断是否是2的n次方 | value:整数 返回:布尔值 |
Mathf.IsPowerOfTwo(4); // true |
纹理尺寸判断(图形处理需求) |
| Mathf.Sign(float f) | 返回f的符号(正数/0→1,负数→-1) | f:数值 返回:1或-1 |
Mathf.Sign(-10); // -1 |
方向判断(角色移动方向正负) |
插值运算(Lerp)深度解析
| 用法 | 代码示例 | 效果 | 适用场景 |
|---|---|---|---|
| 用法一:先快后慢 | pos = Mathf.Lerp(pos, target, Time.deltaTime * speed); |
初始速度快,逐渐趋近目标(阻尼效果) | 镜头跟随、物体平滑停止 |
| 用法二:匀速接近 | t += Time.deltaTime; pos = Mathf.Lerp(start, end, t); |
t累加,匀速到达目标(t≥1时固定为end) | 倒计时、线性动画 |
| 扩展:颜色插值 | color = Color.Lerp(Color.red, Color.blue, t); |
t 0→1时红→蓝平滑过渡 | UI渐变、技能特效 |
三角函数与角度转换
| 方法/常量 | 说明 | 参数/返回值 | 示例代码 | 游戏场景 |
|---|---|---|---|---|
| Mathf.Rad2Deg | 弧度转角度常量(1弧度≈57.29578°) | float(只读) | float deg = rad * Mathf.Rad2Deg; |
欧拉角显示、UI角度标注 |
| Mathf.Deg2Rad | 角度转弧度常量(1°≈0.01745329弧度) | float(只读) | float rad = deg * Mathf.Deg2Rad; |
三角函数计算、旋转方向 |
| Mathf.Sin(float rad) | 正弦函数(输入弧度) | rad:弧度 返回:正弦值 |
Mathf.Sin(90 * Mathf.Deg2Rad); // 1 |
正弦波运动(摆锤、波浪效果) |
| Mathf.Cos(float rad) | 余弦函数(输入弧度) | rad:弧度 返回:余弦值 |
Mathf.Cos(0 * Mathf.Deg2Rad); // 1 |
方向向量计算(敌人巡逻路径) |
| Mathf.Atan2(float y, float x) | 反正切函数(返回弧度,计算y/x的角度) | y/x:坐标差值 返回:弧度 |
float angle = Mathf.Atan2(y, x) * Mathf.Rad2Deg; |
角色朝向、子弹追踪 |
坐标系
四大坐标系对比
| 坐标系 | 定义 | 原点 | 轴方向 | 典型场景 |
|---|---|---|---|---|
| 世界坐标系 | 全局唯一,所有物体的绝对位置基准 | 场景中心(0,0,0) | X→右,Y→上,Z→前(默认) | 物体位置存储、碰撞检测 |
| 本地坐标系 | 相对于父对象的局部坐标系 | 父对象的位置 | 随父对象旋转/缩放而变化 | 子对象相对父对象的位置调整 |
| 屏幕坐标系 | 基于屏幕像素的2D坐标系 | 左下角(0,0) | X→右,Y→上,Z→深度(摄像机距离) | 鼠标位置获取、UI元素定位 |
| 视口坐标系 | 归一化的屏幕坐标系(0-1范围) | 左下角(0,0) | X→右,Y→上,Z→深度(0-1) | 分屏显示、全屏适配 |
坐标转换核心方法
世界 ↔ 本地(Transform 方法)
| 方向 | 方法 | 说明 | 参数/返回值 | 游戏场景 |
|---|---|---|---|---|
| 世界→本地(点) | InverseTransformPoint(Vector3 worldPos) | 将世界点转为本地坐标(含位置+缩放+旋转) | worldPos:世界点 返回:本地点 |
子对象相对于父对象的位置计算 |
| 世界→本地(方向) | InverseTransformDirection(Vector3 worldDir) | 仅转换方向(忽略缩放,保留旋转) | worldDir:世界方向 返回:本地方向 |
子弹方向相对于坦克的朝向调整 |
| 本地→世界(点) | TransformPoint(Vector3 localPos) | 将本地点转为世界坐标(含父对象变换) | localPos:本地点 返回:世界点 |
炮塔位置转世界坐标生成子弹 |
| 本地→世界(方向) | TransformDirection(Vector3 localDir) | 仅转换方向(忽略缩放,保留旋转) | localDir:本地方向 返回:世界方向 |
角色朝向转世界方向(技能释放) |
世界 ↔ 屏幕(Camera 方法)
| 方向 | 方法 | 说明 | 关键参数 | 游戏场景 |
|---|---|---|---|---|
| 世界→屏幕 | WorldToScreenPoint(Vector3 worldPos) | 世界点转屏幕坐标(Z为到摄像机的距离) | worldPos:世界点 返回:屏幕点(x,y,depth) |
头顶血条位置计算(UI锚点) |
| 屏幕→世界 | ScreenToWorldPoint(Vector3 screenPos) | 屏幕点转世界坐标(需设置Z轴,默认Z=0为近裁剪平面) | screenPos:屏幕点(需设Z) 返回:世界点 |
鼠标点击生成物体(需Z=10) |
世界 ↔ 视口(Camera 方法)
| 方向 | 方法 | 说明 | 坐标范围 | 游戏场景 |
|---|---|---|---|---|
| 世界→视口 | WorldToViewportPoint(Vector3 worldPos) | 世界点转视口坐标(X/Y∈[0,1],Z为深度) | worldPos:世界点 返回:视口点(0-1) |
检测物体是否在屏幕内(视口坐标>0且<1) |
| 视口→世界 | ViewportToWorldPoint(Vector3 viewportPos) | 视口点转世界坐标(X/Y∈[0,1],Z为深度) | viewportPos:视口点(需设Z) 返回:世界点 |
分屏游戏中左半屏生成物体(X=0.5) |
Vector3
基础属性(实例成员)
| 方法/属性 | 说明 | 参数/返回值 | 示例代码 | 游戏场景 | 注意事项 |
|---|---|---|---|---|---|
| magnitude | 向量的模长(只读) | float(返回长度) | Vector3 dir = target - transform.position;<br>float distance = dir.magnitude; |
敌人距离检测、射程判断 | 等价于Mathf.Sqrt(x²+y²+z²),性能略低于Distance方法 |
| normalized | 单位向量(只读,方向不变,长度1) | Vector3(返回单位向量) | Vector3 normDir = dir.normalized; |
方向移动(如角色朝向) | 若原向量为零向量,返回零向量(避免除以零) |
| sqrMagnitude | 模长平方(只读,性能更高) | float(返回长度²) | if (dir.sqrMagnitude < 25) 触发攻击; |
快速距离比较(避免开平方) | 用于判断“是否在半径5内”时,比magnitude快约50% |
静态方法(基础运算)
| 方法 | 说明 | 参数/返回值 | 示例代码 | 游戏场景 | 几何意义 |
|---|---|---|---|---|---|
| Distance(a, b) | 两点间距离(等价于(b-a).magnitude) | a,b:Vector3 返回:float |
float dist = Vector3.Distance(A, B); |
子弹射程、寻路范围 | 欧几里得距离公式 |
| Angle(from, to) | 两向量夹角(0°~180°,度数) | from,to:Vector3 返回:float |
float angle = Vector3.Angle(forward, dir); |
扇形攻击范围、视野检测 | 基于点乘计算,忽略方向(仅角度) |
| Cross(a, b) | 叉乘(左手坐标系,返回垂直于a、b的向量) | a,b:Vector3 返回:Vector3 |
Vector3 up = Vector3.Cross(right, forward); |
判断左右方位(如敌人侧击) | 右手法则(数学)→ 左手法则(Unity) |
| Dot(a, b) | 点乘(返回标量,a·b = |a||b|cosθ) | a,b:Vector3 返回:float |
float dot = Vector3.Dot(forward, dir); |
方向相似性判断(如面向目标) | 正数→方向相近,负数→方向相反 |
| Lerp(a, b, t) | 线性插值(t∈[0,1],a到b的直线过渡) | a,b:Vector3 t:float 返回:Vector3 |
pos = Vector3.Lerp(start, end, Time.deltaTime*3); |
平滑移动、镜头跟随 | t>1时固定为b,t<0时固定为a |
| Slerp(a, b, t) | 球形插值(保持向量长度,球面过渡) | a,b:Vector3 t:float 返回:Vector3 |
dir = Vector3.Slerp(forward, targetDir, 0.1f); |
平滑旋转、物体轨迹 | 常用于方向向量插值,避免“翻折” |
| MoveTowards(current, target, maxDistanceDelta) | 向目标移动(自动限制步长) | current,target:Vector3 maxDistanceDelta:float 返回:Vector3 |
transform.position = Vector3.MoveTowards(pos, target, speed*dt); |
角色寻路、敌人追击 | 等价于Lerp+Clamp,更安全 |
Quaternion
构造与属性
| 方法/属性 | 类型 | 说明 | 参数/返回值 | 示例代码 | 游戏场景 | 注意事项 |
|---|---|---|---|---|---|---|
| Quaternion() | 构造函数 | 初始化四元数(默认单位四元数) | - | Quaternion q = new Quaternion(); |
空旋转初始化 | 等价于Quaternion.identity |
| AngleAxis(angle, axis) | 静态 | 绕轴旋转(角度→四元数) | angle:float(度数) axis:Vector3 返回:Quaternion |
Quaternion q = Quaternion.AngleAxis(90, Vector3.up); |
绕Y轴旋转90°(如门开合) | 轴向量无需归一化,内部自动处理 |
| Euler(x, y, z) | 静态 | 欧拉角转四元数(按Z→X→Y顺序旋转) | x,y,z:float(度数) 返回:Quaternion |
Quaternion q = Quaternion.Euler(30, 0, 0); |
欧拉角转四元数(角色转头) | 可能引发万向节死锁,避免直接修改欧拉角 |
| eulerAngles | 实例属性 | 四元数转欧拉角(只读,自动处理-180~180°) | Vector3(返回欧拉角) | Vector3 angles = q.eulerAngles; |
调试旋转角度 | 非连续旋转时可能跳跃(如179°→-179°) |
| identity | 静态属性 | 单位四元数(无旋转) | Quaternion(只读) | transform.rotation = Quaternion.identity; |
重置旋转(如复位操作) | 等价于new Quaternion(0,0,0,1) |
核心方法(旋转控制)
| 方法 | 类型 | 说明 | 参数/返回值 | 示例代码 | 游戏场景 | 对比/优化 |
|---|---|---|---|---|---|---|
| Lerp(a, b, t) | 静态 | 线性插值(非球面,可能翻转) | a,b:Quaternion t:float 返回:Quaternion |
q = Quaternion.Lerp(rotA, rotB, 0.5f); |
快速插值(如UI旋转) | 比Slerp快20%,但可能“翻折”(适合非方向敏感场景) |
| Slerp(a, b, t) | 静态 | 球形插值(保持旋转方向,球面过渡) | a,b:Quaternion t:float 返回:Quaternion |
transform.rotation = Quaternion.Slerp(rot, targetRot, dt*5); |
平滑转向(如角色转头) | 计算开销略高,适合方向敏感场景(如镜头旋转) |
| LookRotation(dir, up) | 静态 | 看向目标方向(自动处理Up轴) | dir:Vector3(目标方向) up:Vector3(默认Vector3.up) 返回:Quaternion |
Quaternion look = Quaternion.LookRotation(target - pos); |
角色/镜头看向目标(敌人) | 等价于Transform.LookAt,但更灵活(可自定义Up轴) |
| operator * (q1, q2) | 运算符 | 四元数乘法(q1先旋转,q2后旋转) | q1,q2:Quaternion 返回:Quaternion |
transform.rotation *= Quaternion.AngleAxis(10, Vector3.up); |
绕自身轴连续旋转(飞机翻滚) | 左乘:世界坐标系;右乘:本地坐标系(默认右乘) |
| operator * (q, vec) | 运算符 | 向量绕四元数旋转 | q:Quaternion vec:Vector3 返回:Vector3 |
Vector3 newDir = q * Vector3.forward; |
子弹方向计算(扇形发射) | 等价于Transform.TransformDirection(vec) |
延迟函数
核心方法
| 方法 | 作用 | 参数 | 返回值 | 执行时机 | 典型场景 |
|---|---|---|---|---|---|
| Invoke(method, time) | 延时执行一次方法 | method: 函数名(字符串) time: 延迟时间(秒) |
无 | 第1次在time秒后执行 | 倒计时提示、技能冷却触发 |
| InvokeRepeating(method, time, repeat) | 延时重复执行方法 | method: 函数名 time: 首次延迟 repeat: 重复间隔(秒) |
无 | 第1次time秒后,之后每repeat秒执行 | 定时刷新(敌人巡逻、自动射击) |
| CancelInvoke([method]) | 取消延迟函数 | method: 可选(取消指定函数,不填则取消所有) | 无 | 立即生效 | 对象销毁前清理任务(停止重复播放) |
| IsInvoking([method]) | 判断是否存在待执行的延迟函数 | method: 可选(检查指定函数,不填则检查所有) | bool | 实时判断 | 避免重复开启延迟(防止多次点击) |
对象状态对延迟函数的影响
| 对象状态 | Invoke/InvokeRepeating | CancelInvoke | 恢复执行 |
|---|---|---|---|
| 激活(Active) | 正常执行 | 正常取消 | - |
| 失活(Inactive) | 继续执行(不受影响) | 正常取消 | 激活后继续(仅InvokeRepeating) |
| 销毁(Destroy) | 立即终止 | 无效 | 无法恢复 |
| 脚本禁用(Disable) | 继续执行(生命周期未结束) | 正常取消 | 启用后继续(仅InvokeRepeating) |
延迟函数 vs 协程(Coroutine)
| 对比项 | 延迟函数(Invoke) | 协程(Coroutine) |
|---|---|---|
| 执行控制 | 简单(字符串函数名) | 灵活(可传参、中途中断) |
| 性能 | 较高(底层优化) | 中等(迭代器开销) |
| 参数支持 | 不支持(需代理) | 支持(通过IEnumerator传参) |
| 生命周期依赖 | 独立于对象激活状态 | 依赖MonoBehaviour生命周期 |
| 适用场景 | 简单定时任务(冷却、提示) | 复杂流程控制(动画插值、异步加载) |
协同程序
协程方法
| 分类 | 方法 | 作用 | 执行机制 | 适用场景 | 核心特性 |
|---|---|---|---|---|---|
| 启动 | StartCoroutine(ie) | 开启协程(迭代器函数) | 主线程分时执行,依赖yield控制 | 异步加载、批量操作(创建1000个物体) | 可传参,通过迭代器分步执行 |
| 关闭 | StopCoroutine(ie/c) | 停止指定协程(推荐用返回的Coroutine对象) | 立即终止 | 中途取消(如加载失败) | StopAllCoroutines()清空所有 |
| 暂停标记 | yield return X | - null/数字:下一帧 - WaitForSeconds(t):等待t秒 - WaitForFixedUpdate():物理帧 - WaitForEndOfFrame():渲染后 |
调度器根据X决定恢复时机 | 分步逻辑(动画插值、截图) | 迭代器本质,代码块拆分为多段执行 |
| 生命周期 | - | 组件失活继续执行,物体/组件销毁终止 | - | - | 依赖MonoBehaviour生命周期,适合Unity对象操作 |
多线程与协程对比表
| 对比项 | 多线程(Thread) | 协程(Coroutine) |
|---|---|---|
| 本质 | 操作系统层面的并行执行,独立于主线程 | 基于C#迭代器的分时执行,在主线程分支运行 |
| 访问Unity对象 | 不能直接访问Unity相关对象(如transform) | 可正常访问Unity对象 |
| 性能开销 | 开销大,创建和销毁线程有资源消耗 | 开销小,基于迭代器实现 |
| 执行顺序 | 并行执行,执行顺序不确定 | 分时执行,可控制执行时机 |
| 适用场景 | 复杂逻辑计算(如A星寻路)、网络消息接收 | 异步加载(文件、场景)、批量创建对象 |
| 生命周期管理 | 需手动关闭,否则会持续运行 | 组件或物体销毁、物体失活时停止(组件失活除外) |
特殊文件夹
| 路径/文件夹 | 获取方式 | 创建要求 | 读写权限 | 打包后状态 | 典型用途 | 注意事项 |
|---|---|---|---|---|---|---|
| Application.dataPath | print(Application.dataPath) |
自动生成(无需创建) | 编辑模式:可读可写 发布后:只读 |
包含所有资源(只读) | 编辑模式调试路径(加载外部配置) | 发布后不可写,避免运行时修改 |
| Resources | 无(通过Resources.Load访问) | 手动创建(Assets下) | 编辑/发布:只读 | 压缩加密(仅Resources.Load可用) | 动态加载资源(UI预制体、音效) | 打包后无法直接访问路径,避免存放过大资源 |
| StreamingAssets | print(Application.streamingAssetsPath) |
手动创建(Assets下) | 编辑/PC:可读可写 移动:只读 |
不压缩(保留原始格式) | 存放需自定义加载的初始资源(AB包、配置文件) | 移动平台仅可读,PC可读写 |
| persistentDataPath | print(Application.persistentDataPath) |
自动生成(无需创建) | 全平台:可读可写 | 不打包(运行时生成) | 存储动态数据(用户存档、下载资源) | 路径唯一,适合热更新和持久化存储(推荐使用) |
| Plugins | 无(自动识别) | 手动创建(Assets下) | 编辑/发布:只读 | 包含插件(按需编译) | 存放平台专属插件(iOS/Android SDK) | 不同平台文件需分目录(如Plugins/iOS/Android) |
| Editor | 无(仅编辑器可见) | 手动创建(Assets下) | 仅编辑器:可读可写 | 不打包(编辑器脚本专用) | 存放编辑器扩展脚本(自定义面板) | 内容不会打入游戏包,编辑器模式下生效 |
| Standard Assets | 无(自动识别) | 手动创建(Assets下) | 编辑/发布:只读 | 包含资源(正常打包) | 存放Unity官方标准资源或项目基础资源 | 避免滥用,按需提取资源;处理好编译顺序冲突 |
Resources
同步加载
| 方法 | 说明 | 参数/返回值 | 示例代码 | 适用场景 | 注意事项 |
|---|---|---|---|---|---|
| Resources.Load(path) | 同步加载资源(最常用) | path: 资源路径(无扩展名) 返回: Object |
GameObject cube = Resources.Load<GameObject>("Prefabs/Cube"); |
小资源(UI、音效) | 阻塞主线程,大资源慎用 |
| LoadAll(path) | 加载文件夹下所有资源(按类型过滤) | path: 文件夹路径 返回: Object[] |
Object[] textures = Resources.LoadAll("Textures"); |
批量加载同目录资源 | 同名资源按类型区分,需强制转换 |
| Load |
泛型加载(自动类型转换) | T: 目标类型 返回: T |
AudioClip bgm = Resources.Load<AudioClip>("Music/BGM"); |
明确类型的资源(推荐) | 无需手动as,代码更简洁 |
| GameObject 加载 | 需配合Instantiate实例化 | - | Instantiate(Resources.Load<GameObject>("Player")); |
预制体动态生成 | 直接加载GameObject不实例化会占用内存,但无法卸载 |
异步加载
| 方法 | 说明 | 执行机制 | 示例代码 | 适用场景 | 核心优势 |
|---|---|---|---|---|---|
| LoadAsync |
异步加载(返回ResourceRequest) | 新开线程加载,主线程检测完成 | ResourceRequest req = Resources.LoadAsync<Texture>("BigTex"); |
大资源(模型、高清纹理) | 避免主线程卡顿 |
| 回调监听 | req.completed += OnLoadEnd; | 加载完成触发回调 | void OnLoadEnd(AsyncOperation op) { tex = op.asset as Texture; } |
简单加载后逻辑 | 代码简洁,不支持多资源并行加载 |
| 协程加载 | yield return req; 或 while (!req.isDone) |
协程分时检测加载状态 | IEnumerator LoadCoroutine() { yield return req; 渲染纹理(); } |
复杂逻辑(进度条、多资源) | 支持并行加载,灵活控制加载流程 |
| 进度跟踪 | req.progress(0~1,非精确) | 实时获取加载进度 | progressBar.value = req.progress; |
显示加载进度条 | 移动端可能因压缩导致进度跳跃 |
资源卸载
| 方法 | 说明 | 适用资源 | 示例代码 | 最佳实践 | 禁忌 |
|---|---|---|---|---|---|
| UnloadAsset(asset) | 卸载指定资源(非GameObject) | 纹理、音效、文本等非实例化资源 | Resources.UnloadAsset(tex); |
释放不再使用的单次加载资源 | 禁止卸载GameObject预制体(会报错) |
| UnloadUnusedAssets() | 卸载未使用的缓存资源(配合GC.Collect) | 所有未引用的Resources资源 | Resources.UnloadUnusedAssets(); GC.Collect(); |
场景切换时清理内存 | 需在主线程调用,避免频繁调用 |
| GameObject 卸载 | 实例化对象需Destroy()释放 | 实例化的预制体 | Destroy(instance.gameObject); |
动态生成的对象(子弹、UI) | 仅销毁实例,预制体缓存仍存在 |
SceneManager
场景同步加载
实现方式:使用 SceneManager.LoadScene 进行场景同步加载,需引用 UnityEngine.SceneManagement 命名空间。
using UnityEngine.SceneManagement;
// 场景同步切换
SceneManager.LoadScene("Lesson21_场景异步加载Test");
缺点:切换场景时会删除当前场景所有对象,若场景对象过多会导致卡顿。
场景异步加载
事件回调异步加载场景
原理:利用 SceneManager.LoadSceneAsync 异步加载场景,通过 AsyncOperation 的 completed 事件回调处理加载完成逻辑。
// 封装的场景异步加载方法
private void LoadSceneAsyncWithCallback(string sceneName)
{
// 异步加载场景
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneName);
// 加载完成回调
asyncOperation.completed += (aO) =>
{
print("加载结束");
};
asyncOperation.completed += AsyncLoadOver;
}
// 异步加载场景回调
private void AsyncLoadOver(AsyncOperation ao)
{
print("AsyncLoadOver");
}
注意事项:即使对象销毁,异步加载回调仍会执行,因回调存储在事件中。
协程异步加载场景
原理:通过协程调用 SceneManager.LoadSceneAsync,加载过程中可执行其他逻辑(如更新进度条)。
// 异步加载场景协程函数
IEnumerator CoroutineAsyncLoadScene(string name)
{
// 异步加载场景
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(name);
print("异步加载过程中打印的信息");
// 利用场景异步加载的进度更新进度条(不太准确)
while (!asyncOperation.isDone)
{
print(asyncOperation.progress);
yield return null;
}
// 等待异步加载结束
yield return asyncOperation;
print("异步加载结束后打印的信息");
}
常用方法和属性
| 分类 | 属性/方法 | 描述 |
|---|---|---|
| 属性 | activeScene | 获取或设置当前活动场景(运行场景,操作基于此场景) |
| 属性 | sceneCount | 获取当前加载的场景数量(管理多个同时加载的场景) |
| 属性 | sceneCountInBuildSettings | 获取Build Settings中场景总数(了解项目场景数量) |
| 方法 | GetSceneAt(int index) | 根据索引获取已加载的场景(索引0到sceneCount - 1) |
| 方法 | GetSceneByName(string name) | 根据名称获取已加载的场景(未找到返回空Scene对象) |
| 方法 | GetSceneByPath(string path) | 根据路径获取已加载的场景(路径相对于Assets文件夹) |
| 方法 | UnloadSceneAsync(int sceneBuildIndex) | 异步卸载指定索引的场景(释放内存资源) |
| 方法 | UnloadSceneAsync(string sceneName) | 异步卸载指定名称的场景(释放内存资源) |
LineRenderer
LineRenderer组件参数
| 分类 | 参数 | 说明 | 常用值/操作 |
|---|---|---|---|
| 场景点编辑 | Simplify Preview | 开启后线段预览更明显(简化密集点显示) | 勾选(调试用)/ 取消(正式) |
| Tolerance | 点简化宽容度(值越大,线段越粗略) | 1(默认,密集点时调大) | |
| 编辑点操作 | Subdivide Selected | 框选相邻点后插入新点(细化拐角) | 鼠标框选+点击(需启用编辑模式) |
| 添加点模式 | Input Mode | 点创建方式: - Mouse Position:摄像机位置创建 - Physics Raycast:射线碰撞点创建 |
Physics Raycast(勾选) |
| LayerMask | 射线检测层(仅响应指定层的点击) | 勾选目标层(如“Ground”“UI”) | |
| Min VertexDistance | 拖动创建点的最小间隔(避免过密) | 0.1~0.5米(根据手感调整) | |
| 通用属性 | Loop | 首尾点连接(闭合图形/开放线段) | true(攻击范围)/ false(轨迹线) |
| PositionCount | 线段总点数(需先设置,再赋值) | 2(直线)~ 100(复杂曲线) | |
| Use World Space | 坐标系模式: - true:世界坐标 - false:本地坐标 |
false(角色激光线) | |
| 渲染属性 | Start/End Width | 线段首尾宽度(支持曲线插值) | 0.01f(纤细)~ 0.5f(粗壮) |
| Start/End Color | 线段渐变颜色(需材质支持) | Color.red → Color.blue(渐变) | |
| Corner/End Vertices | 拐角/末端圆角精度(值越大越圆滑) | 2(默认)~ 5(高精度圆角) | |
| Material | 线段材质(支持纹理/着色器,如火焰、激光) | 自定义材质(需关联主纹理) | |
| 光照相关 | Generate Lighting Data | 启用光照计算(材质需光源) | true(有光照场景)/ false(无光照) |
| Cast/Receive Shadows | 投射/接收阴影(增加真实感) | 按需勾选(默认关闭以优化性能) | |
| 高级设置 | Alignment | 线段对齐方式: - View:始终朝向相机 - Transform Z:沿物体Z轴方向 |
View(UI指引线)/ Transform Z(模型轮廓) |
| Texture Mode | 纹理映射模式: - Stretch:单次拉伸 - Tile:平铺重复 |
Stretch(激光纹理)/ Tile(铁链纹理) | |
| 性能优化 | Dynamic Occludee | 动态遮挡剔除(减少不可见线段渲染) | 勾选(复杂场景) |
| 排序控制 | Sorting Layer | 渲染层级(避免被UI遮挡) | 自定义层级(如“Foreground”) |
LineRenderer参数和方法
| 功能 | API | 说明 | 注意事项 |
|---|---|---|---|
| 动态创建线段 | AddComponent |
给GameObject添加LineRenderer组件 | 需先创建空物体 |
| 设置闭合状态 | lineRenderer.loop = true; | 闭合线段(首尾相连) | 需配合PositionCount ≥ 2 |
| 设定宽度 | lineRenderer.startWidth = 0.02f;lineRenderer.endWidth = 0.02f; |
设置线首尾宽度,支持动态修改 | 实时刷新需在Update中调用 |
| 设定颜色 | lineRenderer.startColor = Color.white;lineRenderer.endColor = Color.red; |
设置线首尾颜色,支持动态修改 | 实时刷新需在Update中调用 |
| 设置材质 | material = Resources.Load<Material>("火焰材质");lineRenderer.material = material; |
赋值自定义材质,支持纹理动画 | 材质需包含主纹理(Main Tex) |
| 设置点个数 | lineRenderer.positionCount = 4; | 设置或获取顶点数 | 设置点时,要先设置点的个数 |
| 批量设置点 | lineRenderer.SetPositions(new Vector3[] { new Vector3(0,0,0), new Vector3(0,0,5), new Vector3(5,0,5)}); | 一次性设置所有点 | 需先定义PositionCount,数组长度需 ≤ PositionCount |
| 单点修改 | lineRenderer.SetPosition(3, new Vector3(5, 0, 0)); | 单独修改某点坐标 | index从0开始,坐标基于useWorldSpace的设置 |
| 坐标系切换 | lineRenderer.useWorldSpace = false; | 切换为本地坐标系(线段随物体移动) | 动态切换需谨慎,可能导致坐标混乱 |
| 设置光照影响 | lineRenderer.generateLightingData = true; | 配置线以生成法线和切线,让场景光照影响线 | 若材质需要光照效果,需开启此选项 |
物理系统-范围检测
范围检测基础
| 分类 | 要点 | 关键细节 |
|---|---|---|
| 定义 | 瞬时检测指定范围内的对象(无实体碰撞器,仅计算) | 用于攻击范围、触发判定等瞬时逻辑 |
| 必备条件 | - 对象需含碰撞器(Collider) - 检测代码执行时触发 |
无刚体也可检测(区别于碰撞事件) |
| 层级过滤 | LayerMask 位运算(左移+或运算) | 1 << LayerMask.NameToLayer("Enemy") 表示仅检测Enemy层 |
| 触发器处理 | QueryTriggerInteraction 枚举: - UseGlobal(全局设置) - Collide(检测触发器) - Ignore(忽略触发器) |
全局设置路径:Edit > Project Settings > Physics > Queries Hit Triggers |
核心方法
| API | 形状 | 参数 | 返回值 | 典型场景 | 优化建议 |
|---|---|---|---|---|---|
| OverlapBox | 盒状 | 中心坐标, 半尺寸, 旋转, 层掩码, 触发器模式 | Collider[](动态数组) | 矩形攻击范围(地刺、AOE) | 避免高频调用(每次生成新数组) |
| OverlapBoxNonAlloc | 盒状 | 中心坐标, 半尺寸, 预分配数组, 层掩码, 触发器模式 | int(碰撞数) | 高频检测(如每帧扫描) | 预分配数组(colliders = new Collider[10])减少GCAlloc |
| OverlapSphere | 球形 | 中心坐标, 半径, 层掩码, 触发器模式 | Collider[](动态数组) | 圆形范围(角色拾取、技能半径) | 配合LayerMask过滤非目标层 |
| OverlapSphereNonAlloc | 球形 | 中心坐标, 半径, 预分配数组, 层掩码, 触发器模式 | int(碰撞数) | 性能敏感场景(子弹爆炸检测) | 提前计算半径(避免重复计算) |
| OverlapCapsule | 胶囊状 | 端点1, 端点2, 半径, 层掩码, 触发器模式 | Collider[](动态数组) | 柱状范围(角色近战攻击、NPC视野) | 用Vector3.ProjectOnPlane计算地面投影 |
| OverlapCapsuleNonAlloc | 胶囊状 | 端点1, 端点2, 半径, 预分配数组, 层掩码, 触发器模式 | int(碰撞数) | 复杂地形检测(爬墙判定) | 缓存端点坐标(减少重复赋值) |
射线检测
| 分类 | 核心内容 | 关键细节 |
|---|---|---|
| 基础概念 | 瞬时发射射线,检测与碰撞器的交点(无实体,仅计算) | 适用:鼠标选择、射线攻击、视线阻挡判定 条件:目标需有碰撞器(无需刚体) |
| Ray 类 | - 构造:new Ray(origin, direction)- 摄像机射线: Camera.ScreenPointToRay(鼠标位置) |
origin:射线起点 direction:方向向量(非终点) 屏幕点转射线用于UI/场景交互 |
| 核心API | Physics.Raycast(单碰撞) 参数:(射线/起点, 方向, out 碰撞信息, 距离, 层掩码, 触发器模式) |
返回:bool 用途:检测首个碰撞目标(如射击命中) 层掩码:1 << 层号(如1 << 8) |
| Physics.RaycastAll(多碰撞) 参数:(射线/起点, 方向, 距离, 层掩码, 触发器模式) |
返回:RaycastHit[](无序数组) 用途:获取所有碰撞目标(如区域内敌人) |
|
| Physics.RaycastNonAlloc(预分配) 参数:(射线/起点, 方向, 预分配数组, 距离, 层掩码, 触发器模式) |
返回:int(碰撞数) 优化:避免GCAlloc,适合高频检测 |
|
| RaycastHit | - collider:碰撞器 - point:交点坐标 - normal:碰撞面法线 - distance:起点到交点距离 |
normal用于特效朝向(如弹孔法线对齐) point用于生成命中特效(如子弹痕迹) |
| 参数顺序 | 必记顺序: 1. 射线/起点 2. 方向(非必填,重载支持) 3. out 碰撞信息(单碰撞) 4. 距离(0=无限) 5. 层掩码 6. 触发器模式 |
易错:层掩码误填为第2参数(正确:第4/5位) 示例: Raycast(ray, out hit, 100, 1<<8) |
| 触发器处理 | QueryTriggerInteraction枚举: - Collide(检测触发器) - Ignore(忽略) - UseGlobal(全局设置) |
全局设置路径:Project Settings > Physics > Queries Hit Triggers(默认检测触发器) |
| 性能优化 | - 预分配数组:RaycastHit[] hits = new RaycastHit[10]- 缓存射线:避免重复创建(如Camera.main缓存) |
NonAlloc比RaycastAll少生成临时数组,适合高频场景(如技能检测) |
| 典型场景 | 1. 鼠标选物体:ScreenPointToRay + Raycast(层过滤UI) 2. 射线攻击:检测首个碰撞目标(distance限制射程) 3. 视线阻挡:Raycast返回distance与预设值比较 |
示例:FPS射击检测墙体遮挡:if (hit.distance < 敌人距离) 被遮挡 |
25.3 面试题精选
基础题
1. Mathf.Lerp 的两种用法有何区别?分别适合什么场景?
题目
Mathf.Lerp(a, b, t) 中参数 t 的使用方式不同会带来什么效果差异?「先快后慢」和「匀速接近」两种写法分别怎么实现?
深入解析
- 用法一(先快后慢):
current = Mathf.Lerp(current, target, Time.deltaTime * speed),每帧用当前位置作为起点,t 固定较小值,初始差距大时变化快,接近目标时变化慢,产生阻尼效果。 - 用法二(匀速接近):
t += Time.deltaTime; current = Mathf.Lerp(start, end, t),t 从 0 累加到 1,线性过渡,t≥1 时固定为目标值。 - 前者适合镜头跟随、物体平滑停止;后者适合倒计时、线性动画。
答题示例
第一种每帧用当前值当起点,t 固定,越近越慢,像阻尼;第二种 t 累加,匀速走完。
镜头跟随用第一种,倒计时用第二种。
参考文章
- 2.3D数学-Mathf数学计算公共类
2. 世界坐标系、本地坐标系、屏幕坐标系、视口坐标系各自的原点和轴方向是什么?
题目
请简述 Unity 中四大坐标系的原点位置、轴方向以及典型使用场景。
深入解析
- 世界坐标系:原点为场景中心(0,0,0),X→右、Y→上、Z→前,用于物体绝对位置存储、碰撞检测。
- 本地坐标系:原点为父对象位置,轴方向随父对象旋转/缩放变化,用于子对象相对父对象的位置调整。
- 屏幕坐标系:原点为屏幕左下角(0,0),X→右、Y→上、Z→摄像机距离,用于鼠标位置获取、UI 元素定位。
- 视口坐标系:原点为左下角(0,0),X/Y 范围均为 0~1,用于分屏显示、全屏适配。
答题示例
世界坐标全局唯一,本地坐标相对父对象;屏幕坐标以像素为单位,视口坐标归一化到 0~1。
鼠标位置用屏幕坐标,分屏适配用视口坐标。
参考文章
- 4.3D数学-坐标系
3. Vector3.magnitude、normalized、sqrMagnitude 三者的区别与性能考量?
题目
获取向量长度、方向、长度平方分别用哪个属性?为什么说 sqrMagnitude 性能更高?什么场景适合用 sqrMagnitude?
深入解析
- magnitude:返回向量模长(开平方后的实际长度),等价于
Mathf.Sqrt(x²+y²+z²)。 - normalized:返回单位向量(方向不变,长度为 1),零向量返回零向量。
- sqrMagnitude:返回模长平方,省去开平方运算,性能比 magnitude 高约 50%。
- 适合用于距离比较:判断「是否在半径 5 内」时,比较
sqrMagnitude < 25即可,无需计算真实距离。
答题示例
magnitude 是真实长度,normalized 是单位向量,sqrMagnitude 是长度平方。
只比较距离大小时用 sqrMagnitude 省开方,性能更好。
参考文章
- 5.3D数学-Vector3向量-向量模长和单位向量
4. Invoke 与 InvokeRepeating 的区别?对象失活或销毁时延迟函数如何表现?
题目
Invoke 和 InvokeRepeating 各自执行几次?对象被 SetActive(false) 或 Destroy 后,已注册的延迟函数会怎样?
深入解析
- Invoke:延时执行一次,参数为方法名(字符串)和延迟时间。
- InvokeRepeating:延时后重复执行,参数为方法名、首次延迟、重复间隔。
- 对象失活:延迟函数继续执行,不受影响;激活后 InvokeRepeating 会继续。
- 对象销毁:延迟函数立即终止,无法恢复。
- 脚本禁用:延迟函数继续执行(生命周期未结束)。
答题示例
Invoke 执行一次,InvokeRepeating 重复执行。
失活不影响延迟函数,销毁则立即终止;脚本禁用也不影响。
参考文章
- 14.MonoBehavior中的重要内容-延迟函数
进阶题
1. TransformPoint 与 TransformDirection 的核心区别是什么?
题目
将本地坐标转为世界坐标时,TransformPoint 和 TransformDirection 分别处理什么类型的数据?是否受缩放影响?
深入解析
- TransformPoint:处理本地空间中的点(位置),受父对象的位置、旋转、缩放共同影响。用于「在角色前方 2 米生成物体」等场景。
- TransformDirection:处理方向向量,只受旋转影响,不受缩放影响。用于「将角色本地前进方向转为世界方向」。
- 还有 TransformVector:受旋转和缩放影响,不受位置影响。
- 对应的逆变换:
InverseTransformPoint、InverseTransformDirection、InverseTransformVector。
答题示例
Point 处理位置,受缩放影响;Direction 处理方向,只受旋转影响。
在角色前方生成物体用 TransformPoint,获取角色朝向用 TransformDirection。
参考文章
- 4.3D数学-坐标系
2. Vector3.Angle 返回的角度范围是多少?如何判断目标在左侧还是右侧?
题目
Vector3.Angle(from, to) 返回的角度范围是多少?为什么它无法区分左右?如何结合叉乘判断目标在左侧还是右侧?
深入解析
- Angle 返回 0°~180° 的无符号角度,基于点乘计算
cosθ = (a·b)/(|a||b|),无法区分方向。 - 叉乘判断左右:
Vector3 cross = Vector3.Cross(forward, toTarget),在 Unity 左手坐标系中:- cross.y > 0 → 目标在右侧
- cross.y < 0 → 目标在左侧
- 叉乘结果垂直于两向量所在平面,方向由左手法则决定。
答题示例
Angle 只返回 0~180 度,不分左右。
用 Cross 叉乘,结果 y 分量正负可判断左右:正为右,负为左。
参考文章
- 7.3D数学-Vector3向量-向量点乘
- 8.3D数学-Vector3向量-向量叉乘
3. Quaternion.Euler 与欧拉角直接赋值 transform.eulerAngles 有何风险?
题目
为什么 Unity 推荐用四元数处理旋转而不是直接操作欧拉角?什么是「万向节死锁」?Quaternion.Euler 如何避免这个问题?
深入解析
- 欧拉角:直观但存在万向节死锁问题——当绕某轴旋转 90° 时,另外两个轴可能重合,导致丢失一个自由度。
- 四元数:无万向节死锁,插值平滑,是 Unity 内部存储旋转的方式。
Quaternion.Euler(x, y, z)将欧拉角转为四元数,避免直接修改eulerAngles带来的死锁风险。- 读取
eulerAngles时可能跳跃(如 179°→-179°),因为内部是四元数转换结果。
答题示例
欧拉角直观但有万向节死锁风险;四元数无此问题且插值平滑。
用 Quaternion.Euler 转换后再赋值 rotation,避免直接改 eulerAngles。
参考文章
- 10.3D数学-Quaternion四元数-为何使用四元数
- 11.3D数学-Quaternion四元数-四元数是什么
4. StartCoroutine 与 Invoke 的核心区别是什么?各自适合什么场景?
题目
协程 StartCoroutine 与延迟函数 Invoke 在参数传递、执行控制、生命周期依赖上有何不同?各举一个典型使用场景。
深入解析
- 参数支持:Invoke 不支持传参(需借助代理或全局变量);协程支持传参(通过 IEnumerator 方法参数)。
- 执行控制:Invoke 简单但只能延时/重复;协程可中途 yield 暂停、条件等待、分帧执行。
- 生命周期依赖:Invoke 独立于对象激活状态(失活仍执行);协程依赖 MonoBehaviour 生命周期(对象失活时协程停止)。
- 性能:Invoke 底层优化较好;协程有迭代器开销。
- 场景:Invoke 适合简单定时(冷却、提示);协程适合复杂流程(异步加载、动画插值、分帧创建大量物体)。
答题示例
Invoke 简单但不能传参,失活仍执行;协程灵活可传参可暂停,但依赖对象激活。
简单冷却用 Invoke,异步加载或分帧处理用协程。
参考文章
- 14.MonoBehavior中的重要内容-延迟函数
- 15.MonoBehavior中的重要内容-协同程序
5. Resources.Load 与 AssetBundle 加载资源的主要区别?
题目
Resources.Load 加载资源有什么限制?为什么说生产环境更推荐使用 AssetBundle 或 Addressables?
深入解析
- Resources 文件夹:打包时所有资源会被打入主包,无法按需加载,增加包体大小和内存占用。
- Resources.Load:同步加载会阻塞主线程,异步加载
LoadAsync可缓解但仍需等待。 - AssetBundle:资源独立打包,可按需下载、卸载、热更新,适合大型项目。
- Addressables:Unity 官方推荐的资源管理方案,封装了 AssetBundle,提供更友好的 API。
- Resources 适合小型项目或配置文件;生产环境推荐 AssetBundle/Addressables。
答题示例
Resources 打包时全进主包,无法按需加载,增加包体。
AssetBundle 可独立打包、按需下载、热更新,适合大型项目。
参考文章
- 17.Resources资源动态加载-Unity中特殊文件夹
- 18.Resources资源动态加载-Resources同步加载
6. Physics.OverlapBox 与 Physics.CheckSphere 的区别?
题目
范围检测中 Physics.OverlapBox 和 Physics.CheckSphere 各自返回什么?分别适合什么场景?
深入解析
- OverlapBox:返回碰撞器数组
Collider[],获取盒形范围内所有碰撞到的物体,适合获取范围内敌人列表、拾取物列表。 - CheckSphere:返回 bool,只判断球形范围内是否有碰撞体,不返回具体对象,适合判断「前方是否有障碍物」。
- 类似的还有
OverlapSphere(返回数组)、OverlapCapsule等。 - 性能:Check 系列只判断有无,比 Overlap 系列更快。
答题示例
OverlapBox 返回碰撞器数组,用于获取范围内物体列表;CheckSphere 返回布尔,只判断有无碰撞。
要列表用 Overlap,只判断有无用 Check。
参考文章
- 23.核心系统-物理系统-范围检测
深度题
1. Quaternion.Slerp 与 Quaternion.Lerp 的区别?为何旋转插值推荐 Slerp?
题目
四元数插值中,Slerp(球形线性插值)与 Lerp(线性插值)在效果和性能上有何差异?为什么旋转插值推荐使用 Slerp?
深入解析
- Lerp:线性插值,在四元数空间中直线路径,可能产生角速度不均匀甚至「翻折」现象,适合非方向敏感场景(如 UI 旋转)。
- Slerp:球形插值,在四元数空间中沿大圆弧路径,保持恒定角速度,旋转更平滑自然。
- 性能:Lerp 比 Slerp 快约 20%,但旋转效果不如 Slerp 稳定。
- 选择:角色转向、镜头跟随等方向敏感场景用 Slerp;UI 简单旋转可用 Lerp。
- 四元数插值 t 值通常用
Time.deltaTime * speed,实现平滑过渡。
答题示例
Lerp 走直线可能翻折,Slerp 走球面弧线更平滑。
角色转向用 Slerp,UI 简单旋转可用 Lerp 省性能。
参考文章
- 12.3D数学-Quaternion四元数-四元数常用方法
2. 协程的底层原理是什么?yield return null 与 yield return new WaitForSeconds 有何本质区别?
题目
Unity 协程基于 C# 的什么机制实现?yield return null 和 yield return WaitForSeconds 在底层处理上有何不同?
深入解析
- 底层原理:协程基于 C# 迭代器(IEnumerator) 和 yield 语句实现。Unity 在每帧检测协程的 MoveNext,根据 yield 返回值决定何时继续执行。
- yield return null:返回 null,Unity 在下一帧继续执行该协程。
- yield return new WaitForSeconds(t):返回 WaitForSeconds 对象,Unity 会等待指定秒数后再继续执行,底层比较
Time.time与记录的结束时间。 - yield return new WaitForEndOfFrame:等待渲染完成后继续。
- yield return new WaitForFixedUpdate:等待下一个 FixedUpdate 后继续。
- 性能优化:避免在循环中
new WaitForSeconds(),应缓存复用。
答题示例
协程基于迭代器,Unity 每帧调 MoveNext 决定是否继续。
yield null 下一帧继续;WaitForSeconds 等指定时间后继续,底层比较时间戳。
参考文章
- 15.MonoBehavior中的重要内容-协同程序
- 16.MonoBehavior中的重要内容-协同程序原理
3. 场景异步加载时,allowSceneActivation 为 false 时加载进度停在 0.9 的原因?
题目
使用 SceneManager.LoadSceneAsync 异步加载场景时,将 allowSceneActivation 设为 false 后,为什么进度会停在 0.9?如何正确使用这个特性?
深入解析
- allowSceneActivation = false:场景加载完成后不会自动激活,进度停在 0.9 表示「加载完成但未激活」。
- 设计意图:允许在加载完成后做一些准备工作(如显示提示、初始化数据),再手动设置
allowSceneActivation = true激活场景。 - 正确用法:
- 开始异步加载,设置
allowSceneActivation = false - 等待
progress >= 0.9f(表示加载完成) - 执行准备工作(显示提示、初始化等)
- 设置
allowSceneActivation = true激活场景
- 开始异步加载,设置
- 注意:进度 0.9 是 Unity 的约定值,实际判断应
progress >= 0.9f。
答题示例
allowSceneActivation 为 false 时,加载完成停在 0.9,表示「已加载未激活」。
可在此时做准备工作,再设为 true 激活场景,实现加载完成后的可控切换。
参考文章
- 21.场景异步加载
4. Physics.Raycast 中 LayerMask 的位运算原理是什么?
题目
Physics.Raycast 的 LayerMask 参数如何表示「只检测某些层」?1 << LayerMask.NameToLayer("Enemy") 这个写法是什么意思?
深入解析
- LayerMask 本质:32 位整数,每一位对应一个层(0~31),1 表示检测该层,0 表示忽略。
- 位运算原理:
1 << n:将 1 左移 n 位,得到第 n 层的掩码。例如1 << 8表示第 8 层。LayerMask.NameToLayer("Enemy"):返回层名对应的层索引(0~31)。1 << LayerMask.NameToLayer("Enemy"):得到 Enemy 层的掩码。
- 组合多个层:用按位或
|,如(1 << 8) | (1 << 9)表示检测第 8 和第 9 层。 - 排除层:用按位取反
~,如~(1 << 2)表示忽略第 2 层(Ignore Raycast 常用)。 - 性能:指定 LayerMask 可减少不必要的碰撞检测,提高性能。
答题示例
LayerMask 是 32 位整数,每位代表一层。
1 << n得到第 n 层的掩码。组合多层用
|,排除层用~。指定 LayerMask 可减少无效检测。
参考文章
- 24.核心系统-物理系统-射线检测
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com