前言
提示:声明:此篇文章是个人学习笔记,并非教程,所以内容可能不够严谨。可作参考,但不保证绝对正确。如果你发现我的文章有什么错误,非常欢迎指正,谢谢哦。
本文大部分内容来自官方文档!!!
官方手册:Rigidbody
官方API:Rigidbody Api
一、 Rigidbody组件简介
如果希望某个游戏物体能够受到物理系统中力的影响,就要添加此组件
二、Rigibody组件属性
属性 | 功能 |
---|---|
Mass | 物体的质量(默认情况下以千克为单位)。 |
Drag | 阻力。物体移动时,空气阻力大小,0表示没有空气阻力,无限值会让物体立马停下来 |
Angular Drag | 角阻力。 物体的旋转时,空气阻力对物体的影响程度。0表示没有空气阻力。请注意,仅通过将其角度阻力设置为无穷大,无法使对象停止旋转。 |
Use Gravity | 是否使用重力 |
Is Kinematic | 如果启用,则对物体不会受物理系统的任何影响,但是会充当一个静态物体对其它Rigidbody产生影响。它不再受力的影响,也就是说只能通过Transform进行移动、旋转操作。主要用于将物体的控制权从物理系统交给动画系统 |
Interpolate | 插值。如果你发现此rigidbody移动的时候会发生抖动,再去调整此项 |
None:无插值,默认选项 Interpolate:基于前一帧的Transform来变换平滑Transform Extrapolate:基于预估的下一帧的Transform来平滑Transform | |
Collision Detection | 碰撞检测,用于防止快速移动的对象在未检测到碰撞的情况下通过其他对象。 |
Discrete:对场景中的其它物体采用离散碰撞检测,其它物体也会对它采用离散碰撞检测。适合普通物体。 | |
Continuous:对动态物体(有rigidbody)采用离散碰撞检测,对静态物体(没有rigidbody)采用连续碰撞检测。Rigidbody设为Continuous Dynamic的物体会对此物体采取连续碰撞检测,其他物体将会对此物体采用离散碰撞检测。适用于会被 Continuous Dynamic 刚体碰撞的物体。如果使用Discrete没啥问题,就不要选这个,太耗资源。 | |
Continuous Dynamic:对场景中其采用Continuous 和 Continuous Dynamic的物体采用连续碰撞检测,对静态物体也使用连续碰撞检测。对其它物体使用离散碰撞检测。适用于快速移动的物体。 | |
上面三种模式容易看的糊涂,可以看这篇文章有助于理清思路。 | |
Continuous Speculative:对刚体和碰撞体采用推测连续碰撞检测。这是唯一你可以用于运动学实体的连续碰撞模式。它比前一项Continuous Dynamic更节省资源。这是一种可以在动态和运动对象上使用的碰撞检测模式。该模式的成本通常低于其他 CCD模式。此外,还可以更好地处理角运动。但是,在某些情况下,高速对象仍可能穿过其他几何形状。 | |
Constraints | 给刚体设置运动的限制条件 |
Freeze Position:让刚体不会沿世界坐标的X轴或Y轴或Z轴移动 | |
Freeze Rotation:让刚体不会绕自身坐标的X轴或Y轴或Z轴旋转 |
关于碰撞检测(Collision Detection)属性
(百度百科)离散点的碰撞检测是指定某一时刻T的两个静态碰撞体,看它们之间是否交迭,如果没有交迭则返回它们最近点的距离,如果交迭则返回交迭深度,交迭方向等。连续碰撞检测则是分别指定在T1、T2两个时刻两个碰撞体的位置,看它们在由T1运动到T2时刻的过程中是否发生碰撞,如果碰撞则返回第一碰撞点的位置和法线。连续碰撞检测是最为自然的碰撞检测,可以大大方便碰撞响应逻辑的编写,可以很容易避免物体发生交迭或者穿越。离散点的碰撞检测则没有那么友好,当检测到碰撞时两个物体已经发生了交迭,如果其中有三角形网格对象那么已经有许多三角形发生了交迭,如何将两个交迭的对象分开并按合理的方式运动是一个挑战。虽然连续碰撞检测是最自然的方式,但它的实现非常复杂,运算开销也很大
我的理解:离散碰撞会在每一帧检测两个物体是否有重叠部分,如果没有重叠部分就表示两个物体没有碰撞。在这种情况下,如果物体速度过快,可能第一帧A在B前面,第二帧A就在B后面了,因为这两帧二者都没有重叠,就会被当作没有发生碰撞处理了。
我的理解:连续碰撞检测会在每一帧都计算当前帧和上一帧之间两个物体的移动路线是否有交点,有就表示发生过碰撞。这种方式就不会出现,物体移动速度过快而导致没有检测到碰撞的情况。但是这会大大增加物理系统的开销。
三、移动和旋转的函数
添加Rigidbody后,移动或者旋转物体最好就不要用Transform了,容易出现问题。应该直接调用Rigidbody组件提供的函数或属性。推荐在FixedUpdate函数中调用这些施加力的函数, 而不要Update函数中调用。物理的更新和Update的帧频率不是匹配的,但是FixedUpdate函数是在每次物理更新之前调用,就正好把更新的数据传入物理系统。
函数名称 | 功能 | 函数声明 | 参数说明 |
---|---|---|---|
AddExplosionForce | 给Rigidbody施加了一个力模拟爆炸效果。力会施加到物体距离爆炸中心最近的表面上的点。 | public void AddExplosionForce(float explosionForce, Vector3 explosionPosition, float explosionRadius, float upwardsModifier = 0.0f, ForceMode mode = ForceMode.Force)); | explosionForce:爆炸的力度,可随距离变化。 explosionPosition:爆炸中心位置 explosionRadius:爆炸半径。如果设为0,则表示不管距离爆炸中心多远都承受全部的力。 upwardsModifier:可将施加力的位置沿Y轴移动-upwardModifier,但是力的大小不变,可用于营造物体被炸到空中的效果。 mode:施加力的方式,参见下方“参数ForceMode” |
AddForce | 给Rigidbody添加一个力 | public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force); | Force:力的方向和大小。 mode:施加力的方式,参见下方“参数ForceMode” |
AddForceAtPosition | 在指定点施加力.如果想要真实的效果,力的施加位置应在物体表面附近 | public void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode mode = ForceMode.Force); | force:力的方向和大小 。 position:施加力的位置。 mode:施加力的方式,参见下方“参数ForceMode” |
AddRelativeForce | 施加一个相对其自身坐标系的力 | public void AddRelativeForce(Vector3 force, ForceMode mode = ForceMode.Force); | force:力的方向和大小。 mode:施加力的方式,参见下方“参数ForceMode” |
AddTorque | 给Rigidbody添加一个扭矩/转矩,让物体旋转。会发现物体的旋转速度达到一定就不能再增加了,可以通过修改maxAngularVelocity解决。 | public void AddTorque(Vector3 torque, ForceMode mode = ForceMode.Force); | torque:扭矩/转矩方向和大小。 mode:施加力的方式,参见下方“参数ForceMode” |
AddRelativeTorque | 施加一个相对其自身坐标系的扭矩。 | public void AddRelativeTorque(Vector3 torque, ForceMode mode = ForceMode.Force); | torque:扭矩/转矩方向和大小。 mode:施加力的方式,参见下方“参数ForceMode” |
MovePosition | 让Rigidbody移动到指定位置。会根据Rigidbody组件的Interpolate属性进行计算 。如果想要传送Rigidbody,直接设置Rigidbody.position的值即可 | public void MovePosition(Vector3 position); | position:世界坐标系位置 |
MoveRotation | 将Rigidbody旋转到指定角度(四元数)。会根据Rigidbody组件的Interpolate属性进行计算 | public void MoveRotation(Quaternion rot); | rot:想要rigidbody达到的旋转角度 |
参数ForceMode
我们都知道,力作用在物体上会增加物体的移动速度(不考虑摩擦),而随着力作用的时间越长增加的速度就越大。因此我们使用Rigidbody的函数施加力的时候,也要考虑力作用的时间长度,其中ForceMode参数就是用来设置作用时间长度的。
ForceMode意为施加力的模式,它是个枚举类型,在我们使用上面的函数给Rigidbody施加力的时候,可以传入具体值来指定施加力的方式。本质上就是用来指定力的作用时间长度的,函数会根据指定的ForceMode的值,计算施加力后Rigidbody的速度值。
下面例子均为假设物体初速度2,质量为5,而施加力为10(水平X轴方向)的情况
值 | 含义 |
---|---|
Force | 表示力作用到物体0.02s(一个固定帧的时间长度/FixedUpdate函数的执行间隔,可修改)。物体的速度直接变为该力作用到物体0.02s后的速度。 速度 = v0+at = v0 + f/mt = 2+10/50.02=2.04 |
Impulse | 表示力作用到物体1s,物体的速度直接变为该力作用到物体1s后的速度。 速度 = v0+at = v0 + f/mt = 2+10/51 = 4 |
Acceleration | 和Force一样,不过会用1代替物体的实际质量。 速度 = v0+at = v0 + f/mt = 2+10/10.02=2.2 |
VelocityChange | 和Impulse一样,不过会用1代替物体的实际质量。 速度 = v0+at = v0 + f/mt = 2+10/11=12 |
四、补充:
Transform和Rigidbody
Transform组件和Rigidbody组件都可以用来移动和旋转物体,在Transform上修改物体位置或者旋转,是直接修改物体坐标来移动物体不涉及物理系统。而使用Rigidbody组件移动和旋转物体是会参与物理系统计算的。通常情况下,同一个物体只会使用Transform或Rigidbody中的一个来对物体移动或旋转,如果同时使用两个可能会出现问题。
关于父子关系
1.Rigidbody使用的collider是{本物体的Collider以及所有没有Rigidbody的子物体的Collider组成的整体}。换个说法,某个物体有collider但是没用rigidbody,那么它的collider会作为有rigidbody的父物体的collider。
2.有Rigidbody组件的物体的物理碰撞效果是单独的,会与父物体分隔开。
例如:有三个球体分别是(1) Red、(2) Green、(3) Blue。其中(1) Red和(2) Green有Rigidbody,而(3) Blue没有。它们的父子关系如下
当运行游戏时会发现,(1) Red和(3) Blue会作为一个整体计算物理效果。而(2) Green作为一个单独的物体计算物理效果。
3.有Rigidbody的物体,在父物体修改位置的时候,依然会受到父物体Transform的影响,但同时也会保持物理效果(重力、碰撞等。)
例如:有两个球体分别时(1) Red 和(2) Green,其中(2) Green有Rigidboy而(1) Red没有。它们的父子关系如下
当运行游戏后,修改(1) Red的位置会发现(2) Green的位置也会受到影响,但同时,(2) Green依然保持物理效果。
碰撞体的大小比Mass属性更重要
默认情况下,Unity的1个单位表示1米。如果你发现物体的物理效果不合意,要考虑模型大小是否符合现实。大楼倒塌的方式和玩具积木是不同的。所以最好在建模时按照真实的尺寸。如果导入的模型尺寸不合适,你又不方便去调整模型了,可以点击导入的模型,在Import Setting中修改缩放比例。
另外要注意,如果你在游戏中实例化不同尺寸的同一物体,你可以选择修改Transform上的Scale缩放物体,但是这种做法会让物理系统多消耗一丢丢资源,无伤大雅,但是更好的方法是上面提到的两种。
提示
1.两个Rigidbody的相对Mass(质量)会决定二者碰撞时的效果。
2.想要一个物体下落比另一个慢,就去调整Drag(空气阻力)值。调整质量是毛有用滴。
3.一个低的Drag(空气阻力)值会让物体看起来更重,通常Drag的值为0.01(金属块)到10(羽毛)。
4.如果你想要使用Transform来控制物体移动但是又想保留物理效果,就添加Rigidbody组件然后勾选Is Kinematic。
5.如果你使用Transform移动某个物体,又希望能接收到碰撞或触发器的回调,那只能添加一个Rigidbody组件洛。
6.如果你想让一个物体停止旋转,仅仅将Rigidbody的Angular Drag 属性设为无限是没用滴。