最近看到很多人讨论这个,也有一些分析的文章,但是我认为最靠谱的还是来自知乎的一篇分析, 作者把黑神话逆向,解包之后,得出的结论是:
黑神话的脚本方案是魔改的USharp,自己实现了mono/clr/il2cpp的运行模式,且支持全平台(PC/Mac/Linux/Android/iOS/PS5/XBox)。
以下是原文内容:
地址:https://zhuanlan.zhihu.com/p/715690420
作者:John
前言
《黑神话:悟空》上线了,第一时间下载体验了一下。然后很好奇他们用的是什么脚本方案,因为大型UE项目绝不可能纯C++开发,所以解包看了一下,也发现一点有意思的东西。
黑神话文件目录
其实上线之前,他们官方就发了一个免费的测试工具,实际上这个测试工具是包含完整的代码的,只是剔除了大部分游戏资源。因为项目组不可能有精力从头搞一个新工程来做测试工具,剔除资源比剔除代码容易一万倍,程序员应该都懂。
引擎版本
首先是引擎版本,大家都知道他们一开始是UE4,UE5首发之后他们就切换到UE5了,所以引擎版本是UE5.0.0。之后虽然Epic发布了更新的UE5.0.3等版本,但是项目组一般不会轻易升级的。可能只是移植一些需要的代码过来,毕竟换引擎的话,以前的大量魔改不好迁移。
游戏入口exe
代码段大小
有一点值得注意的是,exe的大小达到了859MB,这个代码段大小不太正常。参考同样是UE开发的游戏,三角洲行动端游140MB,极品飞车集结端游105MB。感觉可能是包含了太多没用的插件代码或者没剔除一些不必要的符号?
脚本方案
在查看代码符号时,我发现了大量V8的字段,所以理所当然的以为竟然用了PuerTS,觉得项目组还挺跟随潮流。但是却搜不到PuerTS相关的符号,转而搜索js,发现代码中包含了Unreal.js插件。
Unreal.js是一个N年前的插件,一直没维护,去年才加了UE5的支持。看起来黑神话对这个插件做了些修改。
就这样,我以为用的是Unreal.js,直到开始解包pak才发现并没有这么简单。
Pak解包
解包UE的Pak,一般要知道对应版本的引擎和AESKey,因为每个版本PakInfo结构不太一样。这里不太适合公开细节,略过。
直接说结果,我发现Pak中找不到类似js、ts、wasm的代码文件,所以可能他们继承了这个插件但是没有用。也可能是用其他方式存储了。
但是翻Pak文件我发现大量存在的两种二进制文件,.data和.Data。小写开头的像是配置文件,大写开头的像是某种代码的二进制文件。
如下 .data 文件,就是物品的配置,看起来包含了物品的蓝图路径和其他配置内容,是类似protobuf的结构(因为存储的目录名是PBTable,在游戏行业使用PB存储配置是很常用的方式):
.data
如下.Data 文件看起来 就像是某种代码的二进制,但按我的经验js或者ts应该不是这样,倒像是一种自定义的状态机的样子(根据后续分析,确实是一种用于策划编辑的自定义蓝图结构):
.Data
这就奇怪了,没有js代码,也没有找到类似luac的代码,但是既然有类似二进制的代码,那么是不是用了C#相关方案呢?比如USharp、UnrealCLR、UnrealSharp,直接在代码符号中搜索相关字符串果然找到了USharp,并且Pak中也有USharp.uplugin文件,所以可以确定用了USharp。
SharpClass
上图可以看到蓝图节点中大量使用了SharpClass,也就是USharp的自定义UBlueprintGeneratedClass,用于运行时将C#中的自定义UClass动态生成UE的类型。
USharp.uplugin
但是,貌似没这么简单,因为USharp不支持UE5,并且不支持主机和移动端,但是黑神话包内的uplugin文件包含了这些平台。我想黑神话官方对USharp进行了大量的魔改和兼容。
但到这里我还是没搞清楚黑神话到底用了什么脚本方案,因为Pak中并没有找到js、lua或者USharp的dll文件。可是明明代码中用了Unreal.js和USharp插件。
别着急,继续往下看。
il2cpp
提到il2cpp,Unity开发者们都很熟悉,为了解决mono的兼容性和性能等问题推出的方案。
但是这是Unity专属的,跟黑神话有什么关系?在查看USharp相关符号时,我发现一个令人震惊的事实,黑神话所使用的USharp和原版并不一样,提供了USharp运行模式的切换,其中包含了主流的mono、clr,以及il2cpp!
il2cpp
这些模式与设置都是原版USharp并不包含的内容,也就是说,项目组为了兼容性和性能做了大量优化和魔改,完全可以说基于USharp重新开发了一个C#的脚本方案,支持mono、clr和il2cpp。
那么既然Pak中没包含dll或者脚本,显然在Release版本中他们使用了il2cpp,所以,C#中定义的类型和各种符号都跑到了C++中,也就是最终的exe里面。这也是导致exe如此大的根本原因。
根据评论区的补充,黑神话的代码符号中除了il2cpp,还包含了Mono和UnityEngine的部分等dll,可以确定同时使用了Mono和Unity il2cpp的代码,但具体使用了哪种方式还没法确定。
C#符号
实际上如果打包成dll,可能只有几十MB,exe可以降到100MB,不过性能会略有损失。
总结
总结一下,黑神话的脚本方案是魔改的USharp,自己实现了mono/clr/il2cpp的运行模式,且支持全平台(PC/Mac/Linux/Android/iOS/PS5/XBox)。
此外,代码集成了Unreal.js但是貌似没用到,还使用了自研的某种状态机/行为树,配置表使用的是protobuf结构的二进制数据。