刚体

概述
在整个 Unity 物理系统中,最重要概念就是刚体 Rigidbody。

刚体是物理学中的概念,它是指在运动中和受力后,形状和大小不变,并且内部各点相对位置不变的物体。刚体是一种为了方便物理计算而提出的理想化模型,在不需要考虑物体本身的形变时,可以把物体当做刚体进行处理。

在 Unity 中,提供了 Rigidbody 组件,用来模拟刚体,我们叫它刚体组件。给游戏对象添加刚体组件,就能够使游戏对象受到碰撞弹开、能够受到重力下落、能够给其它刚体施加力等。所以刚体组件的主要作用就是:

使游戏对象能够受力和施力。

 

刚体组件


Mass 质量:用来模拟物体的质量,单位为千克。质量会影响惯性大小,而且质量不同的两个刚体相互碰撞时,它们各自的反应也不同。要注意物体的质量大小不会影响自由落体时的速度,想想伽利略的两个铁球同时落地。

Drag 空气阻力:物体受力移动时受到的空气阻力大小,0 表示没有空气阻力。如果想让两个物体自由落体时速度不同,就给它们设置不同的空气阻力。空气阻力越大,物体感觉起来就越轻,比如铁块的空气阻力可以设置为 0.001,羽毛的空气阻力可以设置为 10。这个值一般不需要设置,如有特殊需要的话,就参考铁块和羽毛的阻力范围进行设置。

Angular Drag 角阻力:物体受力旋转时受到的空气阻力大小。因为物体旋转时的速度叫做角速度,所以旋转时受到的阻力就叫做角阻力。这个属性和 Drag 空气阻力属性类似, Drag 空气阻力属性会影响物体在空气中移动时的速度,Angular Drag 角阻力会影响物体在空气中旋转时的速度。

Use Gravity 启用重力:物体是否受重力影响。受重力影响的物体会进行自由落体。

Is Kinematic 运动学:如果我们只是需要让物理系统进行碰撞检测,不需要使用物理系统控制游戏对象,而是在脚本中使用 Transform 控制物体,此时我们可以勾选刚体组件中的 Is Kinematic 属性。

Interpolate 插值:物体在进行物理运动时的插值方式,默认是 None 不使用插值。

-None 不使用插值:这个选项是默认值,一般情况下此选项不需要设置。如果物体在物理系统控制下,会发生轻微的抖动,可以尝试使用另外两种选项。

-Interpolate 插值:这个选项会根据上一帧中物体的 Transform 来计算当前帧的插值。

-Extrapolate 向后推断:这个选项会推断下一帧中物体的 Transform 来计算当前帧的插值。

Collision Detection 碰撞检测:碰撞检测模式,默认是 Discrete 离散检测。

- Discrete 离散检测:对场景中的其它碰撞体使用离散碰撞检测。一般情况下我们不需要修改此属性。物理系统默认的碰撞检测是离散的,所以有时高速移动的刚体会穿透障碍物,此时我们才需要使用另外两种碰撞检测模式。

- Continuous 连续检测:刚体对其它没有刚体的碰撞器采用连续检测,防止刚体速度过快穿透障碍物。比如快速飞行的子弹碰撞静止的墙壁,此时如果采用 Dscrete 离散检测,子弹不会打在墙壁上反弹,而是会穿透墙壁。但该模式下并不能正确检测两颗高速飞行的子弹相互碰撞,因为它们两个都带有刚体,此时请使用第三种模式。要注意连续检测会影响物理性能,非必要情况请使用默认的 Discrete 离散碰撞检测。

- Continuous Dynamic 连续动态检测:会对其它刚体(碰撞检测模式不能是 Discrete 离散碰撞检测)进行连续检测,防止互相穿透。对没有刚体的碰撞器也会采用连续碰撞检测

Constraints 限制:对刚体的运动进行限制。

- Freeze Position 冻结位移:当刚体移动时,可选地忽略在某个轴上的位移。有 3 个可勾选属性 X、Y、Z,分别表示在 X 轴、Y 轴和 Z 轴方向上的位移。如果勾选了 Y 则表示,刚体受力运动后,不会在 Y 轴方向上发生位移。

- Freeze Rotation 冻结旋转:当刚体旋转时,可选地忽略绕某个轴的旋转。也有 3 个可勾选属性,分别表示绕 X 轴旋转、绕 Y 轴旋转和绕 Z 轴旋转。如果勾选了 X 则表示,刚体受力运动后,不会绕 X 轴发生旋转。

 

物理系统和 Transform
在 Unity 中,控制游戏对象有两种方式:一种是直接使用 Transform,比如 Transform.Position 或者 Transform.Rotation 等;另一种方式就是给游戏对象添加刚体,通过物理系统控制游戏对象。

需要注意的是,一旦给游戏对象添加了刚体组件,那么这个游戏对象的运动会由物理系统负责计算。此时不要在脚本中直接使用 Transform.Position 或者 Transform.Rotation 等代码直接修改游戏对象的位置和旋转,否则会导致物理计算结果不正确。也就是说,使用物理系统控制的游戏对象,不要直接修改 Transform 属性。

这是因为物理系统会根据刚体的受力情况,计算出每一帧物体的正确位置以及旋转,然后改变物体的 Transform。此时如果我们通过代码强行修改 Transform 的属性,最终会导致物体的运动不正确,或无法正确地碰撞。

使用物理系统时,如果在脚本中需要控制刚体,可以使用刚体的 void AddForce (Vector3) 方法,给刚体施力使刚体移动,或者使用刚体的 void AddTorque (Vector3) 方法,给刚体施加力矩使刚体产生旋转。如果需要让刚体相对精确地移动和旋转,可以使用刚体的 void MovePosition (Vector3)方法,移动刚体的位置,或者使用 void MoveRotation (Quaternion) 方法,旋转刚体。

如果我们只是需要让物理系统进行碰撞检测,不需要使用物理系统控制游戏对象,而是在脚本中使用 Transform 控制物体,此时我们可以勾选刚体组件中的 Is Kinematic 属性。

在使用刚体时还有一个需要额外注意的就是刚体的大小。场景中的刚体要保证它的大小,或者说缩放比例是合适的。一个鸡蛋大小的球体和一个月球大小的球体,自由下落时看起来效果是不一样的,尽管它们两个下落速度相同,但更大的物体下落时,在感官上会觉得下落速度很慢。所以如果感觉场景中物体的物理效果不对时,也要先检查一下物体大小是否正确。

 

刚体组件的属性
在脚本中,我们可以使用 GetComponent<Rigidbody> () 方法获取当前游戏对象身上的刚体组件,当然前提是它有这个组件。

 

除了这行代码以外,其它关于

物理控制的代码都建议放在 void FixedUpdate () 方法中完成

不建议在 void Update () 方法中。因为 Update 方法是帧更新,也就是场景中视图的更新,帧更新的速度并不稳定并且频率比 FixedUpdate 方法低。将物理控制代码放在 FixedUpdate 中能够保证物理效果的及时处理,这样等到每次帧更新时,场景中的刚体都会是正确的位置和旋转。

获取到刚体组件后,我们可以通过代码给它施加力、力矩,或者改变它的位置、旋转,或者直接修改它的速度、角速度等。不过首先我们看看刚体属性面板中的这些属性,如何通过代码进行修改。

 

其中 constraints 属性比较特殊,它是一个枚举类型,表示刚体运动时限制的位移或旋转轴。它的值可以通过按位或运算进行状态叠加,比如我们只需要限制 PositionX 和 PositionZ,其它的都不限制,那么可以这样写,两个枚举值中间进行按位或运算(|)。

 

 

位置和旋转
刚体最常用的功能就是以物理的方式控制游戏对象的运动,所以 Rigidbody 中有一些与位置、旋转相关的属性和方法。

 

其中用来控制刚体位置的有一个 position 属性和一个 MovePosition 方法,用来控制刚体旋转的是 rotation 属性和 MoveRotation 方法。

使用 transform.position 或者 transform.rotation 也可以控制位移和旋转, rigidbody.position 和 rigidbody.rotation 使用方式和 Transform 没太大区别,不一样的是 Rigidbody 这两个属性修改后,会直接在下一次物理循环中更新 Transform,响应速度更快一些,能保证物理效果的正确性。

而 rigidbody.MovePosition () 方法和 rigidbody.MoveRotation () 方法更适合在 FixedUpdate 中连续移动或旋转物体。这两个方法会考虑刚体的插值设置,在连续运动时减少抖动。如不需要连续运动想直接瞬移物体,就使用 rigidbody.position 和 rigidbody.rotation 属性。连续移动并旋转刚体的例子如下。

 

 

力和速度
除了直接修改刚体的位置和旋转以外,我们也可以给刚体施力让它产生运动。力是矢量,所以一般用 Vector3 表示一个力的方向和大小。在脚本中有四种方法给刚体施力。

最常用的是 AddForce 方法。它直接给刚体的质心(质量中心,默认是几何中心)施加力,并且这个力会以世界坐标系进行处理。如果我们参数填写的是 Vector3.forward * 10f,那么这个力的方向就是世界坐标系中 Z 轴正方向,大小是 10。

如果我们需要让这个力以刚体本地坐标系进行处理,可以使用 AddRelativeForce 方法。它的使用方式和 AddForce 方法完全一样,唯一不同的就是,这个方法会把参数中设置的力,以刚体的本地坐标系进行处理,而不是世界坐标。如果我们使用这个方法,参数填写的是 Vector3.forward * 10f,那么这个力的方向就是刚体本地坐标系中 Z 轴正方向,大小是 10。

 

我们也可以不把力施加到刚体的质心,使用 AddForceAtPosition 方法可以在指定的位置向刚体施力。此时第一个参数表示力的大小和方向,第二个参数就是施力的位置,这两个参数都是以世界坐标系进行处理的。除此以外这个方法使用起来和 AddForce 方法一样。

如果我们要模拟爆炸时周围物体都被炸飞的效果,可以使用 AddExplosionForce 方法添加爆炸力。方法至少需要三个参数,分别表示力的大小(float)、爆炸中心位置(世界坐标系 Vector3)和爆炸的有效半径(float)。添加爆炸力的方法需要由刚体实例来调用,爆炸发生时需要让哪些刚体炸飞,那么这些刚体都需要在同一时间调用这个方法,传递相同的参数。

爆炸发生时,会以爆炸中心位置和爆炸有效半径限定一个球形范围,若刚体在此范围内则会受到一个力的影响,力的方向是从爆炸中心指向刚体质心的向量,力的大小决定于刚体和爆炸中心的距离。

使用爆炸力时,还有可选的第 4 个参数,upwardsModifier。它是 float 类型,默认值为 0。表示在爆炸发生时计算受力方向,会将爆炸中心点位置的 Y 值,往负方向移动多远。比如此参数设置为 2 时,爆炸看起来的位置就在实际位置向下 2 单位,最终的爆炸效果会把周围刚体都向上炸飞。这个参数只会影响爆炸方向,不会影响力的大小。

以上四种添加力的方法都还有一个可选参数,ForceMode 枚举类型参数,施力模式。默认值都是 ForceMode.Force 表示以力的方式施加。另外还有 3 个枚举值,我们可以根据需要设置施力模式。

加速度模式 ForceMode.Acceleration。在此模式下,Vector3 类型的参数不再表示力的方向和大小,而是表示加速度的方向和大小(加速度也是矢量)。

 

此时会直接给刚体添加一个加速度。设置了加速度之后,假设当前帧刚体速度是 v1,那么等到下次物理帧刷新时刚体的速度就是 v2,而 Δt 则是 Time.deltaTime。

冲量模式 ForceMode.Impulse。在此模式下,Vector3 类型的参数也不再表示一个力,而是一个冲量。

 

设置冲量可以给物体一个瞬时冲击力,而且要注意冲量的作用效果和物体的质量相关,给不同质量的物体设置相同的冲量,作用效果是不一样的。

速度模式 ForceMode.VelocityChange。在此模式下,Vector3 类型的参数直接表示一个速度。

 

以上三种模式并不常用到,但有些特殊的物理相关的游戏中也许有用。直接给刚体添加冲量或加速度能更容易实现某些物理效果,而不需要我们手动计算力或者速度大小。

力矩也是物理学中的概念,用来描述物体受力旋转的矢量。力矩的方向垂直于旋转平面。我们可以直接给刚体添加力矩使之旋转。

 

使用 AddTorque 方法或者 AddRelativeTorque 方法,它们两个的区别在于坐标系不同。

我们也可以使用更直观的方式,直接控制刚体的速度或者角速度,角速度的方向也是垂直于旋转平面的。但不建议直接对速度和角速度进行修改,因为这有可能导致物理效果不真实。在某些特殊情况下,我们需要让速度或角速度立即生效,此时可以直接修改速度属性。

 

一般情况下,我们使用 AddForce 等相关方法施加力或者控制速度,使用 AddTorque 方法控制旋转或者角速度,用 MovePosition 和 MoveRotation 方法连续精确地控制位移和旋转。除了这些常用的属性和方法,还有一些不常用的,比如设置质心的位置、设置最大角速度(默认值是 7)、刚体的休眠和唤醒(默认系统自动完成)、获取刚体上某一点的瞬时速度等。
---------------------
作者:波波课堂小波
来源:CSDN
原文:https://blog.csdn.net/songxiaobor/article/details/80406949
版权声明:本文为博主原创文章,转载请附上博文链接!

posted @ 2019-01-30 16:29  EkkoMay  阅读(1160)  评论(0编辑  收藏  举报