【Unity】物理引擎、生命周期物理阶段、刚体、碰撞体、触发器、物理材质


物理引擎

什么是物理引擎?

Unity 可帮助您在项目中模拟物理系统,以确保对象正确加速并对碰撞、重力和各种其他力做出响应。

从开发方式的角度出发,Unity提供了面向对象面向数据两种方式。

面向数据指的是Unity提供的一种新的技术栈DOTS(Data Oriented Tech Stack),而面向数据的物理引擎需要使用专用的DOTS物理包。由于此项技术栈尚不成熟,可用性很低,所以暂不考虑。

面向对象就是现在常用的Unity内置的物理引擎,其中又包括2D和3D两种:

  • 内置3D物理系统(集成 NVIDIA PhysX 引擎)
  • 内置2D物理系统(集成Box2D引擎)

物理和时间

物理引擎通常是在时间按固定值前进的假设下运行的,Unity的两个物理引擎也都以这种方式运行。每个迭代都称为时间步长。物理引擎将只使用非常特定的时间值来处理每个时间步长,这与渲染上所花的时间无关。该时间步长在Unity中称为Fixed Update Timestep,它的值默认为20毫秒(每秒更新50次)。

注意:由于体系结构(浮点值表示方式)的不同以及客户端之间的延迟,如果物理引擎使用可变的时间步长,很难在两台不同的计算机之间产生一致的碰撞和力的结果。这样无理引擎往往会在多人的客户端之间或在录制的重播期间生成非常不一致的结果。

物理阶段生命周期

下图为官方提供的物理阶段生命周期图:
在这里插入图片描述
由上图可见物理阶段是在初始化阶段之后、输入事件阶段之前的一个阶段,通常这个阶段用于处理物理运动计算。

下图为我个人对这个阶段生命周期图的解读:
在这里插入图片描述

FixedUpdate(固定更新)

FixedUpdate基于一个可靠的定时器被调用,独立于帧率之外。如果固定的时间步长小于实际的帧更新时间,那么每帧的物理循环可能会发生不止一次。处理物体的物理属性(Rigidbody、Force、Collider)或者输入事件时,需要用FixedUpdate代替Update,以使物体的物理表现更平滑。实际上,FixedUpdate并不是真的按照现实时间间隔执行的,而是按照Timer时间间隔执行的,但Timer并不是真正意义上的现实时间,它的作用是在运行环境下创造一个与现实时间高度相近的变量来实现物理帧的逻辑稳定。因为FixedUpdate的这个特质,强烈建议在此环节只做物理相关的处理,不要把其他类型(如网络帧同步)的处理也放入此步骤。默认频率大概为0.02s,该频率可手动修改。

在这期间的操作

固定更新结束后,系统内部会进行一系列的操作,最重要的莫过于Unity的内部物理更新,这个是真正的物理更新操作执行。
具体执行步骤大概如下:
在这里插入图片描述

OnTriggerXXX(触发)

触发器被触发时调用。

OnCollisionXXX(碰撞)

产生碰撞事件时调用。

yield WaitForFixedUpdate(协程:物理帧结束)

当物理帧执行完毕后会跳转到此协程,协程的调用跟方法是不同的,可以理解为在一段代码中设置一个卡点,当程序执行到这个卡点所匹配的时机时卡点后面的代码才会继续执行。

public class Test : MonoBehaviour
{
    
    
    void Awake()
    {
    
    
        Debug.Log("0");
        StartCoroutine(TestCoroutine());
        Debug.Log("2");
    }

    void FixedUpdate()
    {
    
    
        Debug.Log("3 - FixedUpdate");
    }

    IEnumerator TestCoroutine()
    {
    
    
        Debug.Log("1");
        yield return new WaitForFixedUpdate();
        Debug.Log("4");                      // 当物理帧结束(触发WaitForFixedUpdate)后才会执行这条语句。 
    }
}

执行结果:01234。

物理阶段总结

该阶段的发生与渲染无关,其特性决定了其处理物理事件的功能,使物理展示效果更为平滑,另外固定更新的频率并非真的是固定的,实际的执行会根据CPU轮转时间片产生偏移,但这个偏移基本可以忽略不记。

关于Unity完整的生命周期,官方曾给出两个版本的示意图,如果需要深入了解,可以参考我的另一篇文章:【Unity】Unity 生命周期

刚体(Rigidbody)

刚体可使游戏对象受物理引擎控制,在受到外力时产生真实世界的运动效果。

  • Mass(质量):默认为1,大部分在0~1之间,不同质量的物体在不考虑阻力的情况下,下落速度是一致的,但两个刚体相互碰撞后的反弹效果会根据质量有所改变。
  • Drag(阻力):表示物体移动时的空气阻力,0表示没有阻力,极大时可使物体停止运动。通常砖头阻力为0.001,而羽毛为10。
  • AngularDrag(角阻力):当受到扭力旋转时物体受到的空气阻力,极大时可使物体停止旋转。
  • Use Gravity(使用重力):使物体受地面引力的影响。
  • Is kinematic(是否动力学):如果激活,则该物体不受物理引擎控制,而只能通过变换组件来操作(比如台球杆)。
  • Interpolate(插值):用于缓解刚体运动时的抖动。
    • Interpolate(内插值):基于上一帧的变换来平滑本帧变换。
    • Extrapolate(外插值):基于下一帧的预估变换来平滑本帧变换。
  • Constraints(约束):用于约束刚体的运动。
    • Freeze Position(冻结位置):选中后刚体不会在对应的轴上移动。
    • Freeze Rotation(冻结旋转):选中后刚体不会在对应的轴上旋转。

碰撞检测(CollisionDetection)

  • CollisionDetection(碰撞检测):用于控制避免高速运动的游戏对象穿过其他的对象而未发生碰撞,有三项可供选择:
    • Discrete(离散碰撞器):该模式与场景中其他的所有碰撞体进行碰撞检测。该项为默认值。
    • Continuous(连续碰撞检测):该模式用于检测与动态碰撞体(带有Rididbody)的碰撞,使用连续碰撞检测模式来检测与网格碰撞体(不带ridigbody)碰撞。Rigidbody设置为连续动态碰撞检测模式将使用连续碰撞来检测。其他刚体会采用离散碰撞模式。此模式适用于那些需要与采用连续动态检测的对象相碰撞的对象。这对物理性能会有很大影响,如果不需要对快速运动对象进行碰撞检测,就使用离散碰撞检测模式。
    • Continuous Dynamic(连续动态碰撞检测模式):该模式用于检测与采用连续碰撞撞模式或连续
      动态碰撞模式对象的碰撞,也可用于检测没有rigidbody的静态网格碰撞体。对于与之碰撞的其他对象可采用离散碰撞检测。动态连续碰撞检测模式也可用于检测快速运动的游戏物体。

关于CollisionDetection的选择,可以按照以下思路进行:

  1. 可能发生碰撞的高速对象,选择连续动态模式;
  2. 可能与高速对象碰撞的对象,选择连续模式;
  3. 其他的选择离散模式。

Continuous和Continuous Dynamic就是为了用来保证void OnCollisionEnter(Collision hit)函数能够检测到高速运动物体,而至于一个为Continuous Dynamic的刚体和一个Discrete的刚体碰撞,前者会使用使用Continous(连续)碰撞,后者离散碰撞。

刚体碰撞特性

  • 特性1:必须双方都有碰撞体才会产生碰撞效果。
  • 特性2:刚体休眠:只有运动中的刚体才会主动检测碰撞。

刚体碰撞条件

主动a 被动b 效果
刚体 刚体 穿过
刚体 碰撞体 穿过
刚体 刚体+碰撞体 穿过
碰撞体 刚体 穿过
碰撞体 碰撞体 穿过
碰撞体 刚体+碰撞体 b动
刚体+碰撞体 刚体 穿过
刚体+碰撞体 碰撞体 a动
刚体+碰撞体 刚体+碰撞体 a、b都动

上表为不同情况下a物体主动碰撞b物体产生的效果。

碰撞体(Collider)

碰撞体使物体拥有实质性的边界,刚体在拥有碰撞体后才能实现碰撞效果,而碰撞体往往可以独立存在于物体上,从而实现更复杂的动态效果。

Unity 3D 为游戏开发者提供了多种类型的碰撞体资源,如下图所示。当游戏对象中的 Rigidbody 碰撞体组件被添加后,其属性面板中会显示相应的属性设置选项,每种碰撞体的资源类型稍有不同。
在这里插入图片描述

  • Box Collider(方块碰撞器):方块形状的碰撞器,也是最常用的碰撞器,可以指定触发器(Is Trigger)、材质(Material)、中心(Center)、大小(Size)。通常用于墙壁、门、平台、箱柜等物体。
    在这里插入图片描述

  • Sphere Collider(球状碰撞器):球状碰撞器,可以指定触发器(Is Trigger)、材质(Material)、中心(Center)、半径(Radius)。通常用于球状物体。
    在这里插入图片描述

  • Capsule Collider(胶囊碰撞器):胶囊状的碰撞器,可以指定触发器(Is Trigger)、材质(Material)、中心(Center)、半径(Radius)、高度(Height)。通常用于角色碰撞器。
    在这里插入图片描述

  • Mesh Collider(网格碰撞器):根据 Mesh 形状产生碰撞体,比起 Box Collider、Sphere Collider 和 Capsule Collider,Mesh Collider 更加精确,但会占用更多的系统资源。

    • 凸起(Convex):勾选该项,则Mesh Collider将会与其他的Mesh Collider发生碰撞。
    • 触发器(Is Trigger):当凸起被选中后,可以指定触发器。
    • 材质(Material):物体材质。
    • 网格(Mesh):获取游戏对象的网格并将其作为碰撞体。
      在这里插入图片描述
  • Wheel Collider(车轮碰撞器):车轮碰撞器是一种针对地面车辆的特殊碰撞体,自带碰撞侦测、轮胎物理现象和轮胎模型,专门用于处理轮胎。

    • Mass(质量):用于设置 Wheel Collider 的质量。
    • Radius(半径):用于设置碰撞体的半径大小。
    • Wheel Damping Rate(车轮减震率):用于设置碰撞体的减震率。
    • Suspension Distance(悬挂距离):该项用于设置碰撞体悬挂的最大伸长距离,按照局部坐标来计算, 悬挂总是通过其局部坐标的 Y 轴延伸向下。
    • Center(中心):用于设置碰撞体在对象局部坐标的中心。
    • Suspension Spring(悬挂弹簧):用于设置碰撞体通过添加弹簧和阻尼外力使得悬挂达到目标位置。
    • Forward Friction(向前摩擦力):当轮胎向前滚动时的摩擦力属性。
    • Sideways Friction(侧向摩擦力):当轮胎侧向滚动时的摩擦力属性。
    • Wheel Damping Rate(车轮减震率):用于设置碰撞体的减震率。
      在这里插入图片描述
  • Terrain Collider(地形碰撞器):地形碰撞器根据指定的材质(Material)及地形数据(Terrain Data)来实现碰撞效果。
    在这里插入图片描述

物理材质

物理材质用于调整碰撞对象的摩擦力和反弹效果。

Unity 3D 提供了一些物理材质资源,通过资源添加方法可以添加到当前项目中。

标准资源包提供了 5 种物理材质:

  • 弹性材质(Bouncy)
  • 冰材质(Ice)
  • 金属材质(Metal)
  • 橡胶材质(Rubber)
  • 木头材质(Wood)

在这里插入图片描述

属性详解:
在这里插入图片描述

  • DynamicFriction(动态摩擦力):移动时的摩擦力[0 ~ infinity],通常为0 ~ 1之间。
  • StaticFriction(静态摩擦力):静止时的摩擦力[0 ~ infinity],通常为0 ~ 1之间。
  • Bounciness(弹力):值为 0 时不发生反弹,值为 1 时反弹不损耗任何能量。
  • Friction Combine(摩擦力合并方式):定义两个碰撞物体的摩擦力如何相互作用,即将两个物体的摩擦力组合的方式。
    • Average:取平均值。
    • Minimum:取最小值。
    • Multiply:取叠加值。
    • Maximum:取最大值。
  • Bounce Combine (反弹合并方式):定义两个相互碰撞的物体的相互反弹模式,即将两个物体的弹力力组合的方式。
    • Average:取平均值。
    • Minimum:取最小值。
    • Multiply:取叠加值。
    • Maximum:取最大值。

注意:3D物体由网格和纹理组成,所以如果要更换物体就要改变对象的MeshFilter.Mesh和MeshRender.Material.maintexture两个属性。

触发器(Trigger)

如果既想要检测到物体的接触,又不想让碰撞检测影响物体移动,或者要检测一个物体是否经过空间中的某个区域,这时就可以用到触发器。触发器是碰撞器的一个属性,碰撞体是触发器的载体。

例如,碰撞体适合模拟汽车被撞飞、皮球掉在地上又弹起的效果,而触发器适合模拟人站在靠近门的位置时门自动打开的效果。

触发器用于在游戏中触发事件。比如在游戏中的剧情里,玩家通过某个任务道具召唤了任务相关的 NPC 或者可击杀的怪物,就可以用触发器来实现。

在 Unity 3D 中,检测碰撞发生的方式有两种:

  • 利用碰撞体(Collider)
  • 利用触发器(Trigger)

勾选了Is Trigger的碰撞体,当满足以下条件后会调用OnTrigger方法。

  1. 两者具有碰撞组件;
  2. 其中之一带有刚体组件;
  3. 其中之一勾选了Is Trigger。

当绑定了碰撞体的游戏对象进入触发器区域时,会运行触发器对象上的 OnTriggerEnter 函数,同时需要在检视面板中的碰撞体组件中勾选 IsTrigger 复选框。

触发信息检测使用以下 3 个函数:

MonoBehaviour.OnTriggerEnter(Collider collider),当进入触发器时触发。
MonoBehaviour.OnTriggerExit(Collider collider),当退出触发器时触发。
MonoBehaviour.OnTriggerStay(Collider collider),当逗留在触发器中时触发。

OnCollision调用

在这里插入图片描述

OnTrigger调用

在这里插入图片描述

总结

刚体为系统提供了物理作用,但其基于帧的计算方式使其容易出现检测失败的情况(运动过快时会出现),所以一般在为了简单实现物理效果时使用刚体。物理引擎消耗较大,所以一般都是游戏主要对象设为刚体,而场景和道具使用碰撞体。碰撞器面数越少性能越好。

另外,运动较快时可以使用Physics来处理,物理引擎检测频率与FixUpdate一致。


本文碰撞检测部分内容参考文章《论Collision Detection的作用》,感谢分享。

更多内容请查看总目录【Unity】Unity学习笔记目录整理

猜你喜欢

转载自blog.csdn.net/xiaoyaoACi/article/details/126605207