jlucaso1/unturned-godot

GitHub: jlucaso1/unturned-godot

一个在 Godot 4 中从零解析并还原 Unturned 真实地图数据的实验性引擎移植项目。

Stars: 0 | Forks: 0

# unturned-godot ![CI](https://raw.githubusercontent.com/jlucaso1/unturned-godot/main/../../actions/workflows/ci.yml/badge.svg) 一项实验:直接从你的 Steam 安装目录加载真实的 [Unturned](https://store.steampowered.com/app/304930/Unturned/) 地图(地形、物体、植被、道路、光照、音频、角色、僵尸),并在 [Godot 4.7](https://godotengine.org/) (.NET / C#) 中运行它。每个文件格式都是从零开始重新实现的,并使用 [U3-SDK](https://github.com/SmartlyDressedGames/U3-SDK) 作为每个格式序列化方式的参考,与游戏自身的数据逐字节进行核对。 ## 目前已实现的功能 | 区域 | 状态 | |---|---| | **地形** | 地形高度图 + Splatmap,按图层混合的真实图层纹理(`Terrain/Materials.unity3d`),物理材质 | | **物体** | 所有放置的物体和树木,带有真实的网格、材质和纹理,通过基于 GUID 的碰撞进行流式传输 | | **植被** | 将 `Foliage.blob` 中的草、花和鹅卵石作为分块的 MultiMesh 渲染(在 PEI 中约有 66.7 万个实例) | | **道路 / 水** | 通过移植 `Road.buildMesh` 放样的贝塞尔样条线,真实的道路纹理;来自地图光照的海平面 | | **光照** | 由地图的 `Lighting.dat` 关键帧驱动的日夜循环:太阳、环境光、雾气,以及移植的 skybox(太阳光盘、星星、月相、云层) | | **玩家** | 移植了 `PlayerMovement`/`PlayerLook`/`PlayerStance`,并使用了游戏自身的常量;真实的角色模型、骨骼和动画;支持第一/第三人称 | | **音频** | 通过像 `PhysicsTool.GetTerrainMaterialName` 一样的地形 splat 解析的脚步声/落地声,从 master bundle 的 FSB5 banks 中提取的声音片段 | | **僵尸** | 生成表、导航边界和预烘焙的 navmeshes;检测、狩猎和 `Zombie.cs` 动画集 | | **多人游戏** | 基于 UDP 的权威服务器 + 快照插值客户端;单人游戏是在回环地址上运行的相同技术栈。监听服务器、专用服务器和通过地址加入均可正常工作 | 实现目标的顺序是**先保证一致性,再追求性能**,并且有一个性能 HUD(`F3`)随时可用,以便时刻关注运行数据。 ### 尚未实现 - **压缩 / 流数据网格**:PEI 不需要(那里没有物体使用它们),因此 `m_CompressedMesh` 和 `.resS` 中的顶点数据未被解码;此类网格会回退显示为方块。(不过纹理像素*确实*会从 `.resS` 中读取。) - **游戏玩法**:没有物品、库存、载具、建造、伤害或生存数据。僵尸存在并且会狩猎,但你无法反击。 - **PEI 以外的地图**:只要其他地图使用相同的功能,它们就可以加载;PEI 是唯一经过验证的地图。 ## 环境要求 - **Godot 4.7, .NET/Mono 版本**([下载](https://godotengine.org/download))。普通版本无法运行 C#。 - **.NET SDK 10**:用于测试套件和独立工具。 - **Unturned,通过 Steam 安装**:Linux、Windows 或 macOS。项目会自动找到它(包括通过 `libraryfolders.vdf` 找到额外的 Steam 库驱动器);如果它位于不寻常的位置,请使用 `UNTURNED_PATH` 覆盖。 不需要其他任何东西:master bundle、地图和资源都将直接从该安装目录中读取。 ## 运行 在 Godot 编辑器中打开项目并按下播放键,或者在终端中运行: ``` # Linux / macOS: 将 GODOT 指向你的 Godot 4.7 .NET 二进制文件 GODOT=/usr/bin/godot-mono "$GODOT" # windowed, boots to the main menu "$GODOT" --headless # load + validate the data, then exit SCREENSHOT_PATH=/tmp/pei.png "$GODOT" --resolution 1600x900 # render one frame to a PNG ``` ``` # Windows (PowerShell) $env:UNTURNED_PATH = "D:\SteamLibrary\steamapps\common\Unturned" # only if autodetection misses & "C:\Godot\Godot_v4.7-stable_mono_win64.exe" ``` 首次启动会将模型、纹理和音频从 master bundle 中提取到 Godot 的 `user://` 缓存中。这只需几分钟时间;后续运行将直接从缓存启动。 **控制**(Unturned 自身的默认设置,来自 `PlayerSettings`):`WASD` 移动,鼠标视角,`Space` 跳跃,`Shift` 冲刺,`X` 蹲下,`Z` 趴下,`H`(或 `F5`)切换第一/第三人称,`Esc` 暂停。在自由视角模式下:`WASD` + `Q`/`E` 下降/上升,`Shift` 加速。`F3` 切换性能 HUD。 **多人游戏。** 主菜单的 *Connect* 可加入 `host:port` 服务器。若要创建主机,请开启一个会话并在暂停菜单中点击 *Open to LAN*,或者运行专用服务器: ``` "$GODOT" --headless -- --server --port=27015 ``` **有用的环境变量标志**(主要用于自动化和截图):`UNTURNED_PATH`, `SOLO=1`(直接启动本地会话),`FREECAM=1`,`JOIN=host:port`,`OPEN_LAN=1`,`PLAYER=1`,`SCREENSHOT_PATH`,`TIME_OF_DAY=0..1`,`DAY_SPEED=N`,`NAV_DEBUG=1`,`AUDIO_DEBUG=1`。 ### 导出 `export_presets.cfg` 包含写入到 `build/export//`(被 git 忽略)的 Linux、Windows 和 macOS 预设。请先在 Godot 编辑器中安装匹配的导出模板: ``` "$GODOT" --headless --export-release Linux "$GODOT" --headless --export-release "Windows Desktop" "$GODOT" --headless --export-release macOS ``` 如果你添加了任何导出密钥,它们会存放在 `export_credentials.cfg` 中,该文件会被 git 忽略,因此千万不要提交它。 ## 结构 | 项目 | 包含内容 | 引擎依赖 | |---|---|---| | `core/` (`UnturnedGodot.Core`) | 纯逻辑:二进制/文本解析器,地形数学计算,网络代码,僵尸 AI,资源解析。仅使用托管的 Godot 结构体。 | 无,可在 xUnit 下运行 | | `src/` (`unturned-godot`) | Godot 胶水代码:`Main`,世界构建器,UI,玩家/僵尸节点。`[ExcludeFromCodeCoverage]`。 | Godot.NET.Sdk | | `tests/` (`UnturnedGodot.Tests`) | xUnit 套件;对 `core/` 实现 100% 的行覆盖和分支覆盖。 | 无 | | `tools/PerfHarness` | 针对 Core 解析器的独立微基准测试。 | 无 | 保持解析器不依赖引擎是实现完整单元测试覆盖的关键。`core/`、`tests/` 和 `tools/` 包含 `.gdignore` 文件,因此 Godot 编辑器会忽略它们(它们通过 .NET SDK 构建)。 非 Godot 二进制文件(core + tests,Debug 和 Release 版本)会被输出到 `build///`,而不是散落在 `bin`/`obj` 中;游戏将其由 Godot 管理的输出保留在 `.godot/` 下。整个 `build/` 目录均被 git 忽略。 ### 内容是如何被读取的 `core/Unity/` 是一个从零开始编写的读取器,用于读取游戏的 `core_*.masterbundle`:UnityFS 容器,LZ4(自带解码器)+ LZMA (SharpCompress) 块,SerializedFile v22,基于 TypeTree 的对象读取器,网格(顶点通道、UV、子网格、蒙皮),材质(`_Color`/`_MainTex`)和 Texture2D (DXT1/DXT5/RGB/RGBA)。 该 bundle 是一个约 1.4 GB 的 LZMA 块,因此它只会被遍历**一次**(`ModelExtractor`):每个放置物体的 GUID 映射到其最高细节的 LOD 网格,并通过物体的 `MaterialPalette`,映射到每个子网格的扁平 `_Color`,以及(如果存在)来自 `.resS` 流的 `_MainTex` 纹理。网格、碰撞体和去重后的纹理会被缓存到 `user://` 下;后续运行仅加载地图所需的内容。已处理 `Bundle_Override_Path`(节日/变体重用)和外部网格引用。 ## 开发 ``` dotnet build unturned-godot.sln # game + core + tests dotnet test tests/UnturnedGodot.Tests.csproj # run the suite dotnet format unturned-godot.sln --verify-no-changes # lint (fails on style drift) dotnet format unturned-godot.sln # auto-format # 覆盖率(通过 coverlet.runsettings 排除源生成代码) dotnet test tests/UnturnedGodot.Tests.csproj --settings coverlet.runsettings ``` 代码风格和分析器通过 `.editorconfig` + `Directory.Build.props`(`EnableNETAnalyzers`,`EnforceCodeStyleInBuild`)强制执行;构建过程没有任何警告,且 CI 构建使用了 `-warnaserror`。 涉及真实游戏数据的测试在未安装 Unturned 时会自动跳过,因此测试套件在纯净的机器上也能显示为通过,这正是 CI 在 Linux、Windows 和 macOS 上运行的方式。 如果你的 Godot 来自发行版包,并且其确切的 `GodotSharp`/`Godot.NET.Sdk` 版本尚未在 nuget.org 上发布,请按机器注册本地的 nupkg 文件夹(切勿在仓库的 `nuget.config` 中注册,因为缺失的本地源会导致整个还原过程失败): ``` dotnet nuget add source /usr/lib/godot-mono/GodotSharp/Tools/nupkgs -n GodotLocal ``` 基准测试和性能分析:请参阅 [docs/PROFILING.md](docs/PROFILING.md)。 ## 许可证 代码使用 [MIT](LICENSE) 协议。Unturned 及其内容版权归 Smartly Dressed Games 所有。有关归属说明以及本项目包含和不包含的内容,请参阅 [NOTICE.md](NOTICE.md)。
标签:Godot, Unturned, 多人体追踪, 多人联机, 引擎移植, 游戏开发, 资源解析