最近做了一个物理小游戏 - “平衡球过关”,游戏使用了大量的3D物理方面的技术,今天抽出了时间总结了下Unity中3D 物理系统的知识,并在文章中穿插列举了项目中的使用。
作者微信公众号
二、物理技术的主要组件1)刚体概述刚体 (Rigidbody) 是实现游戏对象的物理行为的主要组件。连接刚体后,对象将立即响应重力。如果还添加了一个或多个__碰撞体__组件,则游戏对象会因发生碰撞而移动。
由于刚体组件会接管附加到的游戏对象的运动,因此不应试图借助脚本通过更改变换属性(如位置和旋转)来移动游戏对象。相反,应该施加__力__来推动游戏对象并让物理引擎计算结果。
在某些情况下,可能希望游戏对象具有刚体,并让刚体的运动摆脱物理引擎的控制。例如,可能希望直接从脚本代码控制角色,但仍允许触发器检测角色(请参阅碰撞体主题下的_触发器_)。脚本产生的这种非物理运动称为_运动学_运动。刚体组件有一个名为 Is Kinematic 的属性,该属性可以让刚体摆脱物理引擎的控制,并允许通过脚本以运动学方式来移动刚体。可以通过脚本来更改 Is Kinematic 的值,从而为某个对象开启和关闭物理引擎,但这会产生性能开销,应谨慎使用。
有关这些组件的设置和脚本选项的更多详细信息,请参阅刚体和 2D 刚体参考页面。
睡眠当刚体移动速度低于规定的最小线性速度或转速时,物理引擎会认为刚体已经停止。发生这种情况时,游戏对象在受到碰撞或力之前不会再次移动,因此将其设置为“睡眠”模式。这种优化意味着,在刚体下一次被“唤醒”(即再次进入运动状态)之前,不会花费处理器时间来更新刚体。
在大多数情况下,刚体组件的睡眠和唤醒是透明发生的。但是,如果通过修改__变换__位置将静态碰撞体(即,没有刚体的碰撞体)移入游戏对象或远离游戏对象,则可能无法唤醒游戏对象。这种情况下可能会导致问题,例如,已经从刚体游戏对象下面移走地板时,刚体游戏对象会悬在空中。在这种情况下,可以使用 WakeUp 函数显式唤醒游戏对象。有关睡眠的更多信息,请参阅刚体和 2D 刚体组件页面。
2)碰撞体碰撞体组件可定义用于物理碰撞的游戏对象的形状。碰撞体是不可见的,其形状不需要与游戏对象的网格完全相同。网格的粗略近似方法通常更有效,在游戏运行过程中难以察觉。
最简单(并且也是处理器开销最低)的碰撞体是__原始__碰撞体类型。在 3D 中,这些碰撞体为盒型碰撞体、球形碰撞体和胶囊碰撞体。
在我的游戏中使用了盒型碰撞体,如下图,但也可以是平面,这样的话网络数可以少点,增加性能。
复合碰撞体复合碰撞体可以模拟游戏对象的形状,同时保持较低的处理器开销。为了获得更多灵活性,可以在子游戏对象上添加额外的碰撞体。例如,可以相对于父游戏对象的本地轴来旋转盒体。在创建像这样的复合碰撞体时,层级视图中的根游戏对象上应该只使用一个刚体组件。
原始碰撞体无法正常处理剪切变换。如果在变换层级视图中组合使用旋转和非均匀比例,从而使产生的形状不再是原始形状,则原始碰撞体无法正确表示这个形状。
网格碰撞体然而,在某些情况下,即使复合碰撞体也不够准确。在 3D 中,可以使用网格碰撞体精确匹配游戏对象网格的形状。在 2D 中,2D 多边形碰撞体不能完美匹配精灵图形的形状,但您可以将形状细化到所需的任何细节级别。
这些碰撞体比原始类型具有更高的处理器开销,因此请谨慎使用以保持良好的性能。此外,网格碰撞体无法与另一个网格碰撞体碰撞(即,当它们进行接触时不会发生任何事情)。在某些情况下,可以通过在 Inspector 中将网格碰撞体标记为 Convex 来解决此问题。此设置会产生“凸面外壳”形式的碰撞体形状,类似于原始网格,但填充了底切。
这样做的好处是,凸面网格碰撞体可与其他网格碰撞体碰撞,因此,当有一个包含合适形状的移动角色时,便可以使用此功能。但是,一条适用的规则是将网格碰撞体用于场景几何体,并使用复合原始碰撞体近似得出移动游戏对象的形状。
静态碰撞体可将碰撞体添加到没有刚体组件的游戏对象,从而创建场景的地板、墙壁和其他静止元素。这些被称为_静态_碰撞体。相反,具有刚体的游戏对象上的碰撞体称为_动态_碰撞体。静态碰撞体可与动态碰撞体相互作用,但由于没有刚体,因此不会通过移动来响应碰撞。
在我的游戏中的地面,如下图,静态碰撞体勾上选项“static”,动态碰撞体不用勾选
物理材质当碰撞体相互作用时,它们的表面需要模拟所应代表的材质的属性。例如,一块冰将是光滑的,而橡胶球将提供大量摩擦力并且弹性很好。虽然碰撞时碰撞体的形状不会变形,但可以使用 Physics Materials(物理材质)__配置碰撞体的摩擦力和弹力。可能需要进行多次试验和纠错后才能获得正确参数,比如冰材质将具有零(或非常低的)摩擦力,而橡胶材质则具有高摩擦力和近乎完美的弹性。有关可用参数的更多详细信息,请参阅物理材质和 2D 物理材质的参考页面。请注意,由于历史原因,3D 资源实际上称为 Physic Material(物理材质)__(_不带_ s),而等效的 2D 资源则称为 _Physics Material 2D(2D 物理材质)__(_带 s)。
在我的游戏中的草地使用“物理材质”,在有碰撞体组件的游戏对象上添加“物理材质”组件 ,是为了增加地面的摩擦,如下图中红色箭头的标记
3)触发器脚本系统可以使用 OnCollisionEnter 函数检测何时发生碰撞并启动操作。但是,也可以直接使用物理引擎检测碰撞体何时进入另一个对象的空间而不会产生碰撞。配置为__触发器__(使用 Is Trigger 属性)的碰撞体不会表现为实体对象,只会允许其他碰撞体穿过。当碰撞体进入其空间时,触发器将在触发器对象的脚本上调用 OnTriggerEnter 函数。
4)脚本碰撞回调发生碰撞时,物理引擎会在附加到相关对象的所有脚本上调用特定名称的函数。可以在这些函数中放置所需的任何代码来响应碰撞事件。例如,当汽车撞到障碍物时,可以播放碰撞音效。
在第一个检测到碰撞的物理更新中,将调用 OnCollisionEnter 函数。在保持接触的更新期间,将调用 OnCollisionStay,最后由 OnCollisionExit 指示接触已经中断。触发碰撞体会调用模拟的 OnTriggerEnter、OnTriggerStay 和 OnTriggerExit 函数。请注意,对于 2D 物理,可使用在名称中附加了 2D 字样的等效函数,例如 OnCollisionEnter2D。有关这些函数和代码示例的完整详细信息,请参阅关于 MonoBehaviour 类的脚本参考页面。
对于正常的非触发碰撞,还有一个额外的细节,即所涉及的对象中至少有一个对象必须具有非运动刚体(即,必须关闭 _Is Kinematic_)。如果两个对象都是运动刚体,则不会调用 OnCollisionEnter 等函数。对于触发碰撞,此限制不适用,因此运动和非运动刚体都会在进入触发碰撞体时提示调用 OnTriggerEnter。
在我的“平衡球”小游戏中有具体的使用场景,“平衡球”遇到“进度保存的碰撞体”时可以穿过,大家先看下代码,以下业务逻辑实现是实现游戏的“游戏进度”
private void OnTriggerEnter(Collider other)
{
if (other.transform.tag == "GameProgressPoint")
{
int index = GlobalData.GameProgressPointMgr.FindGameProgressPointIndex
(other.gameObject.GetComponent<GameProgressPoint>());
if (index == -1)
return;
GlobalData.GameProgressPointMgr.SaveCurGamProgressPointIndex(index);
}
}
5)碰撞体相互作用碰撞体彼此之间的相互作用根据刚体组件的配置不同而不同。三个重要的配置是_静态碰撞体 (Static Collider)(即完全没有附加任何刚体)、刚体碰撞体 (Rigidbody Collider)_ 和_运动刚体碰撞体 (Kinematic Rigidbody Collider)_。
静态碰撞体静态碰撞体是具有碰撞体而没有刚体的游戏对象。静态碰撞体在大多数情况下用于表示始终停留在同一个地方而永远不会四处移动的关卡几何体。靠近的刚体对象会与静态碰撞体发生碰撞,但不会移动静态碰撞体。
在特殊情况下,物理引擎针对永远不会移动的静态碰撞体进行优化。例如,即使您移动静态碰撞体,停在静态碰撞体上的车辆也会保持睡眠状态。您可以在运行时启用、禁用或移动静态碰撞体,而不会特别影响物理引擎的计算速度。另外,只要缩放比例是统一的(无偏差),就可以安全地缩放静态网格碰撞体。
刚体碰撞体这是一种附加了碰撞体和普通非运动刚体的游戏对象。刚体碰撞体完全由物理引擎模拟,并可响应通过脚本施加的碰撞和力。刚体碰撞体可与其他对象(包括静态碰撞体)碰撞,是使用物理组件的游戏中最常用的碰撞体配置。
运动刚体碰撞体这是一种附加了碰撞体和_运动_刚体(即启用了刚体的 IsKinematic 属性)的游戏对象。可使用脚本来移动运动刚体对象(通过修改对象的变换组件),但该对象不会像非运动刚体一样响应碰撞和力。运动刚体应该用于符合以下特征的碰撞体:偶尔可能被移动或禁用/启用,除此之外的行为应该像静态碰撞体一样。这方面的一个例子是滑动门,这种门通常用作不可移动的物理障碍物,但必要时可以打开。与静态碰撞体不同,移动的运动刚体会对其他对象施加摩擦力,并在双方接触时“唤醒”其他刚体。
即使处于不动状态,运动刚体碰撞体也会对静态碰撞体产生不同的行为。例如,如果将碰撞体设置为触发器,则还需要向其添加刚体以便在脚本中接收触发器事件。如果不希望触发器在重力作用下跌落或在其他方面受物理影响,则可以在其刚体上设置 IsKinematic 属性。
可使用 IsKinematic 属性随时让刚体组件在正常和运动行为之间切换。
这方面的一个常见例子是“布娃娃”效果;在这种效果中,角色通常在动画下移动,但在爆炸或猛烈碰撞时被真实抛出。角色的四肢可被赋予自己的刚体组件,并在默认情况下启用 IsKinematic。肢体将通过动画正常移动,直到所有这些肢体关闭 IsKinematic 为止,然后它们立即表现为物理对象。此时,碰撞或爆炸力将使角色飞出,使肢体以令人信服的方式被抛出。
在我的“平衡球”小游戏中有具体的使用场景 - “坐船”关卡,如下图
碰撞操作矩阵当两个对象碰撞时,可能会发生许多不同的脚本事件,具体取决于碰撞对象的刚体配置。以下图表详细列出了根据附加到对象的组件来调用的事件函数。某些组合仅会使两个对象之中的一个对象受到碰撞的影响,但一般规则是物理特性不会应用于没有附加刚体组件的对象。
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved