搞懂物理系统的核心组件:碰撞体与刚体的正确打开方式
很多新手刚接触Unity物理系统时,都会犯一个错误——随便拖个碰撞体和刚体就开始调参数,结果要么物体飘在空中,要么碰撞没反应。其实,碰撞体和刚体是物理系统的“基石”,选对类型、调对参数才能让物理模拟正常工作。

先看碰撞体的类型,不同形状适合不同场景:
碰撞体类型 | 适合场景 | 性能消耗 | 注意事项 |
---|---|---|---|
Box Collider | 立方体物体(箱子、墙壁) | 低 | 尽量贴合物体形状,避免过大 |
Sphere Collider | 球形物体(球、弹丸) | 低 | 中心位置要对齐物体重心 |
Capsule Collider | 角色、柱子等圆柱形物体 | 中 | 高度要覆盖角色全身,避免漏碰 |
Mesh Collider | 复杂形状物体(异形岩石、家具) | 高 | 用简化Mesh,避免使用凸面选项 |
比如做角色控制器时,首选Capsule Collider——它能完美贴合角色的圆柱形身体,而且碰撞检测的稳定性比Mesh Collider高太多。但要注意:不要勾“Is Trigger”!Trigger碰撞体不会产生物理力反馈,只会触发事件。角色要能站在地面上,必须用非Trigger的碰撞体,再配合刚体。
说到刚体(Rigidbody),它是让物体受物理规则影响的关键。但刚体的参数不是随便填的:
– 质量(Mass):Unity的质量是相对值,比如角色质量设为1,箱子设为5,角色推箱子时会更费力。如果质量设得太大(比如1000),物体可能会“穿透”地面,因为物理引擎计算不过来。
– 使用重力(Use Gravity):角色要下落就勾上,但像漂浮的飞船就不要勾。
– 冻结旋转(Freeze Rotation):角色控制器一定要勾上X和Z轴!不然角色撞墙后会“翻跟头”,站都站不稳。
给你看个正确的角色刚体设置代码(直接在Inspector调也可以,但代码更直观):
using UnityEngine;
public class PlayerRigidbodySetup : MonoBehaviour
{
private Rigidbody _rigidbody;
void Awake()
{
_rigidbody = GetComponent<Rigidbody>();
_rigidbody.mass = 1f;
_rigidbody.useGravity = true;
_rigidbody.freezeRotation = true; // 冻结旋转,避免倒地
_rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous; // 解决快速移动穿透问题
}
}
自定义物理行为:用ScriptableObject封装物理规则
默认的Physic Material虽然能调弹性和摩擦力,但没法复用——比如你有10个不同的箱子,每个都要调一遍弹性系数,太麻烦了。这时候ScriptableObject就派上用场了——它能把物理参数封装成可复用的“配置文件”,改一次就能同步到所有引用的物体上。
比如,我们做一个“物理材质数据”的ScriptableObject:
using UnityEngine;
[CreateAssetMenu(fileName = "PhysicsMaterialConfig", menuName = "Game/Physics Material Config")]
public class PhysicsMaterialConfig : ScriptableObject
{
[Header("基础属性")]
public float bounciness = 0.5f; // 弹性(0=不弹,1=完全反弹)
public float friction = 0.3f; // 摩擦力(0=光滑,1=粘滞)
[Header("进阶属性")]
public float drag = 0.1f; // 空气阻力
public float angularDrag = 0.5f;// 旋转阻力
}
然后,写一个脚本把这个配置应用到物体上:
using UnityEngine;
public class CustomPhysicsObject : MonoBehaviour
{
public PhysicsMaterialConfig materialConfig;
private Rigidbody _rigidbody;
private Collider _collider;
void Start()
{
_rigidbody = GetComponent<Rigidbody>();
_collider = GetComponent<Collider>();
// 应用刚体属性
_rigidbody.drag = materialConfig.drag;
_rigidbody.angularDrag = materialConfig.angularDrag;
// 应用碰撞体材质
var physicMaterial = new PhysicMaterial();
physicMaterial.bounciness = materialConfig.bounciness;
physicMaterial.dynamicFriction = materialConfig.friction;
physicMaterial.staticFriction = materialConfig.friction;
_collider.material = physicMaterial;
}
}
这样,你只要在Project窗口右键创建“Physics Material Config”,调整参数,再拖给物体的CustomPhysicsObject脚本,所有物体的物理属性就同步了——是不是比一个个调方便多了?
物理交互的细节优化:从帧率稳定到效果真实
你有没有遇到过这种情况:游戏里有很多物理物体(比如一堆箱子),当玩家碰它们时,帧率突然从60掉到30?这是因为物理模拟的性能消耗太大了。别急,这几个技巧能帮你把帧率拉回来:
1. 用LayerMask过滤碰撞检测
Unity的物理引擎会检测所有碰撞体,但很多碰撞是没必要的——比如玩家的碰撞体不用和其他玩家的碰撞体检测(除非要互相推搡)。你可以:
– 在Edit→Project Settings→Physics里,找到“Layer Collision Matrix”,把不需要检测的层之间的勾去掉。
– 代码里用LayerMask指定检测范围,比如射线只检测地面:
LayerMask groundLayer = LayerMask.GetMask("Ground");
if (Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, 1f, groundLayer))
{
// 检测到地面
}
2. 用简化碰撞体代替Mesh Collider
Mesh Collider的性能消耗是Box Collider的5-10倍!比如你有个复杂的异形岩石,与其用Mesh Collider,不如用3个Box Collider拼接——既能贴合形状,又能降低性能消耗。
3. 用Physics.RaycastNonAlloc代替Raycast
普通的Raycast会返回一个Hit对象,每次调用都会产生GC(垃圾回收),当你在Update里频繁调用时,GC会导致帧率波动。而RaycastNonAlloc会把结果存到预先分配的数组里,完全没有GC:
private RaycastHit[] _hitBuffer = new RaycastHit[10]; // 预先分配缓冲区
void Update()
{
int hitCount = Physics.RaycastNonAlloc(transform.position, Vector3.forward, _hitBuffer, 10f);
for (int i = 0; i < hitCount; i++)
{
Debug.Log("Hit: " + _hitBuffer[i].collider.name);
}
}
4. 利用2025版的增强型Physics Debugger
Unity 2025.1更新了Physics Debugger,现在可以实时查看碰撞体的接触点、刚体的受力方向,甚至能看到物理引擎的计算帧!比如你发现物体总是穿透地面,打开Physics Debugger→Collisions→Contact Points,就能看到碰撞体有没有和地面接触——如果接触点是空的,说明碰撞体的位置不对,或者层掩码没设置好。
2025版Unity物理新特性:DOTS物理的实战应用
如果你要做大量物理对象的场景(比如1000个球同时下落),传统的MonoBehaviour物理系统肯定会卡顿——因为每个刚体都是一个GameObject,CPU要处理大量的对象实例。这时候DOTS(Data-Oriented Tech Stack)物理就派上用场了。
DOTS物理用ECS(Entity Component System)架构,把所有物理对象的数据存在连续的内存块里,用Burst Compiler编译成高效的机器码,性能比传统物理系统高10倍以上。
给你看个DOTS物理的实战例子:spawn 1000个动态刚体球:
首先,在Package Manager里安装Unity Physics、Entities、Burst包(2025版的包已经很稳定了)。然后写一个Spawner脚本:
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
public class DynamicRigidbodySpawner : MonoBehaviour
{
public GameObject prefab; // 要spawn的球预制体(需加ConvertToEntity组件)
public int count = 1000;
public float3 spawnArea = new float3(20, 0, 20); // spawn区域
void Start()
{
var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
// 把预制体转换成Entity
var prefabEntity = GameObjectConversionUtility.ConvertGameObjectHierarchy(prefab, World.DefaultGameObjectInjectionWorld);
for (int i = 0; i < count; i++)
{
// 实例化Entity
var entity = entityManager.Instantiate(prefabEntity);
// 设置位置:在spawn区域内随机
var position = new float3(
UnityEngine.Random.Range(-spawnArea.x/2, spawnArea.x/2),
10f,
UnityEngine.Random.Range(-spawnArea.z/2, spawnArea.z/2)
);
entityManager.SetComponentData(entity, new Translation { Value = position });
// 设置旋转(可选)
entityManager.SetComponentData(entity, new Rotation { Value = quaternion.identity });
}
}
}
注意:预制体必须加“ConvertToEntity”组件,这样Unity才会把它转换成DOTS的Entity。然后,你可以在预制体上加Physics Shape组件(代替传统的碰撞体),选Sphere形状——这样每个球都会有物理碰撞。
运行游戏,你会看到1000个球同时下落,帧率依然保持在60以上!这就是DOTS物理的威力——它能轻松处理大规模的物理模拟,适合做沙盒游戏、粒子效果或者 crowd simulation。
常见物理问题排坑:避免踩中90%开发者的雷区
我整理了几个新手最常犯的错误,帮你快速排雷:
1. 刚体穿透
快速移动的物体(比如子弹)穿过墙壁?解决方法:把刚体的Collision Detection Mode设为Continuous——它会在每帧之间进行“连续”碰撞检测,避免穿透。但Continuous的性能消耗比Discrete高,所以只给快速移动的物体用。
2. 角色跳不高
用AddForce加力时,ForceMode选不对?比如跳的时候用ForceMode.Impulse(瞬间力),而不是Acceleration(持续力)。正确的跳跃代码:
public void Jump()
{
if (IsGrounded()) // 检查是否在地面上
{
_rigidbody.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
}
3. 碰撞没反应
检查这几点:
– 两个物体都有碰撞体吗?
– 至少有一个物体有刚体吗?
– 碰撞体没有勾“Is Trigger”吗?
– 层掩码允许两者碰撞吗?
4. 物理卡顿
除了前面说的优化技巧,还要注意:FixedUpdate的频率不要改——Unity默认是50Hz(每0.02秒一次),改得太高(比如100Hz)会增加CPU负担,改得太低(比如20Hz)会导致物理模拟不流畅。
5. 物体“飘”在空中
刚体的“Interpolate”设为None?改成Interpolate或Extrapolate——它会让刚体的运动更平滑,避免和渲染帧不同步。
现在,你可以试着用这些技巧做个小demo:比如一个房间里有几个箱子,角色能推箱子,箱子碰到墙壁会反弹。先给箱子加Box Collider和Rigidbody,再用ScriptableObject封装箱子的弹性系数,最后用DOTS物理spawn 10个箱子——看看效果是不是比之前流畅多了?
原创文章,作者:,如若转载,请注明出处:https://zube.cn/archives/401