想象你在看一场木偶剧:《嫦娥奔月》里,玉兔从舞台高处掉落时稳稳落地、两尊木偶擦肩而过不会叠在一起、门板能被推开又弹回。台前是木偶在卖力表演,台后其实站着一位从不露面的“舞台监督”——他给每尊木偶称重、记录体型、揣着激光笔探路、用绳索连接道具,让一切运动都符合真实世界的规律。在 Unity 游戏开发里,这位舞台监督就叫 PhysX。它是 NVIDIA 开发、被 Unity 默认集成的物理引擎:刚体负责“承重受力”,碰撞体负责“丈量体型”,物理材质负责“手感”,射线检测负责“探路”,关节负责“连接”。这篇文章就带你翻开这位隐形监督的工作手册:每个组件为什么存在、底层怎么算碰撞、怎么配出真实的物理效果。读完你就能在射击、赛车、平台跳跃游戏里,让物体呈现出真实的重量与质感。

岗位关联

对 AI 从业者而言,物理引擎是虚拟世界“可信度”的基石——无论是游戏玩法开发,还是搭建强化学习所需的仿真训练场,掌握 PhysX 的刚体、碰撞与射线机制,都能让交互既真实又高效。

目录
  1. 可视化演示
  2. 物理引擎概述
  3. 刚体组件配置
  4. 碰撞体类型
  5. 物理材质
  6. 碰撞事件检测
  7. 射线检测
  8. 关节约束
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示PhysX物理引擎核心机制:宽相位AABB粗筛到窄相位精确碰撞、射线检测命中判定、关节约束连接刚体。切换模式查看碰撞检测、射线检测与约束系统。

本节配有交互动效(进入视口后自动加载,约 3KB)。

碰撞检测两阶段流程 宽相位:AABB包围盒快速粗筛候选对 窄相位:GJK/SAT精确检测碰撞 接触点求解器计算碰撞响应 Broad(AABB)→Narrow(GJK)→Solver 两阶段优化大幅减少计算量

物理引擎概述

木偶剧团彩排前,舞台监督会先宣布今晚的“物理铁律”:所有被抛起的木偶都会落回地面、两尊木偶不能同时占据同一块地板、拴着绳索的木偶运动必须互相牵制。监督从不亲自上台,却用一套看不见的规则让整场演出显得真实可信。Unity 里的 PhysX 就是这个舞台监督——它把牛顿力学封装成一套游戏开发者随手可调的组件与服务。

在 Unity 中,一尊“木偶”就是一个 GameObject(游戏对象)。给它挂上 Rigidbody(刚体)组件,相当于递给舞台监督一张身份牌:“这尊木偶受物理支配,可以推动、会被重力拉拽”;再配上 Collider(碰撞体),相当于补充“它的体型轮廓长这样”。

PhysX 底层采用“离散时间积分”推进模拟:它把连续时间切成约每秒 50 个固定小片段(Fixed Timestep),每个片段内先求出物体受到的合力,再算出加速度,进而更新速度与位置。Unity 中物理相关代码写在 FixedUpdate 里,正是为了跟这个节奏对齐。碰撞检测、刚体运动、关节约束、射线检测,都是这位监督在同一套“算账”框架下完成的例行工作。

回到剧团:舞台监督并不亲手托举任何木偶,它只负责在每一个固定步长“算账”——先合计外力,再推出下一帧的位置和朝向。木偶们的表演因此既真实又听话,而游戏开发者要做的,就是决定哪些木偶领“身份牌”、穿什么“轮廓外套”。

F⃗=m×a⃗\displaystyle \vec{F} = m \times \vec{a}
这是舞台监督每次“算账”的核心算式:力等于质量乘以加速度。给木偶施加的力越大、木偶越轻,加速度就越大。
  1. 合力 FF 带箭头表示矢量,既有大小又有方向。木偶受到的推力、重力、摩擦力会先合成一个总力,再做后续计算。
    F⃗\displaystyle \vec{F}
  2. 质量 mm 表示物体抵抗运动变化的“惯性”大小。质量越大,越难被推动,也越难被刹住。
    m\displaystyle m
  3. 加速度 aa 描述速度变化的快慢。同样的力作用在轻木偶上,加速度明显大于重木偶。
    a⃗=F⃗m\displaystyle \vec{a} = \frac{\vec{F}}{m}

刚体组件配置

舞台监督手里的身份牌不是空白的,上面印着几栏必填信息:“体重”(Mass)、“怕不怕风”(Drag)、“是否被重力管”(Use Gravity)、“能不能被外部钉死旋转”(Freeze Rotation)。这就是刚体组件的配置面板——每一栏都在告诉监督“这尊木偶在物理世界里该怎么被对待”。

刚体是 Unity 物体获得物理行为的核心组件。Mass 决定质量,直接影响碰撞时的冲击力;Drag 是线性阻力,模拟空气阻力让运动逐渐变慢;Angular Drag 是角阻力,让旋转慢慢停下;Interpolate(插值)则解决“物理步长和渲染帧率不一致”造成的抖动——监督在两次物理结算之间平滑过渡,观众就看不出木偶在“一顿一顿”地移动。

还有一个容易被忽略的开关 Is Kinematic(运动学刚体)。它相当于“被提线直接操控的木偶”:不受重力与碰撞推力影响,位置由动画或脚本直接指定,但它依然可以推动其他普通刚体——比如一扇被 NPC 用力推开的大门。理解它的本质是:监督不主动给力,只负责“把它当成一个推不动的大块头”去影响别人。

所以身份牌上的“体重”并不会让木偶掉得更快,它影响的是碰撞时把对方推飞多远、以及刹车时有多难停住。想让木偶掉落得轻飘飘,改的应该是阻力(Drag),而不是质量——这是舞台监督手册里最反直觉的一条规矩。

F⃗gravity=m×g⃗\displaystyle \vec{F}_{\text{gravity}} = m \times \vec{g}
刚体受的重力等于质量乘以重力加速度。关键在于:质量在这里同时出现在牛顿第二定律里,两者相消,所以同场景中不同质量的物体下落加速度其实一样。
  1. 重力 F地球对物体的引力,方向始终竖直向下,是舞台上最常见的“隐形提线”。
    F⃗gravity\displaystyle \vec{F}_{\text{gravity}}
  2. 质量 m身份牌上的体重值,它同时决定重力大小和惯性大小,两条规则恰好抵消。
    m\displaystyle m
  3. 重力加速度 g约等于 9.8 m/s²,Unity 中默认取 9.81,方向向下;Use Gravity 关闭后这项归零。
    g⃗=9.8 m/s2 (竖直向下)\displaystyle \vec{g} = 9.8\ \text{m/s}^2 \ (\text{竖直向下})

碰撞体类型

身份牌只说明“这尊木偶会不会被物理管”,但“碰到什么才算数”得由身体轮廓决定。木偶剧团的服装师会给每尊木偶做外套:瘦高个用胶囊形套子,方箱子用矩形纸盒,复杂道具则用全身 3D 扫描的定制外套——每一件外套都是一份 Collider(碰撞体)。

Unity 中最常用的是几类基础几何碰撞体:Box Collider(盒子)、Sphere Collider(球)、Capsule Collider(胶囊)。它们的共同优点是数学计算极其简单:判断两个球是否相撞只需算一次球心距离;判断胶囊可以用“线段到线段的最短距离”。基础几何体之间的碰撞最快最稳,适合绝大多数游戏物体。相比之下,Mesh Collider(网格碰撞体)与模型网格完全贴合,但要用三角形网格做逐面求交,开销大得多,一般只用于地形、静态建筑等不动的物体。

碰撞体的数学本质就是“几何求交”。两种形状是否接触,等价于它们之间是否存在重合区域。以两球为例:只要球心距离不超过两球半径之和,就判定接触。此时舞台监督不需要判断“谁的外套压到了谁”,只需一条距离不等式。

服装师的经验是:外套形状越简单,监督“扫一眼”就能判断是否相撞;定制外套虽然严丝合缝,但每次模拟都要做昂贵的高精度扫描。另外,把碰撞体上的 IsTrigger(触发器)勾上,外套就变成“光幕”——物体可以穿过去,只在穿过瞬间留下事件记录,用于拾取、感应区等场景。

d=∥p⃗1−p⃗2∥,碰撞条件: d≤r1+r2\displaystyle d = \| \vec{p}_1 - \vec{p}_2 \|, \quad \text{碰撞条件: } d \le r_1 + r_2
两个球体碰撞的判定条件:球心距 d 小于等于两球半径之和,就说明轮廓接触到了。
  1. 球心距 d两个球心之间的直线距离,是判断接触的基础指标。
    d=∥p⃗1−p⃗2∥\displaystyle d = \| \vec{p}_1 - \vec{p}_2 \|
  2. 半径之和两球相撞当且仅当球心距不超过两半径相加。
    r1+r2\displaystyle r_1 + r_2
  3. 判断条件满足不等式即判定“轮廓接触”,随后进入碰撞事件与响应阶段。
    d≤r1+r2\displaystyle d \le r_1 + r_2

物理材质

同样的地板,有的区域打了蜡,木偶走上去会滑出一段;有的区域铺了胶垫,木偶能稳稳站住。木偶的手套也有皮面和绒面之分。这些“接触面的手感”在 PhysX 里由 Physics Material(物理材质)负责——它是贴在碰撞体表面的一张“触感说明卡”。

物理材质主要有三个参数:Dynamic Friction(动摩擦系数)控制滑动时的阻力,Static Friction(静摩擦系数)控制“从静止到移动”需要多大的推力,Bounciness(弹性系数)控制碰撞后反弹的程度。摩擦让物体停下来,弹性让物体弹起来,两者共同决定木偶在舞台上的“脚感”。

摩擦力的计算遵循经典摩擦模型:摩擦力的大小不会无限增大,它被限制在摩擦系数乘以正压力以内。这里的正压力就是接触面受到的垂直挤压——木偶越重,压得越紧,能提供的摩擦力越大。当两个物体都有材质时,引擎会按规则合并(默认取两者中较小摩擦系数),所以想让箱子在冰面上滑得远,把地板材质摩擦调低即可。

给舞台地板贴“打蜡卡”、给木偶鞋底贴“防滑卡”,再配合弹性系数控制落地后的回弹,木偶的每一个踉跄与反弹才有了说服力。物理材质调得好的剧组,连“木偶滑倒”都像是在讲一个真实的故事。

ffriction≤μ×N\displaystyle f_{\text{friction}} \le \mu \times N
摩擦力上限等于摩擦系数乘以正压力。摩擦系数由物理材质决定,数值越大,表面越“涩”。
  1. 摩擦力 f阻止相对滑动的力,方向与滑动趋势相反,是让木偶停住的关键。
    ffriction\displaystyle f_{\text{friction}}
  2. 摩擦系数 μ由物理材质决定,动摩擦与静摩擦分别配置,μ 越大越难滑动。
    μ\displaystyle \mu
  3. 正压力 N接触面垂直方向的挤压力。木偶越重、压得越紧,可用的摩擦力越大。
    N\displaystyle N

碰撞事件检测

当两只木偶在台上相撞,舞台监督不会喊“咔”,而是在后台的登记簿上写下一行:“某时某刻,A 与 B 在外套轮廓处发生接触。”游戏里的脚本,就是那个翻登记簿的人——它通过碰撞回调函数得知“谁撞上了谁”,然后决定扣血、播放音效还是触发任务。

Unity 为碰撞提供了三个阶段的回调:OnCollisionEnter(开始接触)、OnCollisionStay(持续接触)、OnCollisionExit(离开接触)。要收到这些回调,需要满足两个条件:发生碰撞的两个物体至少一方带有刚体,且双方都有碰撞体。刚体负责“谁主动谁被动”的关系,碰撞体负责“哪里算接触”。这与触发器的 OnTriggerEnter/Stay/Exit 是两套系统——碰撞是“实打实挡路”,触发器只是“穿过后登记”。

大型场景中,碰撞体的数量动辄成百上千。如果不加控制,物理引擎最朴素的检测方式就是对所有物体两两配对——这在数学上是个组合数问题。为了不让计算量失控,Unity 提供了碰撞矩阵(Layer Collision Matrix):通过给物体分层(Layer),直接规定哪些层之间“不检测碰撞”。比如“玩家子弹”层不与“玩家身体”层碰撞,可以省掉大量无效配对。

登记簿上的每一行既是“存档”也是“警报”。聪明的剧组会分层管理:该省的配对绝不计算,该登记的接触一条不落。这样即使舞台上挤满了木偶,后台的监督依然气定神闲。

C(n,2)=n×(n−1)2\displaystyle C(n, 2) = \frac{n \times (n - 1)}{2}
n 个碰撞体最坏情况需要检查的配对数量:从 n 个物体里任取两个有多少种组合。1000 个物体就是近 50 万对。
  1. 组合数 C从 n 个物体中任取两个的不同配对方式的种数。
    C(n,2)\displaystyle C(n, 2)
  2. 分子 n(n-1)每个物体都可能与其他 n-1 个物体配对,但这样把“A-B“和”B-A”算了两遍。
    n×(n−1)\displaystyle n \times (n - 1)
  3. 除以 2去掉顺序重复后,得到真正的两两配对数量。
    n×(n−1)2\displaystyle \frac{n \times (n - 1)}{2}

射线检测

舞台监督手里总有一只激光笔:从某个位置朝某个方向打出一条直线,看这条线会先照到谁。这就是 PhysX 的射线检测(Raycast)——它不发射任何实体,也没有子弹飞行的过程,只是一次瞬间完成的数学求交查询。

在 Unity 中,一句 Physics.Raycast(origin, direction, maxDistance) 就能完成探测:指定起点、方向与最大距离,引擎会沿这条射线与场景中所有碰撞体求交,返回第一个命中的物体信息(RaycastHit),包括命中点、距离、碰撞体引用等。射线检测是“提问”而非“行动”:它不会推动物体、不会留下光迹,也不会被物理世界反过来影响,因此非常适合做高频查询——每颗子弹、每次点击都调用一次也毫无压力。

射线的数学本质是一条带参数 t 的直线方程:从起点 O 出发,沿着单位方向 D 前进,t 表示走了多远。t 从 0 增长到最大射程 L,途中第一次与某个碰撞体的轮廓相交的那个 t,就是命中的位置。还能通过 LayerMask 指定只检测某些层,比如让子弹无视队友、只打敌人。

激光笔虽不碰实物,却是指挥调度最有力的工具:枪械命中的瞬间判定、鼠标点击拾取物体、NPC 探测玩家是否在视野内——都能用一条“看不见的直线”解决。它是舞台监督在演出中最常用的一件法宝。

P⃗(t)=O⃗+t×D⃗,0≤t≤L\displaystyle \vec{P}(t) = \vec{O} + t \times \vec{D}, \quad 0 \le t \le L
射线上的任意一点都可以写成起点加上方向乘以参数 t。t=0 在起点,t=L 到达最大射程。
  1. 起点 O激光笔的出射点坐标,通常是枪口或摄像机中心。
    O⃗\displaystyle \vec{O}
  2. 方向 D单位方向向量,决定激光笔朝哪个方向照;长度固定为 1,便于直接用 t 表示距离。
    D⃗\displaystyle \vec{D}
  3. 参数 t沿方向前进的距离。物理引擎沿这条线逐步或解析地求交,找到最小的命中 t。
    0≤t≤L\displaystyle 0 \le t \le L

关节约束

有些木偶必须被“绑”在一起演出:门板需要绕着合页转、马车需要弹簧减震、双人木偶要像连体人一样行动一致。这些连接关系在 PhysX 里由 Joint(关节)组件实现——它相当于舞台监督手里的一捆绳索、铰链与锁扣,在两个刚体之间建立约束。

Unity 常用关节有四种:HingeJoint(铰链关节)让物体绕指定枢轴旋转,像门、轮子、机械臂;SpringJoint(弹簧关节)在两个刚体之间施加弹力,像悬挂系统、弹射装置;FixedJoint(固定关节)把两个刚体焊死在一起,像拖车与车厢;ConfigurableJoint(可配置关节)则把前几者的自由度全部开放,让高级玩法自定义。使用关节的前提是至少一端挂着刚体,因为约束的本质是“限制两个刚体之间的相对运动”。

弹簧关节的弹力遵循胡克定律:形变量越大,回弹力越大,且方向总是把物体拉回平衡位置。这里的 k 是弹簧刚度系数,决定弹簧“硬不硬”;x-x₀ 是当前长度与自然长度的差值。调参时如果发现木偶抖得像筛糠,通常是把弹簧刚度调太高、缺少阻尼;如果软绵绵没力气,则需要加大 k 或减小阻尼。

绳索、铰链、弹簧——舞台监督用这些看似简单的连接件,搭建起了会开门、会颠簸、会弹射的复杂机关。木偶之间的每一次牵制、每一段减震,都源自这几个参数在背后的默默计算。

Fspring=−k×(x−x0)\displaystyle F_{\text{spring}} = -k \times (x - x_0)
弹簧力等于负的刚度系数乘以形变量。负号表示力的方向与形变方向相反——拉得越长,回弹越猛。
  1. 弹簧力 F让物体回到平衡位置的恢复力,是关节产生“拉扯感”的来源。
    Fspring\displaystyle F_{\text{spring}}
  2. 刚度系数 k弹簧的硬度。k 越大,同样形变产生的恢复力越大,弹簧越“硬”。
    k\displaystyle k
  3. 形变量 (x-x₀)当前长度与自然长度的差值;负号保证力总是指向平衡位置。
    −k×(x−x0)\displaystyle -k \times (x - x_0)

AI 应用场景

射击游戏的命中判定

阶段:游戏玩法逻辑开发

解决的问题:传统做法用真实子弹实体飞行模拟命中,既消耗物理资源又容易在高速下穿透目标。改用射线检测做 hitscan 命中,从枪口沿准星方向发射一条数学射线,瞬间返回命中目标与距离,稳定、高效、零物理副作用。

具体案例:在一款 FPS 原型中,玩家开枪瞬间执行 Physics.Raycast 从摄像机中心发射射线,命中头部碰撞体则触发暴击特效,未命中则按命中点坐标生成弹孔与火花。

赛车游戏的摩擦与悬挂调参

阶段:物理仿真与参数调优

解决的问题:赛车过弯打滑或颠簸翻车,手感难以捉摸。通过给轮胎和赛道配置物理材质(动摩擦、弹性),并用 SpringJoint 模拟悬挂系统,配合刚体质量分布,可得到稳定可控的漂移手感。

具体案例:把赛道材质的动摩擦系数调低至 0.3、轮胎保持 0.9,车辆在高速过弯时产生可控侧滑;悬挂用弹簧关节加阻尼器吸收路肩颠簸,赛车落地后不再反复弹跳。

强化学习智能体的虚拟训练场

阶段:强化学习虚拟环境仿真

解决的问题:训练机器人或自动驾驶需要海量试错,实物成本与风险极高。用 PhysX 提供重力、碰撞、关节的实时物理反馈,让智能体在虚拟木偶剧场中安全地“摔倒-爬起-学会走路”。

具体案例:使用 Unity ML-Agents 训练四足机器人行走:每条腿用 HingeJoint 连接躯干,脚掌与地面用碰撞体加物理材质模拟抓地力,智能体输出关节力矩,PhysX 结算出位移与速度作为奖励信号。

Python 演示

python66 行
import math  class Rigidbody:    """2D 刚体: 简化版 Unity Rigidbody, 只有重力与反弹"""     def __init__(self, x, y, radius=20, mass=1.0):        self.x = x        self.y = y        self.vx = 0.0        self.vy = 0.0        self.radius = radius        self.mass = mass     def apply_gravity(self, g=500.0, dt=1.0 / 60.0):        self.vy += g * dt     def move(self, dt=1.0 / 60.0):        self.x += self.vx * dt        self.y += self.vy * dt     def bounce_ground(self, ground_y, restitution=0.8):        if self.y + self.radius > ground_y:            self.y = ground_y - self.radius            self.vy = -self.vy * restitution            return True        return False  def ray_vs_horizontal(origin_x, origin_y, direction_x, direction_y, ground_y):    """射线检测: 返回射线与水平地面的交点, 等价于简化版 Physics.Raycast"""    if abs(direction_y) < 1e-6:        return None    t = (ground_y - origin_y) / direction_y    if t < 0:        return None    hit_x = origin_x + direction_x * t    return hit_x, ground_y, t  def simulate(seconds=3.0, dt=1.0 / 60.0):    ball = Rigidbody(x=100.0, y=10.0, radius=20.0)    ground_y = 300.0    frames = int(seconds / dt)    for _ in range(frames):        ball.apply_gravity(g=500.0, dt=dt)        ball.move(dt)        ball.bounce_ground(ground_y, restitution=0.8)    return ball, ground_y  def main():    ball, ground_y = simulate()    print("小球最终位置: x=%.1f, y=%.1f" % (ball.x, ball.y))    print("质量=%.1f 不影响下落速度, 只影响受力大小" % ball.mass)     hit = ray_vs_horizontal(50.0, 0.0, 0.3, 1.0, ground_y)    if hit is not None:        hit_x, hit_y, t = hit        print("射线命中点: x=%.1f, y=%.1f, 参数 t=%.3f" % (hit_x, hit_y, t))    else:        print("射线未命中任何物体")  if __name__ == "__main__":    main()
目的:用纯 Python 实现一个 2D 物理小引擎,模拟刚体的重力下落、地面反弹,以及简化版的射线检测求交,对应 Unity 中 FixedUpdate 的固定步长推进节奏。
  1. class Rigidbody:定义刚体类,相当于发给木偶的“身份牌”,记录位置、速度与质量。
  2. self.vy += g * dt每个固定步长给竖直速度叠加重力增量,模拟舞台监督“每帧算账”。
  3. self.y += self.vy * dt用速度更新位置,完成一个固定步长的位移积分。
  4. if self.y + self.radius > ground_y:用“球心加半径”判断底部是否接触地面,等价于碰撞体的接触判定。
  5. self.vy = -self.vy * restitution反弹并把速度乘以 0.8,模拟碰撞后的弹性衰减。
  6. t = (ground_y - origin_y) / direction_y解射线参数方程,求出命中参数 t。
  7. if t < 0: return None命中点在射线反方向则视为未命中,Physics.Raycast 同样如此。
  8. for _ in range(frames):按固定小步长循环推进,对应 Unity 的 FixedUpdate 更新节奏。
  9. print("小球最终位置: ...")输出模拟结果,验证“质量不影响下落速度,只影响受力”这一刚体特性。

常见误区

误区我以为只要给物体挂上 Rigidbody,它自然就有碰撞能力,不需要再单独加 Collider,反正刚体名字里带着“体”。

正解刚体负责“被力推动”,碰撞体负责“提供形状”。只有刚体没有碰撞体的物体不会被任何东西挡住;只有碰撞体没有刚体的物体则是一堵静止的墙。两者要配合使用:身份牌决定了“会被重力管”,轮廓外套决定了“哪里算碰到”。

为何常见:新手最早接触的官方示例总让物体同时带着两个组件,让人误以为 Rigidbody 自带形状判定的能力。

误区为了让碰撞更精确,我把所有模型都换成了 Mesh Collider,结果游戏变得特别卡,我还以为是电脑配置不够。

正解网格碰撞体用三角形网格做逐面求交,计算量远大于球、盒、胶囊。移动物体之间的网格碰撞尤其昂贵。正确做法是用几个简单几何体组合去近似复杂外形。给每尊木偶做全身 3D 扫描固然贴合,但每秒要算几十次“扫描对齐”;改用轻便纸板箱,肉眼几乎看不出差别。

为何常见:追求精确是本能,但人们没意识到物理引擎每个固定步长都要重算所有碰撞对的几何关系,精度与性能必须权衡。

误区我把碰撞体勾选了 IsTrigger,以为碰到它时会被挡住,只是额外多收到一个事件而已。

正解触发器的本质是“透明登记处”——物体会直接穿过,只触发 OnTriggerEnter/Stay/Exit 事件,用于感应区、拾取道具、警报线等场景。要产生阻挡效果必须使用普通碰撞,而不是触发器。

为何常见:“Trigger“ 被译为”触发器“,让人误以为它触发的是”阻挡动作“,忽略了它其实是”穿越并登记”的机制。

误区我把木箱质量调到 100,以为它会比质量 1 的小球下落快得多,毕竟“重的东西沉得快”。

正解在物理引擎默认不含空气阻力的真空中,重力加速度与质量无关。重力 F=mg 代入牛顿第二定律 F=ma,质量在等式两边抵消,所有物体以相同加速度下落。质量真正影响的是碰撞冲击力和推动其他物体的效果。日常经验里重物掉得快,其实是空气阻力在捣乱。

为何常见:地面生活的直觉被空气阻力污染了,而物理引擎默认不模拟空气阻力,于是产生了强烈的反直觉落差。

误区我用 Physics.Raycast 之后,以为场景里真的出现一条可见光线,还会击飞沿路的物体,至少应该能看到一道光柱。

正解射线检测是一次纯数学的直线求交查询,不产生任何物理影响——不会推动物体、不会留光迹、也不能被遮挡后“断掉”。它只是“提问”:沿这条线走,第一个碰到谁?想要可视化,需要额外用 Debug.DrawLine 画辅助线才能看到。

为何常见:“射线”这个词的视觉联想太强,加上许多教程用 Debug 线把查询结果画出来,让初学者误以为那条线真实存在于物理世界中。

面试题

Q1. 在 Unity 中,要让一个物体受重力自然下落,必须给它挂载哪个组件?

单选题 · easy

  • A.碰撞体组件
  • B.刚体组件← 正确答案
  • C.物理材质组件
  • D.网格碰撞体

答案:B。刚体就是木偶的“身份牌”,决定物体是否受重力与外力支配;碰撞体只提供体型轮廓,物理材质只决定接触手感,网格碰撞体只是碰撞体的一种。没有刚体,物体就不会被重力拉落。

Q2. 想让两个物体“穿过去但留下记录”,碰撞体应如何设置?

单选题 · easy

  • A.勾选 IsTrigger← 正确答案
  • B.去掉刚体组件
  • C.换成网格碰撞体
  • D.调大弹性系数

答案:A。IsTrigger 让碰撞体变成“透明登记处”,物体穿过时只触发 OnTrigger* 事件而不产生阻挡。去掉刚体只会让物体变成静止的墙;换网格碰撞体与调弹性系数都无法实现“穿过并记录”。

Q3. 舞台地板“打滑”对应物理材质中的哪个参数?

单选题 · mid

  • A.弹力系数
  • B.动摩擦系数← 正确答案
  • C.物体质量
  • D.重力缩放

答案:B。动摩擦系数决定滑动时阻力大小,系数越低,木偶在舞台上越容易滑出去。弹力系数控制反弹,质量与重力缩放属于刚体配置,与“地板的滑溜程度”无关。

Q4. 关于刚体的质量与下落速度,下列说法正确的是?

单选题 · mid

  • A.质量越大下落越快
  • B.重力加速度与质量无关← 正确答案
  • C.质量直接影响摩擦系数
  • D.质量为零时物体保持静止

答案:B。重力 F=mg 与牛顿第二定律 F=ma 联立后,质量在等式两边抵消,所以所有物体在引擎中的下落加速度相同。质量影响的是碰撞冲击力与推动效果;摩擦系数由物理材质决定,质量也不会为零。

Q5. 用 Physics.Raycast 做枪械命中判定时,通常不需要考虑以下哪项?

单选题 · easy

  • A.发射起点 Origin
  • B.射线方向 Direction
  • C.最大距离 MaxDistance
  • D.枪械后坐力动画← 正确答案

答案:D。射线检测需要起点、方向和最大距离这三大输入。后坐力动画属于表现层,由动画系统处理,与射线求交的数学查询无关,也不参与命中的计算。

Q6. 场景中有 1000 个碰撞体,普通两两检测在最坏情况下的配对数量是多少?

单选题 · hard

  • A.1000 对
  • B.1000×999÷2 对← 正确答案
  • C.1000×1000 对
  • D.500×500 对

答案:B。两两配对是组合问题 C(1000,2) = 1000×999÷2 ≈ 49.95 万对。A 少算了约三个数量级;C 把同一对正反重复计算了两遍;D 是随机猜测值,均不符合组合数学定义。

Q7. 角色高速下落时直接穿过了平台,最可能的原因是?

单选题 · hard

  • A.平台缺少刚体组件
  • B.单帧位移过大发生隧穿← 正确答案
  • C.平台碰撞体设置过大
  • D.物理材质摩擦力过大

答案:B。当物体在一个固定步长内移动的距离超过自身碰撞体厚度时,就可能跳过与平台的接触判定,称为隧穿(tunneling)。平台不需要刚体也能阻挡角色;碰撞体过大反而更容易命中;摩擦力只影响切向滑动。

Q8. 关于 Unity 物理引擎 PhysX,下列说法正确的有?

多选题 · mid

  • A.Unity 默认集成 PhysX 引擎← 正确答案
  • B.刚体负责物体的动力学模拟← 正确答案
  • C.射线检测会在场景中留下光迹
  • D.物理材质同时调节摩擦与弹性← 正确答案

答案:ABD。Unity 默认使用 NVIDIA PhysX;刚体负责受力与运动,碰撞体提供形状;物理材质包含动/静摩擦与弹性参数。射线检测只是数学求交查询,不会产生可见光迹,C 错误。

Q9. 以下哪些属于 Unity 中常用的关节(Joint)类型?

多选题 · mid

  • A.铰链关节 HingeJoint← 正确答案
  • B.弹簧关节 SpringJoint← 正确答案
  • C.固定关节 FixedJoint← 正确答案
  • D.布料关节 ClothJoint

答案:ABC。HingeJoint 用于门和轮子的绕轴旋转,SpringJoint 模拟弹簧减震,FixedJoint 将两个刚体固定为一体,它们都是标准关节。ClothJoint 不是关节组件,布料模拟走的是独立的 Cloth 系统,D 错误。

Q10. 以下哪些是提升物理场景性能的合理手段?

多选题 · hard

  • A.用简单碰撞体组合代替网格碰撞体← 正确答案
  • B.通过碰撞矩阵过滤无关层级的配对← 正确答案
  • C.适当增大固定时间步长减少结算次数← 正确答案
  • D.给所有静态物体挂载刚体组件

答案:ABC。简单碰撞体计算快;碰撞矩阵能直接砍掉大量无效配对;增大 Fixed Timestep 会降低结算频率从而提升性能(但需警惕隧穿)。给静态物体加刚体会让引擎把它们纳入动态计算,反而拖慢性能,D 错误。