前言
最近《黑神话:悟空》上线,第一时间下载体验了一下。然后很好奇他们用的是什么脚本方案,因为大型UE项目绝不可能纯C++开发,所以解包看了一下,也发现一点有意思的东西。
黑神话文件目录
其实上线之前,他们官方就发了一个免费的测试工具,实际上这个测试工具是包含完整的代码的,只是剔除了大部分游戏资源。因为项目组不可能有精力从头搞一个新工程来做测试工具,剔除资源比剔除代码容易一万倍,程序员应该都懂。
引擎版本
首先是引擎版本,大家都知道他们一开始是UE4,UE5首发之后他们就切换到UE5了,所以引擎版本是UE5.0.0。之后虽然Epic发布了更新的UE5.0.3等版本,但是项目组一般不会轻易升级的。可能只是移植一些需要的代码过来,毕竟换引擎的话,以前的大量魔改不好迁移。
游戏入口exe
代码段大小
有一点值得注意的是,exe的大小达到了859MB,这个代码段大小不太正常。参考同样是UE开发的游戏,三角洲行动端游140MB,极品飞车集结端游105MB。感觉可能是包含了太多没用的插件代码或者没剔除一些不必要的符号?
三角洲行动和极品飞车集结
技术总监
了解游戏科学背景的朋友们应该知道他们团队创始成员来自腾讯斗战神项目,他们现在的技术总监招文勇就是2010年加入腾讯量子工作室的,后来合并到光子工作室旗下。
文勇毕业于中山大学的软件工程系,2014年与冯骥、杨奇等斗战神项目组核心成员共同创立游戏科学,此前是《战争艺术:赤潮》的主程。
讲的是配置存储在Lua table,C#通过unsafe code访问,让配置只占一份内存并且保持高效率。
那时候他们用的是xLua,所以我以为他们选择脚本方案应该还是基于Lua的,比如sluaunreal,已经在吃鸡手游上验证过,或者后出的UnLua,有较好的维护。又或者是车神新出的PuerTS,不过考虑到黑神话立项比较早,应该不会用近两年兴起的PuerTS,切换成本会比较高。
要么是C#,不过目前市面上已有的一些C#方案要么没维护,并且基本不能跨平台,只支持Windows。
脚本方案
在查看代码符号时,我发现了大量V8的字段,所以理所当然的以为竟然用了PuerTS,觉得项目组还挺跟随潮流。但是却搜不到PuerTS相关的符号,转而搜索js,发现代码中包含了Unreal.js插件。
v8
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
pixeltris/USharp: C# plugin for Unreal Engine 4 (github.com)github.com/pixeltris/USharp
上图可以看到蓝图节点中大量使用了SharpClass,也就是USharp的自定义UBlueprintGeneratedClass,用于运行时将C#中的自定义UClass动态生成UE的类型。
USharp.uplugin
但是,貌似没这么简单,因为USharp不支持UE5,并且不支持主机和移动端,但是黑神话包内的uplugin文件包含了这些平台。我想黑神话官方对USharp进行了大量的魔改和兼容。
但到这里我还是没搞清楚黑神话到底用了什么脚本方案,因为Pak中并没有找到js、lua或者USharp的dll文件。可是明明代码中用了Unreal.js和USharp插件。
别着急,继续往下看。
il2cpp
提到il2cpp,Unity开发者们都很熟悉,为了解决mono的兼容性和性能等问题推出的方案:
IL2CPP Overview - Unity 手册 (unity3d.com)docs.unity3d.com/cn/2023.2/Manual/IL2CPP.html
但是这是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结构的二进制数据。
注:本文只是浅析黑神话的脚本方案,没有公开解包方法,不涉及资源破解,请勿用于非法用途。
剧透
不想知道结局的别继续看。
网上一直流传有多种结局,实际上根据解包结果来看,游戏中只有两种。
一个是大圣尸骸,另一个是四大天王+杨戬。
一是代号End_A02的大圣尸骸蓝图配置:
End_A02
二是代号End_B01的四大天王+杨戬蓝图配置:
End_B01