一、刚体 Rigid Body
刚体利用体积(碰撞器 Collider)进行碰撞计算,模拟真实的碰撞效果,产生力的作用
碰撞产生的必要条件:
- 两个物体都有碰撞器 Collider
- 至少一个物体有刚体
-
Mass:质量
默认为千克,质量越大惯性越大
-
Drag:空气阻力
根据力移动对象时影响空气阻力大小
0 表示没有空气阻力
-
Augular Drag:旋转阻力
根据扭矩旋转对象时影响对象的空气阻力大小
0 表示没有阻力
-
Use Gravity:是否受重力影响
-
Is Kinematic:是否设置为运动学
如果启用,对象将不会被物理引擎驱动,只能通过 Transform 对其进行操作
对于移动平台,或者如果要动画附加了 HingeJoint 的刚体,此属性将非常有用
-
Interpolate:插值运算
-
None:不应用插值运算
-
Interpolate:根据前一帧的变换来平滑变换
-
Extrapolate:插值运算
根据下一帧的估计变换来平滑变换,若物理帧时间过长,此效果将不好
-
-
Collison Detection:碰撞检测模式
-
Discrete:离散检测
对场景中的所有其他碰撞体使用离散碰撞检测,其他碰撞体在测试碰撞时会使用离散检测。
用于正常碰撞(默认值)
-
Continuous:连续检测
对动态碰撞体(具有刚体)使用离散碰撞检测
对静态碰撞体(没有刚体)使用连续碰撞检测
设置为连续动态的刚体,将在测试与该刚体的碰撞时使用连续碰撞检测(物理性能消耗较大,物体运动缓慢时请设置为 Discrete)
其他刚体将使用离散碰撞检测
-
Continuous Dynamic:连续动态检测
对设置为连续和连续动态碰撞的对象使用连续碰撞检测
对静态碰撞体(没有刚体)使用连续碰撞检测
对其他碰撞体使用离散碰撞检测
用于快速移动的对象
-
Continuous Speculative:连续推测检测
对刚体和碰撞体使用推测性连续碰撞检测
通常比连续碰撞检测的成本更低
显示详细信息无刚体碰撞盒 Discrete Continuous Continuous Dynamic Continuous Speculative 无刚体碰撞盒 不检测碰撞 Discrete Continuous Continuous Continuous Speculative Discrete Discrete Discrete Discrete Discrete Continuous Speculative Continuous Continuous Discrete Discrete Continuous Continuous Speculative Continuous Dynamic Continuous Discrete Continuous Continuous Continuous Speculative Continuous Speculative Continuous Speculative Continuous Speculative Continuous Speculative Continuous Speculative Continuous Speculative 性能消耗关系:Continuous Dynamic > Continuous Speculative > Continuous > Discrete
-
-
Constrains:对刚体运动的约束
- Freeze Position:限制刚体在世界坐标轴下 X、Y、Z 轴的移动
- Freeze Rotation:限制刚体在世界坐标轴下 X、Y、Z 轴的旋转
-
Info:用于显示参数的面板,不修改里面的值
碰撞器表示物体的体积(形状)
碰撞器种类一共有 6 种:
- 盒状碰撞器 Box Collider
- 球状碰撞器 Sphere Collider
- 胶囊碰撞器 Capsule Collider
- 网格碰撞器 Mesh Collider
- 轮胎碰撞器 Wheel Collider
- 地形碰撞器 Terrain Collider
常用的为前 3 种
(一)共同参数
-
Edit Collider:编辑碰撞器的大小
-
Is Trigger:是否为触发器
如果启用,则该碰撞体用于触发事件,并被物理引擎所忽略
主要用于进行没有物理效果的碰撞检测
-
Material:物理材质
可以确定碰撞体和其他对象碰撞时的交互(表现)方式
-
Center:碰撞体的中心偏移位置
(二)常用碰撞器
- Box Collider
- Size:碰撞体在 X、Y、Z 方向上的大小
- Sphere Collider
- Radius:球形碰撞体的半径大小
- Capsule Collider
- Radius:胶囊体的半径
- Height:胶囊体的高度
- Direction:胶囊体在对象局部空间中的轴向
(三)异形物体使用多种碰撞器组合
刚体对象的子对象碰撞信息参与碰撞检测
即父物体添加 Rigid Body,子物体设置 Collider。
(四)不常用碰撞器
-
Mesh Collider
-
Convex:是否为凸面的
勾选后,该 Mesh Collider 将会与其他 Mesh Collider 发生碰撞,最多支持 255 个三角面片
如果该对象添加了刚体 Rigid Body,则该选项必须勾选,否则会报错
-
Cooking Options:物理引擎对网格的处理方式
不常用
-
Mesh:引用需要用于碰撞的网格
-
-
Wheel Collider
赛车游戏中使用,其他时候不常用
注意:添加 Wheel Collider 后一定要添加 Rigid Body(或在父物体添加),否则将失效
-
Terrain Collider
地形系统中使用
性能较为低下,很少使用
在 Project 中创建物理材质 Physics Material
-
Dynamic Friction:滑动摩擦力
0 表示绝对光滑,1 表示迅速静止
-
Static Frction:静摩擦力
0 表示绝对光滑,1 表示无法移动
-
Bounciness:表面弹性
0 表示不会反弹,1 表示反弹没有能量损失,甚至可能会增加少量能量
-
Friction Combine:摩擦力组合方式
- Average:取平均值
- Minimum:取最小值
- Maximum:取最大值
- Multiply:相乘
-
Bounce Combine:弹性组合方式
- Average:取平均值
- Minimum:取最小值
- Maximum:取最大值
- Multiply:相乘
碰撞和触发响应函数属于特殊的生命周期函数,也是通过反射调用
碰撞和触发器函数都可以写成虚函数,在子类去重写逻辑
(一)物理碰撞检测响应函数
// 碰撞触发接触时会 自动执行这个函数
private void OnCollisionEnter(Collision collision)
{
// Collision类型的 参数 包含了 碰到自己的对象的相关信息
// 关键参数
// 1.碰撞到的对象碰撞器的信息
collision.collider
// 2.碰撞对象的依附对象(GameObject)
collision.gameObject
// 3.碰撞对象的依附对象的位置信息
collision.transform
// 4.触碰点数相关
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 + "正在水乳相融");
}
-
只要挂载的对象能和别的物体产生碰撞或者触发,那么对应的这 6 个函数就能够被响应
-
6 个函数不是说都得写,一般根据需求来进行选择书写,一般用 Enter 函数较多
-
如果是一个异形物体,刚体在父对象上,如果你想通过子对象上挂脚本检测碰撞是不行的,必须挂载到这个刚体父对象上才行
(一)刚体自带添加力的方法
- 获取刚体组件
rigidBody = this.GetComponent<Rigidbody>();
- 1
- 添加力
// 相对世界坐标
// 世界坐标系 Z轴正方向加了一个力
// 加力过后 对象是否停止移动 是由阻力决定的
// 如果阻力为0 那给了一个力过后 始终 是不会停止运动
rigidBody.AddForce(Vector3.forward * 10);
// 如果想要在 世界坐标系方法中 让对象 相对于自己的面朝向动
rigidBody.AddForce(this.transform.forward * 10);
// 相对本地坐标
rigidBody.AddRelativeForce(Vector3.forward * 10);
//如果你希望即使有阻力 也希望对象一直动 那你就把下面代码放在Update函数中 一直“推”就行了
rigidBody.AddForce(Vector3.forward * 10);
- 添加扭矩力
// 相对世界坐标
// 绕y轴旋转
rigidBody.AddTorque(Vector3.up * 10);
// 相对本地坐标
rigidBody.AddRelativeTorque(Vector3.up * 10);
- 直接改变速度
// 这个速度方向 是相对于 世界坐标系的
// 如果要直接通过改变速度 来让其移动 一定要注意这一点
rigidBody.velocity = Vector3.forward * 5;
- 模拟爆炸效果
// 模拟爆炸的力 一定是 所有希望产生爆炸效果影响的对象
// 都需要得到他们的刚体 来执行这个方法 才能都有效果
// 参数一 爆炸力
// 参数二 爆炸位置
// 参数三 爆炸半径范围
rigidBody.AddExplosionForce(100, Vector3.zero, 10);
(二)力的模式
// 第二个参数 力的模式 主要的作用 就是 计算方式不同而已
// 由于4中计算方式的不同 最终的移动速度就会不同
rigidBody.AddForce(Vector3.forward * 10, ForceMode.Acceleration);
速度计算公式:v = F * t / m(动量定理)
-
Acceleration
给物体增加一个持续的加速度,忽略其质量
F = (0, 0, 10)、t = 0.02 s、m = 默认为 1
v = 10 * 0.02 / 1 = 0.2 m/s
每物理帧移动 0.2 m/s * 0.02 = 0.004 m -
Force
给物体添加一个持续的力,与物体的质量有关
F = (0, 0, 10)、t = 0.02 s、m = 2 kg
v = 10 * 0.02 / 2 = 0.1 m/s
每物理帧移动 0.1 m/s * 0.02 = 0.002 m -
Impulse
给物体添加一个瞬间的力,与物体的质量有关,忽略时间,t 默认为1
F = (0, 0, 10)、t = 1 s、m = 2 kg
v = 10 * 1 / 2 = 5 m/s
每物理帧移动 5 m/s * 0.02 = 0.1 m -
VelocityChange
给物体添加一个瞬时速度,忽略质量,忽略时间
F = (0, 0, 10)、t = 1 s、m = 默认为 1
v = 10 * 1 / 1 = 10 m/s
每物理帧移动 10 m/s * 0.02 = 0.2 m
(三)力场
为物体添加 Constant Force,若物体之前没有 Rigid Body,则会为其自动添加
进行参数设置
(四)刚体休眠
Unity 为了节约性能,为刚体添加了休眠机制:
Rigidbody sleeping happens completely automatically. Whenever a rigidbody is slower than the sleepAngularVelocity and sleepVelocity it will start falling asleep. After a few frames of resting it will then be set to sleep. When the body is sleeping, no collision detection or simulation will be performed anymore. This saves a lot of CPU cycles.
刚体休眠完全自动发生。只要刚体的速度低于 sleepAngularVelocity 和 sleepVelocity,该刚体就会开始休眠。其空闲一些帧后,就会被设置成休眠状态。处于休眠状态中的物体,不会再对其进行碰撞检测和模拟。这会节约大量的 CPU 开销。
例如,在为 Cube 添加 Rigid Body 后其下落静止在平面上
将平面旋转一定角度后,发现 Cube 悬空,说明此时 Cube 的刚体休眠了
将平面向下以一定速度拖动,则 Cube 下落,刚体被激活
使用代码控制:
// 获取刚体是否处于休眠状态 如果是
if (rigidBody.IsSleeping())
{
// 就唤醒它
rigidBody.WakeUp();
}