Skip to content

简介

要在 SandMod 编辑器中配置 游戏对象(Game Object)之间的碰撞,需要使用 碰撞器(Collider)。碰撞器定义游戏对象需要进行物理碰撞的物体形状(即碰撞盒),同时您可以使用这些碰撞器来管理碰撞事件。

用法

基础用法

碰撞器(Collider)用于处理游戏对象之间的碰撞,将碰撞器以 组件(Component)的形式附加到 游戏对象(Game Object)并根据游戏对象的形状定义碰撞器的碰撞盒样式以实现物理碰撞。

【Collider001.png】

带有球形碰撞器组件的游戏对象撞向有盒子碰撞器组件的游戏对象,此时 SandMod 将模拟这两个游戏对象碰撞的物理效果。

碰撞盒是不可见的,并且不需要与游戏对象的网格形状完全相同。 与网格粗略近似的碰撞盒通常在游戏玩法中有更好的性能,且玩家也难以分辨差别。

【Collider002.png】

图中的房屋和玩家只会在底部有碰撞,因此为房屋创建了近似房子形状的盒子形碰撞盒的碰撞器。

选择一个游戏对象的碰撞盒时,最常用、简单、对游戏性能损耗最低的方式,就是直接选用原始碰撞盒类型的碰撞器:

【Collider003.png】【Collider004.png】【Collider005.png】
盒子碰撞器球形碰撞器胶囊碰撞器

复合碰撞器

复合碰撞器指的不是一个碰撞器类型,而是由多个基础碰撞器组成的可以简易模拟 游戏对象(Game Object)形状的碰撞器组合。如果单一碰撞器无法有效模拟游戏对象的形状,可以使用复合碰撞器模拟游戏对象的形状,同时在游戏项目运行时保持较低的性能损耗。

【Collider006.png】

图中房子为了能更好模拟楼梯的效果,其整体碰撞盒是由多个盒子碰撞器组合的复合碰撞器模拟。

复合碰撞器组合方式:

  • 具有 刚体(Rigidbody) 组件(Component)的游戏对象作为根节点。
  • 多个包含碰撞器的子级空游戏对象。

值得注意的是,复合碰撞器需要只有一个刚体,而且位于根游戏对象上。

【Collider007.png】

图中还原复合碰撞器在 层级窗口(Hierachy)的结构和各个游戏对象挂载的组件。

这样的组合方式下,当以力移动具有 刚体(Rigidbody) 组件的父级游戏对象时,子级的碰撞器也会随之保持组合结构移动。这些碰撞器可以与环境中的其他碰撞器发生碰撞,刚体(Rigidbody)会根据这些碰撞信息体模拟真实的物理逻辑改变其移动方式。

Note:
当您重新配置碰撞器时,您应该注意刚体的质心。在现实中,改变碰撞器位置和比例(相当于改变了物体的形状)会改变刚体的质心,此时您需要使用 Rigidbody.centerOfMass 动态设置质心,以确保该复合碰撞器的碰撞或物理效果更加真实。具体内容请参阅 API 文档关于 Rigidbody.centerOfMass 的相关内容。

网格碰撞器

如果复合碰撞器的碰撞检测精度无法满足您的需求,您可以尝试网格碰撞器。

【Collider008.png】

图中房子使用网格碰撞器能更高效精准地根据网格数据生成其。

网格碰撞器的碰撞盒是基于模型网格而形成的。在大多数情况下,网格碰撞器提供与复合碰撞器类似的解决方案,它们的主要目的是为具有复杂形状的对象提供准确的碰撞信息。

您可以通过与复合碰撞器进行比较,了解网格碰撞器的优点和局限性,这将成为您判断使用网格碰撞器还是复合碰撞器还是结合使用的依据。

网格碰撞器的主要优点:

  • 网格碰撞器非常准确,因为它们与物品的形状完美匹配。
  • 网格碰撞器比复合碰撞器需要更少的手动调整时间,因为 SandMod 会根据网格的形状自动处理其碰撞盒形状。
  • 对于非常复杂的形状,网格碰撞器的性能要求可能低于复合碰撞器。因为要打造一个近似的碰撞盒需要大量的复合碰撞器来进行构建,比起这样,只使用一个网格碰撞器性能可能会更好。

然而,网格碰撞器也有一些明显的局限性:

  • 网格碰撞器无法提供凹形形状的碰撞盒。有关详细信息,请参阅 网格碰撞器 章节关于凹面和凸面网格碰撞器的相关内容。
  • 在运行时模型形状发生变化,网格碰撞器的碰撞盒也不能动态变化。
  • 对于仅需要几个碰撞器来简单打造一个近似的碰撞盒,或者不需要碰撞盒太精确的情况下,复合碰撞器的性能要低于网格碰撞器。例如,网格碰撞器可能会生成数百个多边形,以便精确匹配复杂的形状,但使用原始碰撞器进行近似可能需要的多边形要少得多。

如果您决定使用网格碰撞器,并需要了解更多关于网格碰撞器的相关介绍,请参阅 网格碰撞器 章节。

碰撞器类型

一个碰撞器的类型定义了它如何与其他碰撞器交互,而碰撞器的类型基于 游戏对象(Game Object)碰撞器和如 刚体(Rigidbody)、车辆创建器(Vechicle Creator)等物理体 组件(Component)的配置。

静态碰撞器

静态碰撞器(Static Collider),游戏对象拥有碰撞器,但是没有如刚体、车辆创建器等物理体组件。

【Collider009.png】

静态碰撞器普遍样式。

静态碰撞器不会模拟因碰撞产生的物理效果,即其他碰撞器可以与静态碰撞器碰撞,但静态碰撞器不会响应碰撞。您可以将始终保持在同一位置并且从不四处移动的几何体(例如,地板、墙壁和场景中的其他静止元素)设置为静态碰撞器。由于静态碰撞器并非设计为移动或临时碰撞器,因此物理系统不会重新计算或唤醒有物理体组件的游戏对象以响应静态碰撞器的移动或消失。

--------------
比如说,将一辆带有 刚体(Rigidbody)和 车轮碰撞器(Wheel Collider)的汽车放在静态碰撞器上,车轮碰撞机检测到碰撞器并在其上静止。在没有输入的几帧之后,汽车的物理主体和碰撞器进入睡眠状态。
【Collider010.png】

如果静态碰撞器移动,您可能会认为汽车会随之移动或掉落。然而,物理系统不会响应静态碰撞器的运动而唤醒汽车的物理主体,因此汽车停留在原处。
【Collider011.gif】
--------------

在运行时移动静态碰撞器的唯一方法是通过 变换(Transform)改变游戏对象的位置。但是,建议您仅将静态碰撞器用于在运行时不移动的几何体的碰撞器。如果您想要一个不会被外力影响但可以在运行时以物理系统可以检测和计算的方式移动的碰撞器,您可以使用 运动学碰撞器(Kinematic Collider)。

动态碰撞器

动态碰撞器(Dynamic Collider)是指碰撞器相关的组件和如 刚体(Rigidbody)、车辆创建器(Vechicle Creator)等物理体组件在同一个游戏对象上,且刚体组件的 运动学(Is Kinematic)属性 未被勾选(False)。

【Collider012.png】

动态碰撞器普遍样式。

动态碰撞器可以与其他碰撞器(包括静态碰撞器)碰撞,动态碰撞器会根据其受到的力做出响应并模拟物理效果。

【Collider013.mp4】

视频为两个动态碰撞器碰撞的效果演示。

如果您希望该碰撞器能拥有物理效果,且您可以接受其会被外力影响其运动轨迹,可以使用动态碰撞器。

运动学碰撞器

运动学碰撞器(Kinematic Collider)是指碰撞器相关的组件和 刚体(Rigidbody)体组件在同一个游戏对象上,且刚体组件的 运动学(Is Kinematic)属性 被勾选(True)。

【Collider014.png】

运动学碰撞器普遍样式。

与 静态碰撞器(Static Collider)一样,运动学碰撞器不会模拟受到力后的物理效果。其他碰撞器可以与运动学碰撞器碰撞,但运动学碰撞器不会因碰撞而被移动。但是与静态碰撞器不同,物理系统会针对运动碰撞器的行为进行相关的物理计算。运动碰撞器可以:

  • 当其他有 车轮碰撞器(Wheel Collider)的碰撞器碰撞后会被激活,重新响应物理效果。
  • 可以通过摩擦和挤压给其他动态碰撞器提供力并能影响他们。

碰撞器相互作用

当两个碰撞器发生碰撞,碰撞器事件函数将会被调用。您可以在这些事件函数中编辑发生碰撞后的逻辑。碰撞器事件及其逻辑需要通过脚本进行配置,您无法使用 检视窗口(Inspecter)来配置它们。

碰撞器事件包括两种类型:触发事件(Trigger Events)和 碰撞事件(Collision Events)。

触发事件

触发事件(Trigger Events),当两个碰撞器接触、其中一个碰撞器 是否触发(Is Trigger)的属性 被勾选(True),另一个碰撞器 是否触发(Is Trigger)的属性 不勾选(False),且至少有一个碰撞器不是静态碰撞器时(无论该静态碰撞器是否触发的属性是否有被勾选),就会发生触发事件。

碰撞器 是否触发(Is Trigger)的属性 被勾选(True)后,其被称为触发碰撞器,触发碰撞器与其他碰撞器碰撞后不会模拟物理效果,触发碰撞器用于创建一个区域空间,当其他碰撞器经过该区域时,就会调用触发事件。

下表中描述了不同碰撞器类型的碰撞器碰撞的各种组合,表中勾选代表其对应的横轴和纵轴的碰撞器类型的碰撞器发接触会发生触发事件。

静态碰撞器动态碰撞器运动学碰撞器静态触发碰撞器动态触发碰撞器运动学触发碰撞器
静态碰撞器True.pngTrue.png
动态碰撞器True.pngTrue.pngTrue.png
运动学碰撞器True.pngTrue.pngTrue.png
静态触发碰撞器True.pngTrue.png
动态触发碰撞器True.pngTrue.pngTrue.png
运动学触发碰撞器True.pngTrue.pngTrue.png

发生触发事件时被调用的触发函数是:

  • OnTriggerEnter(collider:Collider){} :当触发碰撞器首次与另一个碰撞器接触时,在触发碰撞器上将调用此函数。
  • OnTriggerStay(collider:Collider){} :当在触发碰撞器内检测到另一个碰撞器,则每帧在触发碰撞器上调用此函数一次。
  • OnTriggerExit(collider:Collider){} :当另一个碰撞器与触发碰撞器接触后离开触发碰撞器并不再接触时,在触发碰撞器上将调用此函数。

触发函数都有一个 Collider 类的返回值,您可以通过该返回值获得与触发碰撞器接触的另一个碰撞器,您可以通过该碰撞器获取其挂载的 游戏对象(Game Object)等信息,具体内容和使用方法请参阅 API 文档关于 Collider 的内容。

调用触发函数的代码示例如下:

typescript
class TriggerEvent extends Component {

    OnStart(): void {
    }

    OnUpdate(): void {
    }

    /*
    当触发碰撞器与另一个碰撞器接触时调用的事件;
    */
    OnTriggerEnter(other:Collider){
        //这里编辑触发事件开始发生时的事件;
        //在控制台输出发生触发事件的本触发碰撞器 游戏对象(Game Object)名字;
        Debug.Log("This trigger collider's name is: ",this.gameObject.name);
        //在控制台输出发生触发事件的另一个碰撞器的 游戏对象(Game Object)名字;
        Debug.Log("The collider be on trigger is: ",other.gameObject.name);
    }

    /*
    当碰撞器在触发碰撞器里面时,每帧在触发碰撞器上调用的事件;
    */
    OnTriggerStay(other:Collider){
        //这里编辑正在触发时的事件;
    }

    /*
    当碰撞器与触发碰撞器接触后离开触发碰撞器时调用的事件;
    */
    OnTriggerExit(other:Collider){
        //这里编辑触发结束时的事件;
    }
}

碰撞事件

碰撞事件(Collision Events),当两个碰撞器接触且两个碰撞器 是否触发(Is Trigger)的属性均 未被勾选(False),且其中一个是动态碰撞器,就会发生碰撞事件。

下表中描述了不同碰撞器类型的碰撞器碰撞的各种组合,表中勾选代表其对应的横轴和纵轴的碰撞器类型的碰撞器发生碰撞会发生碰撞事件。

静态碰撞器动态碰撞器运动学碰撞器静态触发碰撞器动态触发碰撞器运动学触发碰撞器
静态碰撞器True.png
动态碰撞器True.pngTrue.pngTrue.png
运动学碰撞器True.png
静态触发碰撞器
动态触发碰撞器
运动学触发碰撞器

发生碰撞事件时被调用的碰撞函数是:

  • OnCollisionEnter(collision:Collision){} :当两个碰撞器首次接触时,每个碰撞器上将调用此函数。
  • OnCollisionStay(collision:Collision){} :当两个碰撞器接触时,每帧在每个碰撞器上调用此函数一次。
  • OnCollisionExit(collision:Collision){} :当两个碰撞器接触后分开至不再接触时,每个碰撞器上将调用此函数。

碰撞函数都有一个 Collision 类的返回值,您可以通过该返回值获得碰撞事件的相关信息,具体能获得内容请参阅 API 文档关于 Collision 的内容。

调用碰撞函数的代码示例如下:

typescript
class CollisionEvent extends Component {

    OnStart(): void {
    }

    OnUpdate(): void {
    }

    /*
    当两个碰撞器首次接触时调用的事件;
    */
    OnCollisionEnter(collision:Collision){
        //这里编辑碰撞开始发生时的事件;
        //在控制台输出发生碰撞的两个碰撞器的 游戏对象(Game Object)名字;
        Debug.Log(collision.gameObject.name," and ",this.gameObject.name," start colliding.");
    }

    /*
    当两个碰撞器接触时,每帧在每个碰撞器上调用的事件;
    */
    OnCollisionStay(collision:Collision){
        //这里编辑正在碰撞时的事件;
    }

    /*
   当两个碰撞器接触后分开至不再接触时调用的事件;
    */
    OnCollisionExit(collision:Collision){
        //这里编辑碰撞结束时的事件;
    }
}