Cocos Creator 3D 物理模块介绍

2019.10.17 cocos 技术干货 by COCOS

为提升阅读体验,[参考链接]统一放在文末!

设计思路

简单易用​

为了让游戏开发更加简单、友好和高效,Cocos Creator 3D 在研习和摸索中设计了一套比较基础的物理组件,并且还在持续完善中。尽管当前的组件功能还十分有限,但是相信在有了之前的组件设计经验后,很快就可以有更多强大且易用的物理组件。
另外,我们还对物理模块设计了一套抽象层,这是物理组件能够发挥其功能的底层支持,也是我们为多物理后端打下的基础。

多物理后端


用于接入不同的物理引擎,目标是能够在开发时,切换到不同的物理引擎后端中,满足对于不同游戏所需要的不同物理功能。
多物理后端的设计,可以使游戏包体更灵活,但主要的考虑是不同游戏要求的物理功能是不同的,例如:

  • 有些游戏只需要检测系统
  • 有些游戏需要支持基本功能并且包体小的物理
  • 有些游戏需要功能齐全的物理

对于不同的物理引擎后端,抽象层也将致力于提供简单统一的 API 使用,并且将尽力保障相同的参数在每个后端的实际表现达到统一一致。当然,这里针对的是所有后端都支持的功能。我们为多物理后端打下的基础。

目前的状况及后续发展

目前 Cocos Creator 3D 的物理模块已经支持仅碰撞检测的 builtin 和轻量功能的 cannon.js 物理引擎。未来,我们将持续完善已接入 cannon.js 的更多功能特性,并且还将接入功能更加强大齐全的 ammo.js。

使用示例

在介绍使用方法之前,为了更好地展示目前的物理功能,以便更好地结合具体使用与具体情境,我将通过一些示例来进行讲解,文末参考链接[Demo]中可以获取完整示例。

吞噬与吸引效果

上方的动图中,蓝色圈可以看作是一个洞,并具有吸引物体的能力,一个完整的洞效果可以分为物理和渲染两个方面的内容,这里主要介绍的是物理方面。

首先,看一下它们的物理结构:

方块(球类似,区别为碰撞器是球形状的,地面是只有碰撞器的结构)   

蓝色圈      

通过上面的节点树层级关系,可以知道 Hole 节点负责的是物理的功能,而它的子节点负责的是渲染的功能。当然,这里的层级关系也可以改变,例如,把渲染元素所需要的组件挂载到 Hole 节点上。

在 Hole 节点上,主要体现物理元素的是它挂载的两个球碰撞器组件,并且都是触发器的类型。这里可以分别看作内外圈,它们负责的功能分别为“洞”和“吸引”。

“吸引”的实现:监听外圈的触发器进入的事件,当物体进入时,对其施加向洞中心方向的力,这样就可以实现洞的吸引效果。

“洞”的实现:同样监听内圈的触发器进入事件,当物体进入时,修改物体碰撞器的掩码,使得物体不会与地面产生碰撞,物体的刚体由于受到重力和吸引力的影响,就会掉落下去,这也可以看做“吞噬”的效果。

车辆模拟的效果

物理模拟是为了表现真实世界,所以使用物理的最佳实践,就是把实物结构用基础的元素表达出来,再调整每个基础元素的属性,这样一般都可以模拟出期望的效果。

但物理模拟始终是模拟,在游戏中这类需要实时交互的场景里,需要将表现力和实时交互能力平衡。如果需要更佳的实时性能,那么可以尝试对物理结构简化。

需要注意的是,本文介绍的车辆模拟,是基于现有的物理功能制作的,并不是严格的真实车辆模拟,只是一种取巧的方式,而实现此车辆模拟的主要内容是车辆的结构:

上图结构也是通过真实车的结构简化而来的,设计好结构后,还需要调整每个部分的属性:

重心: 车的重心应该要低一些,否则可能会很容易翻车;

车身和车轮:车辆运动应该是很平稳的,可以把摩擦力系数都设置为 0,另外车轮要比车身低一些,这样在碰到障碍物后车辆会有晃动的效果,用来模拟避震;

挡板:因为车身摩檫力设置为 0 了,为了防止车滑起来,加一个摩檫力不为 0 的挡板;

刚体:默认质量为 10,这里可以改成 200;因为摩檫力都为 0,避免车一直滑动,将阻力(damping)设置为 0.9;角速度因子 x 轴向设置成 0.5,减低车在 x 轴向的旋转抖动。

使用简介

根据所需物理功能,选择不同物理

在开发前,可以先想好大致需要什么样的物理功能,根据不同的功能,选择不同的物理,以下条件可供参考:

  • 只需要规则的运动模拟,例如转动风扇、简单的跳跃,可以通过造型方程进行模拟,例如通过圆的方程进行圆周运动。
  • 只需要碰撞检测,可以考虑使用 builtin + collider 组件或者使用 gemotry 模块中的 intersect 相交性检测 API。
  • 需要物理模拟,目前可以考虑使用 cannon + 物理相关组件,或者通过直接获取引擎底层的物理进行开发。
  • 更加复杂的物理,如复杂约束、车量模拟、布娃娃模拟等,目前组件还未提供,但可以考虑在自己项目中嵌入第三方物理。

在使用物理之前,建议先阅读[物理文档]、物理[测试例]和简单的[Demo],在这些参考文档中,你可以了解到一些简单 API 的功能和使用方式,建议文档和测试例同时查看,会更助于理解物理模块。

选择好相应的物理后,接下来通过以下情况做一个简单演示。

规则的运动模拟

在 Cocos Creator 3D 中,可以通过节点 Transform 数据(即 position、rotation、scale 等属性)对节点进行变换,这也同样会驱动其节点链上的模型、粒子、刚体等组件的矩阵信息,以下脚本将通过修改节点的 Transform 信息进行椭圆方程的运动:

    // 这里演示节点以椭圆方程进行运动    // 注:椭圆的参数方程 x = a*cosθ, y = b*sinθ。    const _v3_0 = new Vec3();    update (deltaTime: number) {        const now = performance.now() / 1000;        _v3_0.x = this.a * Math.cos(now);        _v3_0.z = this.b * Math.sin(now);        this.node.setPosition(_v3_0);    }

builtin 物理检测

builtin 碰撞检测,底层实际调用的就是[intersect]提供的接口。首先选择物理模块为 builtin ,如下图:

然后,为需要进行检测的节点,加上碰撞体并调整大小,最后根据注册的触发事件来做出相应的行为。

    // 触发事件分三种类型,Enter\Stay\Exit,需要通过 ColliderComponent 组件注册    start () {        const collider = this.getComponent(ColliderComponent);        collider.on('onTriggerEnter', this.onTriggerEnter, this);        collider.on('onTriggerStay', this.onTriggerStay, this);        collider.on('onTriggerExit', this.onTriggerExit, this);    }    onTriggerEnter (event: ITriggerEvent) { /* TODO */ }    onTriggerStay (event: ITriggerEvent) { }    onTriggerExit (event: ITriggerEvent) { }

cannon.js 物理模拟

选择物理模块为 cannon.js,为需要模拟的节点加上 RigidBodyComponent 组件,这样该节点就会进行物理模拟。再加上相应的 ColliderComponent,该节点的刚体就会增加相应的碰撞体,这会用于检测是否与其它碰撞体产生碰撞。

刚接触物理模拟,遇到最大的问题,大概是不知道如何控制刚体,建议各位开发者先熟悉刚体的每个参数所代表的物理意义,在[官方文档]中有详细介绍。

这里将根据一些简单例子来介绍一些可采用的做法:

模拟跳跃行为:由于运动是可以分解的,所以跳跃和移动等可以看作是一类问题,只要让相应的轴向拥有速度,刚体就会运动起来了。

在 cannon.js 物理中改变速度有多种方式,比如可以通过  setLinearVelocity 直接设置线性速度,以及通过 setAngularVelocity 直接设置角速度,这种方式将会使得物体由最大的速度开始往上。但由于受重力影响,物体 Y 轴向的速度将时间变化减小至 0;

也可以通过施加力或者冲量的方式, applyForce 或 applyImpluse,这种方式是根据公式定律计算得出的速度,以 applyForce 举例:

  • F=M·a F 是刚体的受力,M 是质量,a 是加速度
  • v=a·t v 是瞬时速度,a 是加速度,t 是时间

这种方式需要理解一些参数去实现效果,如施加力时需要考虑刚体的质量,以及对刚体施加的时间(在这里可以说是施加次数或帧数)。

限制刚体在 Y 轴旋转:这类问题可以看作是对刚体的约束,目前在刚体组件里面提供了对线性速度和角速度的缩放属性,即 linearFactor 与 angularFactor (可以看作是与刚体速度执行分量积),将 angularFactor 的 y 分量设置成 0 ,就可以达到这个效果。理解这些属性,并灵活的使用它们,可以实现非常多的效果。

目前的 cannon.js 支持情况:

  • 刚体
  • box\sphere 形状
  • 支持触发、碰撞事件,分别为:enter\stay\exit
  • 物理材质
  • 射线检测

更多功能

组件层服务的是所有的开发者,稳定性、通用性是首要的目标,Cocos 将在这个基础上持续不断的加入更多的功能。

对于一些暂不清晰的功能需求,我们将会提供实验性的组件,例如 mesh collider(没有发布正式组件的主要原因是目前的底层支持不够)

访问底层接口:

对于某些暂未提供组件化服务的功能,各位开发者可以考虑在现有的框架之下直接使用底层的物理接口(例如现有的 cannon.js)。以下步骤将会介绍如何在项目工程中直接访问到底层的物理接口:

1. 校验,在预览页面的控制台中输入 CANNON,判断其是否存在,若存在,则不用执行下列子步骤;
(1)将 CANNON 暴露到 Window 中,这里需要改动引擎代码:首先点击编辑器右上角的安装目录按钮,在相对路径 \resources\3d\engine\cocos\physics\cannon\cannon-world.ts 下,打开该文件;在合适位置加上此行代码 window.CANNON = CANNON;然后点击编辑器的开发者选项栏的编译引擎的选项(快捷键 Ctrl +F7),最后刷新编辑器(快捷键 Ctrl+R)
(2)重复步骤 1 ,确保 CANNON 可以被访问到。

2. 将 CANNON 的类型声明文件复制到项目工程当中 (下载[声明文件],后缀为 .d.ts  的文件)

3. 在脚本中用命名空间的方式访问 CANNON 提供的接口(注意不要使用模块的方式,即 import CANNON from ‘cannon’)

通过以上步骤就可以在项目工程中直接获取底层的接口进行开发了,需要注意的是,模块需要设置成相应的物理底层。

注:暴露其他的模块是类似的方式。

结合现有框架

目前的框架还没有暴露底层的对象,如果要和现有框架结合起来,哪么需要通过 hack 的方式去访问。这里以获取物理系统中的底层世界对象为例子:

首先可以通过控制台打印出 PhysicsSystem.instance 对象,查看他的属性列表,如下图:

通过对象的层级就可以找到底层对象的实体(上图中红色框标注的),即  cc.PhysicsSystem.instance._world._world。

这种方式虽然可以达到目的,但仍需要注意以下几点:

  • 只是提供一种方案,这种方式是不推荐的,更希望大家一起参与讨论和设计;
  • 为满足上层框架,底层物理可能存在一些 API 改动;
  • 未来结构可能会变化,对于这种方式需要自己去维护。

参考文档

[物理文档]

https://docs.cocos.com/creator3d/manual/zh/physics/physics.html

[测试例]

https://github.com/cocos-creator/test-cases-3d

[Demo]

https://github.com/cocos-creator/example-3d

[intersect]

https://github.com/cocos-creator/engine/blob/3d-v1.0.0/cocos/core/geom-utils/intersect.ts

[声明文件]

https://github.com/cocos-creator/cannon.js/blob/cocos-master


以上就是今天 jiaxin 带来的物理模块介绍,有什么疑惑和见解,您可以前往 Cocos 中文社区 Creator 3D 版块与我们进行交流!