Appearance
物理系统
Collider 和 Rigidbody 分别是什么?
一句话:Collider 负责“碰撞形状和检测”,Rigidbody 负责“物理运动和受力”。
| 组件 | 作用 | 类比 |
|---|---|---|
Collider | 定义物体的物理外壳 | “身体轮廓” |
Rigidbody | 让物体进入物理模拟 | “质量、速度、重力、受力系统” |
Collider 是什么
Collider 告诉物理引擎:这个物体占据什么空间,能不能被射线打到,能不能和别的物体发生碰撞。
常见类型:
c
BoxCollider
SphereCollider
CapsuleCollider
MeshCollider如果勾选 Is Trigger,它就不再阻挡物体,只负责触发事件:
c
void OnTriggerEnter(Collider other)
{
Debug.Log("进入触发区");
}Rigidbody 是什么
Rigidbody 让物体变成物理世界里的刚体,拥有:
c
mass 质量
velocity 速度
drag 阻力
useGravity 是否受重力
isKinematic 是否由代码控制例如:
c
rb.AddForce(Vector3.forward * 10f, ForceMode.Impulse);组合关系
只有 Collider:
c
通常是静态物体:地面、墙、障碍物
能挡住别人,但自己不受力运动Collider + Rigidbody:
c
动态物体:箱子、球、掉落物、物理 projectile
会受重力、力、碰撞影响只有 Rigidbody 没有 Collider:
可以有速度和重力,但没有碰撞外壳,基本碰不到东西面试加分说法
NOTE
Collider 定义物体参与物理检测的形状,Rigidbody 定义物体是否参与动力学模拟。静态场景物体通常只需要 Collider;需要被重力、力、碰撞推动的物体,需要 Collider 加 Rigidbody。Trigger 本质也是 Collider,只是不产生物理阻挡,只产生进入、停留、离开的事件。
Trigger 和 Collision 区别是什么?
零基础理解
Trigger 像感应门。
你走进自动门的感应范围,门知道你来了,但是这个感应范围不会把你撞回去。
Collision 像墙。
你撞到墙,真的过不去,物理系统会处理阻挡、反弹、摩擦、接触点。
Unity 里怎么区分?
看 Collider 上的这个选项:
c
Is Trigger如果勾上:
c
这个 Collider 就是 Trigger
只触发 OnTriggerEnter / Stay / Exit
不产生物理阻挡如果不勾:
c
这个 Collider 是普通碰撞体
发生真实接触时触发 OnCollisionEnter / Stay / Exit
会产生阻挡举例
金币拾取:
玩家走进金币范围
金币消失
玩家不会被金币挡住所以金币用 Trigger。
墙壁:
玩家走到墙前
玩家不能穿过去所以墙用 Collision。
Trigger 代码
c
using UnityEngine;
public class Coin : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
Debug.Log("玩家吃到金币");
Destroy(gameObject);
}
}
}金币设置:
c
Coin GameObject
- SphereCollider
- Is Trigger 勾选玩家通常需要:
c
Player GameObject
- Collider
- Rigidbody 或 CharacterControllerCollision 代码
c
using UnityEngine;
public class HitWall : MonoBehaviour
{
private void OnCollisionEnter(Collision collision)
{
Debug.Log("我撞到了:" + collision.gameObject.name);
ContactPoint contact = collision.contacts[0];
Debug.Log("撞击点:" + contact.point);
Debug.Log("撞击面的法线:" + contact.normal);
}
}面试高分说法
TIP
Trigger 是触发检测,不产生物理阻挡,适合拾取、范围检测、技能区域。Collision 是真实碰撞,会进入物理求解,产生接触点、法线、阻挡和碰撞响应,适合墙、地面、箱子、球等真实物理交互。
OnTriggerEnter 和 OnCollisionEnter 的触发条件是什么?
OnTriggerEnter 条件
要触发 OnTriggerEnter,通常需要:
1. 两个物体都有 Collider
2. 至少一个 Collider 勾选 Is Trigger
3. 至少一方有 Rigidbody
4. 两个物体的 Layer 在碰撞矩阵中允许交互代码:
c
private void OnTriggerEnter(Collider other)
{
Debug.Log("进入触发器的是:" + other.name);
}参数 Collider other 是:
进入我触发范围的那个 ColliderOnCollisionEnter 条件
要触发 OnCollisionEnter,通常需要:
1. 两个物体都有 Collider
2. 双方 Collider 都不是 Trigger
3. 至少一方有 Rigidbody
4. 两者真实接触
5. Layer 碰撞矩阵允许碰撞代码:
c
private void OnCollisionEnter(Collision collision)
{
Debug.Log("撞到的是:" + collision.gameObject.name);
}参数 Collision collision 里面信息更多:
c
collision.gameObject; // 撞到的对象
collision.collider; // 撞到的 Collider
collision.contacts; // 接触点数组
collision.relativeVelocity; // 相对速度为什么至少一方要有 Rigidbody?
零基础可以这样理解:
Collider 只是外壳。
Rigidbody 才让物体进入物理事件系统。两个只有 Collider 的静态物体放在一起,Unity 一般把它们当成场景结构,不会当成“正在发生动态碰撞的两个物体”。
常见错误
错误 1:金币有 Trigger,但玩家没有 Rigidbody。
结果:OnTriggerEnter 可能不触发错误 2:想走 OnCollisionEnter,结果 Collider 勾了 Is Trigger。
结果:不会走 OnCollisionEnter,会走 OnTriggerEnter错误 3:Layer 碰撞矩阵关闭了。
结果:两个物体看起来碰到了,但事件不来面试高分说法
IMPORTANT
OnTriggerEnter 需要双方有 Collider,至少一个是 Trigger,并且通常至少一方有关联 Rigidbody。OnCollisionEnter 需要双方是非 Trigger Collider,并发生真实物理接触,也通常要求至少一方有 Rigidbody。Trigger 返回 Collider 信息,Collision 返回更完整的接触信息,比如接触点、法线和相对速度。
静态碰撞体、动态碰撞体、运动学刚体区别是什么?
先看三种物体
静态碰撞体:Collider,没有 Rigidbody
动态碰撞体:Collider + Rigidbody,isKinematic = false
运动学刚体:Collider + Rigidbody,isKinematic = true静态碰撞体 Static Collider
像:
地面
墙
建筑
大石头
不可移动障碍物组件:
BoxCollider / MeshCollider / CapsuleCollider
没有 Rigidbody特点:
不受重力
不受 AddForce
不应该频繁移动
适合作为场景里的固定碰撞环境例子:
地板有 BoxCollider
玩家站在地板上
地板不需要 Rigidbody动态碰撞体 Dynamic Rigidbody
像:
球
箱子
掉落物
可被炸飞的木桶组件:
Collider
Rigidbody
isKinematic = false特点:
会受重力影响
会被力推动
会和其他 Collider 发生真实碰撞
位置由物理引擎更新代码:
c
using UnityEngine;
public class PushBox : MonoBehaviour
{
public Rigidbody rb;
private void Start()
{
rb.AddForce(Vector3.forward * 10f, ForceMode.Impulse);
}
}运动学刚体 Kinematic Rigidbody
像:
移动平台
电梯
自动门
动画控制的机关
过场动画里的角色组件:
Collider
Rigidbody
isKinematic = true特点:
不被重力自动拉下去
不被 AddForce 推动
由代码或动画控制移动
可以影响动态刚体移动平台例子:
c
using UnityEngine;
public class MovingPlatform : MonoBehaviour
{
public Rigidbody rb;
public Vector3 start;
public Vector3 end;
public float speed = 2f;
private void FixedUpdate()
{
float t = (Mathf.Sin(Time.time * speed) + 1f) * 0.5f;
Vector3 target = Vector3.Lerp(start, end, t);
rb.MovePosition(target);
}
}平台设置:
Rigidbody
- Is Kinematic 勾选
- Use Gravity 关闭
Collider
- 不勾 Is Trigger常见坑
不要频繁移动静态 Collider:
// 不推荐:一个没有 Rigidbody 的墙,每帧改位置
transform.position += Vector3.right * Time.deltaTime;原因:
Unity 会把静态 Collider 当作场景物理结构。
你频繁移动它,会导致物理系统不断更新内部结构,性能不好。如果要移动,请加 Rigidbody,并设置成 Kinematic。
面试高分说法
TIP
静态碰撞体只有 Collider,适合不会动的场景物体;动态刚体是 Collider 加非 Kinematic Rigidbody,会受重力、力和碰撞影响;运动学刚体是 Rigidbody 设置 isKinematic,由代码或动画控制,不受物理力驱动,适合移动平台和动画机关。
Rigidbody.isKinematic 有什么作用?
零基础理解
isKinematic 可以理解成:
c
false:交给物理引擎管
true:我自己用代码或动画控制isKinematic = false
c
rb.isKinematic = false;意思是:
这个 Rigidbody 是动态刚体
会受重力影响
会受 AddForce 影响
会被碰撞推动例子:
c
using UnityEngine;
public class FallingBall : MonoBehaviour
{
public Rigidbody rb;
private void Start()
{
rb.isKinematic = false;
rb.useGravity = true;
}
}球会掉下来。
isKinematic = true
c
rb.isKinematic = true;意思是:
物理引擎不再自动控制它的运动
重力不会让它掉落
AddForce 不会把它推走
它的位置通常由代码、动画或 MovePosition 控制例子:
c
using UnityEngine;
public class KinematicDoor : MonoBehaviour
{
public Rigidbody rb;
public Vector3 openPosition;
public float speed = 3f;
private void Awake()
{
rb.isKinematic = true;
}
private void FixedUpdate()
{
rb.MovePosition(Vector3.MoveTowards(
rb.position,
openPosition,
speed * Time.fixedDeltaTime
));
}
}布娃娃例子
角色活着时:
Animator 控制动作
骨骼 Rigidbody 设置 isKinematic = true角色死亡时:
关闭 Animator
骨骼 Rigidbody 设置 isKinematic = false
让角色变成 ragdoll代码示意:
c
using UnityEngine;
public class RagdollSwitch : MonoBehaviour
{
public Animator animator;
public Rigidbody[] ragdollBodies;
private void Awake()
{
SetRagdoll(false);
}
public void Die()
{
animator.enabled = false;
SetRagdoll(true);
}
private void SetRagdoll(bool enabled)
{
foreach (Rigidbody rb in ragdollBodies)
{
rb.isKinematic = !enabled;
}
}
}面试高分说法
TIP
isKinematic 决定 Rigidbody 是否由物理动力学模拟。false 时由物理系统根据重力、力和碰撞更新;true 时不再受力驱动,而由脚本、动画或 MovePosition 控制。它常用于移动平台、动画机关、过场控制和 ragdoll 切换。
AddForce 和直接改 velocity 区别是什么?
零基础理解
AddForce:
我推你一下,至于你动多快,让物理系统算。velocity:
我直接告诉你:你现在速度就是这个。AddForce 例子
c
using UnityEngine;
public class ExplosionPush : MonoBehaviour
{
public Rigidbody rb;
public float force = 10f;
public void Push(Vector3 direction)
{
rb.AddForce(direction.normalized * force, ForceMode.Impulse);
}
}适合:
爆炸
推箱子
击飞
车辆加速
物理感明显的运动ForceMode 是什么?
c
Force:持续施加力,受质量影响
Acceleration:持续加速度,不受质量影响
Impulse:瞬间冲量,受质量影响
VelocityChange:瞬间速度变化,不受质量影响例子:
c
rb.AddForce(Vector3.forward * 10f, ForceMode.Force);
rb.AddForce(Vector3.forward * 10f, ForceMode.Impulse);
rb.AddForce(Vector3.forward * 10f, ForceMode.VelocityChange);简单记:
c
Force:慢慢推
Impulse:猛推一下
VelocityChange:直接让速度变一下直接改 velocity 例子
跳跃常用:
c
using UnityEngine;
public class RigidbodyJump : MonoBehaviour
{
public Rigidbody rb;
public float jumpSpeed = 6f;
public bool isGrounded;
private void Update()
{
if (isGrounded && Input.GetKeyDown(KeyCode.Space))
{
Vector3 v = rb.velocity;
v.y = jumpSpeed;
rb.velocity = v;
}
}
}为什么跳跃适合改 velocity?
因为跳跃通常希望起跳速度是明确的。
不想因为质量不同导致跳跃高度乱变。常见错误
错误:
c
rb.velocity = moveDir * speed * Time.deltaTime;原因:
velocity 本身就是“每秒移动多少米”
不能再乘 Time.deltaTime正确:
c
rb.velocity = moveDir * speed;如果只想改水平速度,保留 y 轴:
c
Vector3 v = rb.velocity;
v.x = moveDir.x * speed;
v.z = moveDir.z * speed;
rb.velocity = v;面试高分说法
NOTE
AddForce 是给刚体施加力,由物理系统根据质量、时间步和 ForceMode 计算速度变化;velocity 是直接指定当前速度,控制更强但会覆盖物理结果。需要真实受力感时用 AddForce,需要明确速度,比如跳跃、固定移动、击退初速度时可以直接改 velocity。
射线检测如何过滤 Layer?
零基础理解
Raycast 像发出一条激光。
但你可能不希望它打到所有东西。
比如:
开枪只想打 Enemy
点地板只想点 Ground
交互只想检测 NPC 和 Door这时候用 LayerMask。
Inspector 方式
c
using UnityEngine;
public class ClickRaycast : MonoBehaviour
{
public Camera mainCamera;
public LayerMask hitMask;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, 100f, hitMask))
{
Debug.Log("射线打到了:" + hit.collider.name);
}
}
}
}然后在 Inspector 里选择:
c
hitMask = Enemy / Ground / Interactable代码方式
c
int mask = LayerMask.GetMask("Enemy", "Ground");
if (Physics.Raycast(ray, out RaycastHit hit, 100f, mask))
{
Debug.Log(hit.collider.name);
}只检测 Enemy 层
c
int enemyLayer = LayerMask.NameToLayer("Enemy");
int mask = 1 << enemyLayer;为什么要 1 << layer?
因为 LayerMask 是位掩码。
c
Layer 编号:第几层
LayerMask:哪些层被选中排除 Player 层
c
int playerLayer = LayerMask.NameToLayer("Player");
int mask = ~(1 << playerLayer);
Physics.Raycast(ray, out RaycastHit hit, 100f, mask);是否检测 Trigger
c
Physics.Raycast(
ray,
out RaycastHit hit,
100f,
hitMask,
QueryTriggerInteraction.Ignore
);可选:
c
UseGlobal:使用全局设置
Ignore:忽略 Trigger
Collide:检测 Trigger面试高分说法
TIP
Raycast 可以通过 LayerMask 参数过滤检测层。项目里通常会把 Enemy、Ground、Interactable、Player 等分层,射线只查询需要的层,避免打到自己、特效或无关碰撞体。对于 Trigger,还可以用 QueryTriggerInteraction 控制是否参与查询。
碰撞矩阵有什么用?
零基础理解
碰撞矩阵像一个“班级座位规则表”。
它告诉 Unity:
Player 和 Enemy 要不要碰?
Player 和 Player 要不要碰?
Bullet 和 Player 要不要碰?
Bullet 和 Bullet 要不要碰?位置:
c
Project Settings → Physics → Layer Collision Matrix例子
你有这些 Layer:
c
Player
Enemy
PlayerBullet
EnemyBullet
Ground
TriggerZone你可以设置:
c
PlayerBullet 只碰 Enemy
EnemyBullet 只碰 Player
Player 不和 Player 碰撞
Enemy 不和 Enemy 碰撞
TriggerZone 只检测 Player为什么不用代码 if 判断?
你当然可以写:
c
if (other.CompareTag("Player"))
{
}但碰撞矩阵更早过滤。
意思是:
物理系统一开始就知道这两层不用碰
直接减少计算
也减少误触发运行时忽略 Layer 碰撞
c
int playerLayer = LayerMask.NameToLayer("Player");
int enemyLayer = LayerMask.NameToLayer("Enemy");
Physics.IgnoreLayerCollision(playerLayer, enemyLayer, true);恢复碰撞:
c
Physics.IgnoreLayerCollision(playerLayer, enemyLayer, false);忽略两个具体 Collider
比如玩家手里的武器不要打到自己:
c
Physics.IgnoreCollision(playerCollider, weaponCollider, true);面试高分说法
IMPORTANT
碰撞矩阵是 Unity 物理系统的全局 Layer 过滤规则,用来控制哪些层之间可以发生碰撞或触发。它比在回调里写 if 更底层,能减少不必要的物理检测,也能让子弹、角色、地形、技能区域之间的交互关系更清晰。
为什么快速移动物体会穿透?怎么解决?
零基础理解
Unity 物理不是无时无刻都检查。
它是一格一格检查的。
假设子弹很快:
第 1 次物理检查:子弹在墙左边
第 2 次物理检查:子弹已经在墙右边Unity 没看到它中间穿过墙的那一瞬间,于是就穿透了。
这叫:
Tunneling,隧穿容易穿透的情况
物体速度太快
墙太薄
Collider 太小
Fixed Timestep 太大
用 transform.position 硬改位置
普通 Discrete 碰撞检测不够解决方案 1:开启连续碰撞检测
c
rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;常见模式:
c
Discrete:默认,离散检测,性能好,但高速可能穿透
Continuous:适合高速物体撞静态物体
ContinuousDynamic:适合高速动态刚体
ContinuousSpeculative:预测式连续检测,运动学刚体也常用解决方案 2:子弹用 Raycast
很多游戏的子弹不是靠刚体飞过去撞,而是用射线判断有没有命中。
c
using UnityEngine;
public class BulletRaycast : MonoBehaviour
{
public Vector3 lastPosition;
public LayerMask hitMask;
public float damage = 10f;
private void Start()
{
lastPosition = transform.position;
}
private void Update()
{
Vector3 currentPosition = transform.position;
Vector3 movement = currentPosition - lastPosition;
float distance = movement.magnitude;
if (distance > 0f)
{
Ray ray = new Ray(lastPosition, movement.normalized);
if (Physics.Raycast(ray, out RaycastHit hit, distance, hitMask))
{
Debug.Log("子弹命中:" + hit.collider.name);
Destroy(gameObject);
}
}
lastPosition = currentPosition;
}
}解决方案 3:有体积的子弹用 SphereCast
c
Physics.SphereCast(
lastPosition,
0.2f,
movement.normalized,
out RaycastHit hit,
distance,
hitMask
);Raycast 是一条线。
SphereCast 像一个球沿路径扫过去,更适合有体积的物体。
解决方案 4:减少 Fixed Timestep
c
Project Settings → Time → Fixed Timestep比如从:
0.02 秒改成:
0.01 秒物理检查更频繁,但 CPU 更累。
注意:Interpolation 不是解决穿透
Interpolation 只是让画面看起来更平滑。
它解决视觉抖动
不等于解决物理穿透面试高分说法
NOTE
高速物体穿透的本质是离散物理步采样不足,物体在两个 FixedUpdate 之间跨过了碰撞体。解决方式包括开启连续碰撞检测、降低 Fixed Timestep、增厚 Collider,以及对子弹这类高速对象使用 Raycast 或 SphereCast 做路径检测。项目中子弹命中通常不会完全依赖刚体碰撞,而会用射线保证稳定性。
物理帧率如何设置?
零基础理解
Unity 有两种常见更新:
Update:画面每刷新一帧调用一次
FixedUpdate:物理系统按固定时间间隔调用如果游戏画面是 60 FPS:
Update 大约每秒 60 次如果 Fixed Timestep 是 0.02:
FixedUpdate 每秒约 50 次因为:
1 / 0.02 = 50在哪里设置?
c
Project Settings → Time → Fixed Timestep代码里:
c
Time.fixedDeltaTime = 0.02f;Rigidbody 相关代码放哪里?
物理操作一般放 FixedUpdate:
c
using UnityEngine;
public class PhysicsMove : MonoBehaviour
{
public Rigidbody rb;
public float force = 10f;
private void FixedUpdate()
{
rb.AddForce(Vector3.forward * force);
}
}输入可以放 Update,再在 FixedUpdate 用:
c
using UnityEngine;
public class RigidbodyInputMove : MonoBehaviour
{
public Rigidbody rb;
public float speed = 5f;
private Vector3 inputDir;
private void Update()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
inputDir = new Vector3(h, 0f, v).normalized;
}
private void FixedUpdate()
{
Vector3 velocity = inputDir * speed;
velocity.y = rb.velocity.y;
rb.velocity = velocity;
}
}Fixed Timestep 小一点会怎样?
比如:
0.02 → 0.01结果:
物理每秒 50 次 → 100 次
碰撞更细
高速物体更不容易漏检
CPU 开销更高Fixed Timestep 大一点会怎样?
比如:
0.02 → 0.04结果:
物理每秒 50 次 → 25 次
CPU 更轻松
物理更粗糙
碰撞更容易不稳定Maximum Allowed Timestep
还有一个相关设置:
Maximum Allowed Timestep它的作用是:
当游戏卡顿时,限制 Unity 最多补多少物理时间。
避免一帧里疯狂补很多次 FixedUpdate。面试高分说法
TIP
Unity 的物理系统按固定时间步更新,由 Fixed Timestep 决定频率,默认常见值是 0.02 秒,也就是约 50Hz。Rigidbody 的受力、速度和物理移动通常放在 FixedUpdate。调小 Fixed Timestep 可以提高物理精度,但会增加 CPU 成本;调大则省性能,但模拟更粗糙,快速物体更容易出现问题。
最后给你一版“面试官问,我怎么答”的完整口述
你可以这样说:
IMPORTANT
Unity 物理里,Collider 决定物体的碰撞形状,Rigidbody 决定物体是否参与动力学模拟。Trigger 是 Collider 的一种触发模式,只检测进入、停留、离开,不产生物理阻挡;Collision 是真实碰撞,会有接触点、法线和碰撞响应。触发这些事件通常要求双方有 Collider,并且至少一方有 Rigidbody。静态碰撞体适合地面和墙,动态刚体适合箱子和球,运动学刚体适合移动平台和动画机关。AddForce 是施加力,交给物理系统计算速度变化;velocity 是直接指定速度。射线检测可以用 LayerMask 过滤目标,碰撞矩阵可以从全局控制哪些 Layer 之间发生碰撞。高速物体穿透是因为离散物理步采样不足,可以用连续碰撞检测、Raycast/SphereCast、降低 Fixed Timestep 等方式解决。物理逻辑一般放在 FixedUpdate,因为 Unity 物理系统按固定时间步运行。
按顺序背就行:Collider → Rigidbody → Trigger/Collision → Kinematic → Force/Velocity → Layer → Matrix → CCD → FixedUpdate。