Havok物理引擎与Unity3D的结合-UNITY3D游戏外包

Havok物理引擎与Unity3D的结合-UNITY3D游戏外包

2 years ago 0 11665

在重度手游的研发过程当中,游戏中的车辆模拟,场景互动,特效展示等功能很多时候需要物理引擎的介入,以提供丰富的交互体验。目前3D手游的开发主要工具是使用Unity3D引擎,于是,如何在Unity3D的开发过程中结合入物理功能变一个需要仔细考虑的问题。

我们考察的2种物理效果实现方案:Unity3D物理引擎和Havok物理引擎。

Unity3d物理引擎介绍

Unity3d在内部集成了PhysX物理引擎,为其提供了物理模拟能力。

Physx是目前使用最为广泛的物理引擎,PhysX目前由Nvidia公司开发并维护,特点是免费且带N卡的GPU物理计算加速功能(1)。

PhysX被很多游戏大作所采用,使用PhysX制作的游戏:

4.X版本的Unity3D集成的是2.8.3的PhysX,该版本较为老旧。在Unity5中将集成PhysX3.3,较2.8.3版本在功能和性能上有较大幅的提升,但是目前unity5并不是非常稳定。

Havok物理引擎介绍

Havok物理引擎是由Havok公司开发的老牌物理引擎,与PhysX不同,Havok专注于CPU端+多线程模拟方案,并且与PhysX的强大市场推广以及免费策略不同,Havok授权很严格,而且基本不提供试用版本下载(2)。

使用Havok引擎的游戏大作在数量上与使用PhysX的不相上下,而且很多令人印象深刻:

Unity3D物理与Havok物理的功能对比

Unity3D集成的PhysX物理功能:

Unity3D通过其提供的各种Component访问PhysX的物理功能,打开菜单栏中的Component->Physics便可以看到各种组件:

其中,

Rigidbody提供了刚体的访问接口。

各种XXXCollider提供了3种碰撞包围体方案(Primitive,Mesh,Terrain)。

WheelCollider组件提供了车轮模拟方案。

XXXCloth组件提供了布料模拟方案。

XXXJoint组件提供了关节与连接点模拟方案。

PhysicsMaterial资源类型提供了表面物理材质描述功能。

在Unity3D中每一种物理组件都有对应的编辑界面,且即拖即用,非常方便。

PhysX引擎目前已经涵盖各个平台,且跨平台特性已经融入Unity3D的跨平台机制中,用户无需再关注跨平台开发。

由于PhysX与Unity3D的深度结合以及Unity3D的闭源特性,修改PhysX的底层模拟机制基本不可能。

Havok物理引擎功能介绍:

Havok物理引擎以C++库的形式,通过组件式的方式,提供了丰富的物理功能。包括了:

Rigidbody刚体模拟。

5种碰撞包围体模拟方案(Primitive,Convex,Mesh,Compount,Terrain)

完整的VehicleKit车辆模拟方案。

HavokCloth不了模拟方案。

Constraint关节与连接点模拟方案。

HKX物理资源数据描述格式以及对应序列化与反序列化接口。

HavokDestruction破碎模拟方案。

Havok并没有开发官方的Unity3D结合插件,市面上也没有第三方的结合插件可以使用,需要自行开发,有一定的开发成本。

Havok具备称述的跨平台能力,但是由于没有结合入Unity3D的跨平台机制,在发布到多平台上需要一点额外的工作量。

Havok在获得授权之后,用户可以修改与定制各个层级的物理功能。

Unity和Havok主要物理功能对比

结论:

游戏中简单的物理效果展示可以使用生的PhysX引擎,但是如果需要扩展功能或者订制细腻的物理效果,可以考虑其它物理方案。

Unity与Havok物理引擎的结合

既然有结合其它物理方案的需求,那么接下来我们便讨论一下结合的方案。

结合基本原理

要实现Havok与Unity3D相结合,那么两个系统之间的交互通信机制便是实现的关键。Unity3D提供的二次开发平台是Mono,并没有提供NativeCode级别的接口,而Havok是完全以C++编写的Native实现。所幸,Mono为IL提供了跨平台的NativeCode交互机制。

在Win和Linux(Android)平台上,Mono提供了以dll和so为基础的动态链接库交互C++代码形式,而在IOS平台上则是以AOT为基础的静态链接库交互形式,而这种混合编程的方法,便是让Unity3D与Havok交互的基础。

如图:结合的主要思路就是,Havok作为一个子系统,以插件的形式和Unity3D结合,并通过Mono进行交互。

结合的具体过程:两个系统的更新流程

由于Unity3D依然负责整个游戏的主更新流程,那么便需要将Havok的更新整合入Unity3D的流程中。一个比较合理的更新流程应该是逻辑对象先更新,然后将物理参数传入物理引擎,逻辑对象更新完成之后,物理引擎开始进行物理模拟,物理引擎返回模拟结果,Unity3D使用模拟结果进行渲染。

要实现正确的物理效果,必须保证Havok的正确更新时序,但是多个MonoBehaviour本身更新时序就是混乱的,给结合带来了一定的难度。解决方案是,建议将整个游戏所有的逻辑对象更新收归于一个MonoBehaviour中,场景中GameObject上挂接的其余MonoBehaviour仅用于配置参数,不做任何的逻辑更新。

结合的具体过程:两个系统关键对象的对应关系

更新流程的结合可以保证两个系统能运转起来,但是要实现具体功能,则必须明确这两个系统之间的功能对象以及他们的对应关系。

Unity3D的游戏世界是以Scene为单位的,而在Havok中对应的概念是HavokWorld,同一个HavokWorld中的对象才会相互碰撞。Unity3D中的游戏对象是以GameObject附带各种组件来表现,而在Havok中,刚体以HavokRigidbody表示,车辆以HavokVehicle表示,布料以HavokCloth表示,并没有一个直接对应关系,因此,需要在概念上加以封装,然后再对应。

如果我们以CGameScene封装了UnityScene,CGameEntity封装了游戏逻辑对象,CHavokWorld封装了物理世界,CHavokEntity封装了物理功能对象,那么,以一个刚体碰撞对象为例,他们之间的对应关系应该是:

对象关系明确之后,对象之间的更新时序也可以相应明确:

简单描述上图中,一个刚体对象的整个功能流程是:CGameWorld中的CGameEntity在一系列逻辑更新之后,准备好了一系列的物理更新参数(可能是需要改变的额外力大小,或者是需要改变的质量大小);然后CGameEntity将这些参数设置给物理对象CHavokEntity,CHavokEntity将参数分解,交给具体的实现对象(hkpRigidbody刚体实现对象);当所有的CGameEntity都传递好参数之后,CGameWorld通知CHavokWorld,开始模拟;等模拟结束之后,所有的CGameEntity再纷纷从CHavokEntity中获取模拟的结果(由hkpRigidbody维护的位置,朝向等最终模拟结果),而这些模拟结果,最终会被设置给GameObject的Transform,拿去做下一步的逻辑处理或者直接渲染。

结合的具体过程:物理资源制作流程的结合

Havok拥有自己的资源描述格式:HKX。HKX可以存放从包围盒到刚体对象(hkpRigidbody实例)等很多信息,HKX拥有文本和二进制两种模式,但是任何一种模式都不被Unity3D直接支持。被结合入Unity3D的Havok物理引擎要想访问到HKX格式的资源有两种方法:

一.将HKX文件直接放到Unity3D的StreammingAssets目录下,在Havok的C++代码层面通过不同平台的IO API去读文件。

这样的不便之处在于需要自己维护StreammingAssets目录下的大量文件,也不能使用Unity3D的异步IO机制。

二.利用Unity3D的ScripableObject机制,将HKX文件内容序列化到UnityAsset中,让Unity3D在IO之后以byteStream的形式传给Havok。

好处在于,可是使用Unity3D编辑器强大的文件管理机制,以及异步IO系统。弊端在于byteStream传递过程中可能带来内存的额外开销(包括GC以及内存峰值)。