参考
Cocos 3.x 3D物理系统 射线检测
[专栏精选]Unity刚体详解
一、RigidBody
https://docs.unity.cn/cn/2019.4/Manual/class-Rigidbody.html
Cocos 3.x中的RigidBody概念上与Unity是一致的,但是稍有区别:
1.刚体类型
动态 Dynamic
- 默认类型
- 受重力和附加力的影响,进而改变速度。
- 性能消耗最高的类型。
- 可通过 MovePosition、MoveRotation 来移动。
- 可通过 velocity 来改变速度。
- 用来模拟移动的物体。
运动学 Kinematic
- 不受力的影响。
- 性能消耗比 Dynamic 少,比 Static 多。
- 可通过 MovePosition MoveRotation 来移动。
- 可通过 velocity 来改变速度。
- 用来模拟大多数时候不动的物体,极少数时候需要移动。比如门。
静态 Static
- 不受力的影响,物理系统不会对该物体进行计算。
- 性能消耗最低的类型。
- 不可通过 MovePosition MoveRotation 来移动。
- 不可通过 velocity 来改变速度。
- 用来模拟从不移动的物体。
cocos通过type选择刚体类型,unity中是这样的:
参考https://docs.unity.cn/cn/2019.4/Manual/CollidersOverview.html
- 可将碰撞体添加到没有刚体组件的游戏对象,从而创建场景的地板、墙壁和其他静止元素。这些被称为静态碰撞体。
- 相反,具有刚体的游戏对象上的碰撞体称为动态碰撞体。静态碰撞体可与动态碰撞体相互作用,但由于没有刚体,因此不会通过移动来响应碰撞。
参考https://docs.unity.cn/cn/2019.4/Manual/RigidbodiesOverview.html
刚体 (Rigidbody)是实现游戏对象的物理行为的主要组件。连接刚体后,对象将立即响应重力。如果还添加了一个或多个碰撞体组件,则游戏对象会因发生碰撞而移动。
由于刚体组件会接管附加到的游戏对象的运动,因此不应试图借助脚本通过更改变换属性(如位置和旋转)来移动游戏对象。相反,应该施加力来推动游戏对象并让物理引擎计算结果。
在某些情况下,可能希望游戏对象具有刚体,并让刚体的运动摆脱物理引擎的控制。例如,可能希望直接从脚本代码控制角色,但仍允许触发器检测角色。脚本产生的这种非物理运动称为运动学运动。刚体组件有一个名为 Is Kinematic 的属性,该属性可以让刚体摆脱物理引擎的控制,并允许通过脚本以运动学方式来移动刚体。可以通过脚本来更改 Is Kinematic 的值,从而为某个对象开启和关闭物理引擎,但这会产生性能开销,应谨慎使用。
可将碰撞体添加到没有刚体组件的游戏对象,从而创建场景的地板、墙壁和其他静止元素。这些被称为静态碰撞体。相反,具有刚体的游戏对象上的碰撞体称为动态碰撞体。静态碰撞体可与动态碰撞体相互作用,但由于没有刚体,因此不会通过移动来响应碰撞。
场景里有一些物体是基本不会动的,比如说一栋大楼,一块大石头等等,他们只需要阻挡别的物体别穿过他们就行了,所以它们只需要添加Collider就行。因为添加了Rigidbody组件后会带来很多的物理计算,所以只需要给会移动的物体添加Rigidbody就行
2.属性
- Mass 对象的质量(默认为千克)。
- Drag 根据力移动对象时影响对象的空气阻力大小。0 表示没有空气阻力,无穷大使对象立即停止移动。
- Angular Drag 根据扭矩旋转对象时影响对象的空气阻力大小。0 表示没有空气阻力。请注意,如果直接将对象的 Angular Drag 属性设置为无穷大,无法使对象停止旋转。
- Use Gravity 如果启用此属性,则对象将受重力影响。
- Is Kinematic 如果启用此选项,则对象将不会被物理引擎驱动,只能通过变换 (Transform)对其进行操作。
Drag和Angular Drag与Cocos 3.x中的Linear Damping 、Angular Damping 是一样的。
3.Interpolate 如果发现刚体移动有卡顿,可以尝试选择此选项
- None 不应用插值。
- Interpolate 根据前一帧的变换来平滑变换。
- Extrapolate 根据下一帧的估计变换来平滑变换。
默认情况下,插值是关闭的。通常对玩家的角色使用刚体插值。 物理以离散时间步长运行,而图形以可变帧率渲染。 由于物理和图形并不完全同步,这可能导致对象出现视觉抖动。 这种效果很细微,但通常能够在玩家角色上看到,特别是当摄像机跟随主角时。 建议对主角打开插值,对于其他所有内容,则禁用它。
4.Collision Detection 用于防止快速移动的对象穿过其他对象而不检测碰撞。
连续碰撞检测是一种阻止快速移动的碰撞体相互穿过的功能。使用正常 (Discrete) 碰撞检测时,如果对象在一个帧中位于某个碰撞体的一侧,而在下一帧中已经穿过了碰撞体,便属于彼此穿过的情况。要解决此问题,可在快速移动对象的刚体上启用连续碰撞检测。
将碰撞检测模式设置为 Continuous 可防止刚体穿过任何静态(即非刚体)网格碰撞体。设置为 Continuous Dynamic 也会防止刚体穿过任何其他支持的刚体(即,碰撞检测模式设置为 Continuous 或 Continuous Dynamic 的刚体)。 盒型碰撞体、球形碰撞体和胶囊碰撞体支持连续碰撞检测。请注意,连续碰撞检测的目的是作为一种安全机制,可在对象会相互穿过的情况下捕获碰撞,但不会提供精确物理碰撞的结果;所以如果遇到快速移动对象的问题,仍然可以考虑在 TimeManager 检视面板中降低固定时间步长值以使模拟更准确。
- Discrete 对场景中的所有其他碰撞体使用离散碰撞检测。其他碰撞体在测试碰撞时会使用离散碰撞检测。用于正常碰撞(这是默认值)。
- Continuous 对动态碰撞体(具有刚体)使用离散碰撞检测,并对静态碰撞体(没有刚体)使用基于扫掠的连续碰撞检测。设置为连续动态 (Continuous Dynamic) 的刚体将在测试与该刚体的碰撞时使用连续碰撞检测。其他刚体将使用离散碰撞检测。用于连续动态 (Continuous Dynamic) 检测需要碰撞的对象。(此属性对物理性能有很大影响,如果没有快速对象的碰撞问题,请将其保留为 Discrete 设置)
- Continuous Dynamic 对设置为连续 (Continuous) 和连续动态 (Continuous Dynamic) 碰撞的游戏对象使用基于扫掠的连续碰撞检测。还将对静态碰撞体(没有刚体)使用连续碰撞检测。对于所有其他碰撞体,使用离散碰撞检测。用于快速移动的对象。
- Continuous Speculative 对刚体和碰撞体使用推测性连续碰
官方文档有点难懂,可以参考Unity-Rigidbody【刚体】组件-Collision Detection碰撞检测模式
有时候开发游戏,对于高速运动的物体(比如:子弹/大炮,或者很高处自由落体的物体),即使再三确认加了Rigidbody组件,检查了碰撞检测的代码完全没有错误,但仍然会出现 物体直接穿过另一个物体 的问题, 则说明这个碰撞检测的程序对于高速运动的物体而言会出错。
Unity物体的Rigidbody组件提供了一个Collision Detection的属性,该属性用于更改物体的碰撞检测模式————一共有三种模式可以选择(选择你想要的模式,底层碰撞检测的算法均有差别)
- Discrete适用于大部分刚体
- Continuous适用于将有可能会被高速移动物体撞上的物体
- Continuous Dynamic适用于高速移动的物体
另外参考官方文档:连续碰撞检测 (CCD)
5.Constraints 对刚体运动的限制:
- Freeze Position 有选择地停止刚体沿世界 X、Y 和 Z 轴的移动。
- Freeze Rotation 有选择地停止刚体围绕局部 X、Y 和 Z 轴旋转。
可以对物体在X、Y、Z三个轴上的位置/旋转进行锁定,即使受到相应的力也不会改变,但可以通过脚本来修改。否则物体在上升过程中会发生飘动(不仅y轴变化,X,Z也在变,不想这种现象,就把X,Z锁定)
6.官方文档提示
https://docs.unity.cn/cn/2019.4/Manual/class-Rigidbody.html
- 两个刚体的相对质量决定了它们相互碰撞时的反应情况。
- 使一个刚体具有比另一个刚体更大的质量并不会使其在自由落体时降落得更快。要实现这一目的,请使用 Drag。
- 较低的 Drag 值会让对象看起来较重。较高的值会使其看起来较轻。Drag 的典型值为 .001(实心金属块)到 10(羽毛)之间。
- 如果要直接操作对象的变换组件,但仍希望获得物理效果,请附加刚体并将其设为 Kinematic。
- 如果要通过变换组件移动游戏对象,但希望接收 Collision/Trigger 消息,必须将刚体附加到正在移动的对象。
- 如果直接将对象的 Angular Drag 属性设置为无穷大,无法使对象停止旋转。
二、碰撞体
1. 盒型碰撞体
立方体的原始形状
2. 球形碰撞体
球体的原始形状
3. 胶囊碰撞体
胶囊体的原始形状
4. 网格碰撞体
根据对象网格创建碰撞体,不能与另一个网格碰撞体碰撞。典型的解决方案是对所有移动的对象使用原始碰撞体,而对静态背景对象使用网格碰撞体。
5. 车轮碰撞体
专门用于创建汽车或其他移动车辆,可参考官方教程:车轮碰撞体教程
https://docs.unity.cn/cn/2019.4/ScriptReference/WheelCollider.html
6. 地形碰撞体
处理与 Unity 地形系统的碰撞
三、碰撞控制
1.碰撞矩阵
参考
Unity的选择碰撞--碰撞矩阵(Collision Matrix)
用代码也可以控制:
https://docs.unity3d.com/cn/2019.4/ScriptReference/Physics.IgnoreLayerCollision.html
使碰撞检测系统忽略 layer1 中的任何碰撞体与 layer2 中的任何碰撞体之间的所有碰撞。
注意,IgnoreLayerCollision 将重置受影响的碰撞体的触发器状态,因此,您可能会收到调用该函数导致的 OnTriggerExit 和 OnTriggerEnter 消息。
您可以在 Physics Inspector 中为任何层组合设置项目默认值。
using UnityEngine;
public class Example : MonoBehaviour
{
//Set the speed number in the Inspector window
public float m_Speed;
Rigidbody m_Rigidbody;
void Start()
{
//Fetch the Rigidbody component from the GameObject
m_Rigidbody = GetComponent();
//Ignore the collisions between layer 0 (default) and layer 8 (custom layer you set in Inspector window)
Physics.IgnoreLayerCollision(0, 8);
}
void Update()
{
//Press right to move the GameObject to the right. Make sure you set the speed high in the Inspector window.
if (Input.GetKey(KeyCode.RightArrow))
{
m_Rigidbody.AddForce(Vector3.right * m_Speed);
}
//Press the left arrow key to move the GameObject to the left
if (Input.GetKey(KeyCode.LeftArrow))
{
m_Rigidbody.AddForce(Vector3.left * m_Speed);
}
}
//Detect when there is a collision
void OnCollisionStay(Collision collide)
{
//Output the name of the GameObject you collide with
Debug.Log("I hit the GameObject : " + collide.gameObject.name);
}
}
2.IgnoreCollision
https://docs.unity3d.com/cn/2019.4/ScriptReference/Physics.IgnoreCollision.html
使碰撞检测系统忽略 collider1 与 collider2 之间的所有碰撞。
这在某些情况下很有用。例如,防止飞弹与发射飞弹的对象发生碰撞。
注意,IgnoreCollision 不是持久性的。这意味着保存场景时忽略碰撞状态将不会存储在编辑器中。
如果 ignore 为 false,可能发生碰撞。将 ignore 设置为 true,可忽略碰撞。
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
public Transform bulletPrefab;
void Start()
{
Transform bullet = Instantiate(bulletPrefab) as Transform;
Physics.IgnoreCollision(bullet.GetComponent(), GetComponent());
}
}
四、事件
当两个对象碰撞时,可能会发生许多不同的脚本事件,具体取决于碰撞对象的刚体配置。以下图表详细列出了根据附加到对象的组件来调用的事件函数。某些组合仅会使两个对象之中的一个对象受到碰撞的影响,但一般规则是物理特性不会应用于没有附加刚体组件的对象。
发生碰撞检测并在碰撞后发送消息
静态碰撞体 | 刚体碰撞体 | 运动刚体碰撞体 | 静态触发碰撞体 | 刚体触发碰撞体 | 运动刚体触发碰撞体 | |
---|---|---|---|---|---|---|
静态碰撞体 | 是 | |||||
刚体碰撞体 | 是 | 是 | 是 | |||
运动刚体碰撞体 | 是 | |||||
静态触发碰撞体 | ||||||
刚体触发碰撞体 | ||||||
运动刚体触发碰撞体 |
碰撞后发送触发器消息
静态碰撞体 | 刚体碰撞体 | 运动刚体碰撞体 | 静态触发碰撞体 | 刚体触发碰撞体 | 运动刚体触发碰撞体 | |
---|---|---|---|---|---|---|
静态碰撞体 | 是 | 是 | ||||
刚体碰撞体 | 是 | 是 | 是 | |||
运动刚体碰撞体 | 是 | 是 | 是 | |||
静态触发碰撞体 | 是 | 是 | 是 | 是 | ||
刚体触发碰撞体 | 是 | 是 | 是 | 是 | 是 | 是 |
运动刚体触发碰撞体 | 是 | 是 | 是 | 是 | 是 | 是 |
1.碰撞事件
using UnityEngine;
using System.Collections;
public class PhysicalTest : MonoBehaviour {
// Use this for initialization
void Start () {}
void OnCollisionEnter(Collision collisionInfo)
{
Debug.Log ("enter");
Debug.Log (collisionInfo.gameObject.name);
Destroy (collisionInfo.gameObject);
}
void OnCollisionStay(Collision collisionInfo)
{
Debug.Log ("stay");
}
void OnCollisionExit(Collision collisionInfo)
{
Debug.Log ("Exit");
}
}
当两个物体发生碰撞的时候,可以看到函数进行了调用。其中OnCollisionEnter函数在刚体进入的时候,调用一次,只要两个物体接触到一起,会一直调用OnCollisionStay函数直到静止。当两个刚体离开的时候,调用OnCollisionExit函数。可以通过参数获得与之发生碰撞的物体,并且进行对应的处理。
2.触发事件
触发器在生活有很多例子,假设进入到一些门口时,当我们踏入到一定范围,门会自动打开,这其实就是触发器在现实生活中的例子,游戏中依然也是,例如,人物走回家的时候会自动的恢复血量,这其实就是在使用触发器
将BoxCollider上的IsTrigger进行勾选,使其成为一个触发器,并且修改脚本如下:
void OnTriggerEnter(Collider colliderInfo)
{
Debug.Log ("Trigger"+
colliderInfo.GetComponent().name);
}
void OnTriggerStay(Collider colliderInfo)
{
//Debug.Log ("Trigger"+ colliderInfo.GetComponent().name);
}
void OnTriggerExit(Collider colliderInfo)
{
Debug.Log ("Trigger");
}
可以看到,勾选之后,虽然没有发生碰撞反应,但是能够监测到物体的碰撞。