Unreal与Unity双引擎联合开发
非常感谢我们的天才设计师“李一奇”同学为我的这篇文章设计的封面图标!
前几天发了一条朋友圈,跟大家分享了一下近来一段时间一直在做的一项比较“大胆”的技术尝试。之所以用“大胆”来形容,是因为我们目前是一个创业团队,技术上的任何决策风险都可能导致整个公司陷入灾难。。。
朋友圈发布之后很多朋友来找我聊具体的实现细节。于是我决定写这篇文章来跟大家仔细分享一下我的整个实现过程。
如上图所示,每一个插件专门写一套导出接口(***PluginForUnity),然后所有的插件连接到GameModule。GameModule再统一对外暴露一套接口。修改UnrealBuildTool(以下简称UBT),使GameModule在任何情况下都生成动态库(IOS除外),并且所有的插件都静态链接进入GameModule输出的动态库。
最终输出的动态库导出的C接口在Unity的C#代码中可以直接调用:
在开发过程中针对Unreal和Unity两种环境下使用的两套接口,在一些代码逻辑实现上会有一些差异。我们可以在一些实现有差异的地方加上宏将代码隔开:
然后我们在Unreal的Build文件中加上宏开关:
这样一来只需要修改Build文件中的一个bool变量的值,然后重新编译代码即可发布相应引擎的功能模块。通过这种实现方式,在Demo实现阶段我就可以随时切换产品依赖的引擎了。
因为有的时候我们的多个模块都需要引入这些头文件,于是我将这些文件以ThirdParty的形式添加进入我们的工程:
1、在ThirdParty文件夹下建立子目录UnityPluginAPI。
2、将PluginAPI文件夹拷贝至UnityPluginAPI目录,然后创建UnityPluginAPI.build.cs文件。
3、在UnityPluginAPI.build.cs文件中我们添加如下代码:
现在我们就可以在我们的插件工程中引入新加入的UnityPluginAPI了:
我首先删除了大量的Unreal Runtime下代码模块。目前能用到的代码模块也只有这些:
以上这些模块中,Core做了大量的修改,其他模块有的做了一点点修改,有的甚至一点没动。在这个过程中还要修改UBT解决编译依赖的一些错误。最终修改编译工具,使我们的改造引擎在Android及Windows平台的Debug、Development及Shipping环境下编译出动态库,而IOS平台始终编译出静态库。这部分比较繁琐,而且也相对产品研发比较特异,所以就不详细介绍了。
最后强调一句,以上所有看似使用Unreal辅助Unity开发,但是其实我们一直没有脱离Unreal的开发环境,并且我保持了Unreal插件开发要掌握的所有细节,只要换个Unreal引擎编译你写的所有代码,一下子就变成了为Unreal开发的代码。也就是说我们可以在给使用Unity的产品开发功能的同时顺便也为使用Unreal的产品开发了功能。
通过这种实现方案目前在我们的产品研发中我们收获了大量的好处:
1、如果公司有多款产品同时使用Unreal和Unity开发,那我们在一些比较独立的功能模块研发上可以做到一份代码多个产品同时使用,再也不用为不同引擎开发相应的版本。
2、Unreal提供了大量的C++库,极为方便和稳定,这为我们开发Unity 的Native插件带来了极大的帮助。
3、写C++代码的时候注意跨平台,这方面Unreal也做了很多适应跨平台的封装。借助UBT的可以多平台编译的特性直接编译目标平台的代码。
4、在我们产品中有一些比较重要的核心算法,我全部使用C++编写。这样就不用费劲去做C#的加密和混淆了,彻底地防止了反编译的发生。
5、产品中的一些底层对效率要求极高的模块也用C++编写,保证了执行的效率。
6、我们借助于UBT可以快速生成多平台的工程文件。这比针对各个平台手动创建工程要方便的多。而且代码文件的层次结构和本地存储目录保持一直,浏览及添加或删除新的代码文件变得极为方便。降低了手动维护出错的概率。UBT的编译配置也非常直观,可灵活配置并定义自己的编译环境。
7、开发一个Unity产品有的时候我们需要接入一些三方库,这些库大部分是C/C++编写的。我们使用了跟Unreal一样的三方库接入方式。然后把一些三方库的接口根据我们的项目要求做封装,再把少量的接口暴露给C#。这样就简化了C++与C#的通信接口: