)
游戏开发【免费下载链接】wukong-minimap黑神话内置实时地图 / Black Myth: Wukong Built-in real-time map项目地址https://gitcode.com/gh_mirrors/wu/wukong-minimap点击查看免费下载本文是hudhook官方教程《Writing the DLL code》的中文深度解读面向需要为《黑神话悟空》wukong-minimap等 Windows 游戏/应用编写内置 Overlay 的 Rust 开发者。通过本文你将掌握如何设计 DLL 内部状态、实现ImguiRenderLooptrait 并在每一帧绘制dear imgui界面同时理解该渲染循环在 DX12 宿主进程中的真实调用机制最终把这份知识直接复用到 wukong-minimap 的 MiniMap 渲染实现 中。一、理解渲染循环在 Overlay 架构中的位置hudhook是一个用于创建游戏内置 Overlay 的 Rust 库库文档它通过 detour 目标应用渲染函数例如IDXGISwapChain::Present的方式在宿主每渲染一帧时插入自定义绘制逻辑再交还控制权。在完整的注入流程中一个可注入 DLL 的生命周期如下编写渲染循环代码本文主题实现ImguiRenderLoop定义每一帧要画什么编写入口点writing the entry point通过hudhook!宏或手写DllMain把渲染循环挂到目标 Hook 类型上注入injecting a library由独立的注入器把 DLL 注入目标进程。因此render方法就是你所有游戏内 UI 逻辑的总入口——wukong-minimap 的小地图绘制、点位渲染、按键处理全部发生在这里。二、用状态结构体承载 DLL 数据原教程的第一步是设计一个结构体保存 DLL 的状态数据。以官方hello-hud示例完整源码为例我们需要记录DLL 被注入的时刻use std::time::Instant; struct HelloHud { start_time: Instant, } impl HelloHud { fn new() - Self { Self { start_time: Instant::now() } } }要点start_time在new()中被初始化为当前时刻之后在渲染中反复读取elapsed()即可得到注入后经过的时间这个结构体就是你的插件全局状态容器。在 wukong-minimap 中等价的MiniMap结构体src/render.rs#L94-L107承载了手柄输入、纹理集合、地图数据、玩家游戏状态等全部插件数据并在MiniMap::new()中完成初始化src/render.rs#L109-L164。三、实现 ImguiRenderLoop trait编写每一帧的渲染代码状态就绪后需要把渲染逻辑提供给hudhook。为此引入ImguiRenderLooptrait 并在自己的结构体上实现它use hudhook::hooks::ImguiRenderLoop; use imgui::*; impl ImguiRenderLoop for HelloHud { fn render(mut self, ui: mut Ui) { ui.window(##hello) .size([320., 200.], Condition::Always) .build(|| { ui.text(Hello, world!); ui.text(format!(Elapsed: {:?}, self.start_time.elapsed())); }); } }代码逐段解读ImguiRenderLoop是hudhook暴露给用户的渲染接口hudhook会每帧把imgui::Ui对象交给你的render方法你只负责实现 UI 绘制无需关心 D3D12 资源管理ui.window(##hello)创建一个名为hello的窗口。imgui 的命名以##开头的部分不会显示在标题栏因此窗口无可见标题.size([320., 200.], Condition::Always)固定窗口尺寸为 320×200Condition::Always表示每次渲染都强制应用该尺寸.build(|| { ... })是 imgui 的 RAII 构建模式闭包内的代码才真正向绘制列表提交控件format!(Elapsed: {:?}, ...)利用Debug格式化输出Instant的时长。这正是 wukong-minimap 渲染代码的原型它的 ImguiRenderLoop 实现 在render中根据按键状态决定绘制主地图render_mainmap或小地图render_minimap并用draw_list.add_image_rounded、add_image_quad等 API 绘制地图贴图、点位图标与玩家方向箭头。四、深入 trait 内部渲染循环的完整生命周期仅实现render就足以工作但源码揭示了ImguiRenderLoop的全部生命周期钩子trait 定义共五个方法默认实现均为空方法调用时机典型用途initialize(mut self, ctx, render_context)首次触发 Hook 时调用一次初始化 imgui 上下文样式、加载纹理before_render(mut self, ctx, render_context)每帧渲染 UI 之前更新纹理内容、刷新数据状态render(mut self, ui)每帧调用构建并绘制 UIon_wnd_proc(self, hwnd, umsg, wparam, lparam)窗口过程被调用时处理自定义窗口消息message_filter(self, io) - MessageFilter查询消息过滤返回不希望透传给主窗口的消息类型其中RenderContext定义见 lib.rs#L152-L168提供两个纹理管理接口load_texture(mut self, data, width, height) - ResultTextureId, Error加载纹理并返回TextureId应放在initialize中调用replace_texture(mut self, texture_id, data, width, height)向已有纹理上传新图像内容适合在before_render中做动态更新。wukong-minimap 就是这两个钩子的典型使用者在 initialize 中设置io.mouse_draw_cursor false、窗口圆角等 imgui 样式并通过load_textures!宏一次性把地图、边框、点位图标等 20 张纹理上传到 GPU在 before_render 中调用update_map()判断玩家是否已进入新地图区域若切换了地图则用render_context.replace_texture(...)把新的 4096×4096 地图图源写入已注册的纹理再更新self.map状态供render使用。五、底层视角render 方法是如何被每帧调用的掌握调用方能帮助你理解渲染循环的执行时机与线程环境。以 wukong-minimap 使用的 DirectX 12 Hookvendor/hudhook/src/hooks/dx12.rs为例ImguiDx12Hooks共 detour 了三个函数IDXGISwapChain3::PresentIDXGISwapChain3::ResizeBuffersID3D12CommandQueue::ExecuteCommandLists调用链如下dx12.rs#L193-L219宿主每帧调用IDXGISwapChain3::Present时被 detour 到dxgi_swap_chain_present_impl该实现通过INITIALIZATION_CONTEXT记录 swap chain并尝试通过ID3D12CommandQueue::ExecuteCommandLists关联 command queue凑齐两者后初始化渲染管线PipelineD3D12RenderEnginedx12.rs#L145-L171管线初始化完成后render()取出当前后缓冲swap_chain.GetBuffer(...)执行pipeline.render(target)——你的ImguiRenderLoop::render就是在这条路径中被调用的渲染完成后调用原始Presenttrampoline 把画面交还给宿主并在检测到eject()请求时执行卸载dx12.rs#L214-L216。此外ResizeBuffers被 Hook 是为了在窗口尺寸变化时同步重建渲染目标ExecuteCommandLists被 Hook 则是为了拿到与 swap chain 关联的 command queue 指针通过扫描 swap chain 对象内存中可读的指针区域来匹配dx12.rs#L113-L137。六、把渲染循环接到 DLL 入口点上写完render后还需把渲染循环挂到 DLL 入口点。最简单的做法是使用hudhook!宏宏实现见 lib.rs#L467-L495use hudhook::hooks::dx12::ImguiDx12Hooks; hudhook::hudhook!(ImguiDx12Hooks, HelloHud::new());宏会展开为一个标准的DllMain在收到DLL_PROCESS_ATTACH时自动在独立线程中通过Hudhook::builder().with::ImguiDx12Hooks(...).with_hmodule(...).build().apply()初始化并启用全部 Hook。如果你的渲染循环需要配合其他渲染 API可把第二个参数替换为对应 Hook 类型use hudhook::hooks::dx9::ImguiDx9Hooks; // DirectX 9 use hudhook::hooks::dx11::ImguiDx11Hooks; // DirectX 11 use hudhook::hooks::opengl3::ImguiOpenGl3Hooks; // OpenGL 3若需要在入口点里执行更多自定义逻辑例如初始化日志、设置 panic 钩子也可以完全手写DllMain并使用Hudhook::builder()构建管线完整写法见 writing the entry point。wukong-minimap 正是采用手写DllMain的方案src/lib.rs#L13-L50它在DLL_PROCESS_ATTACH中先初始化 tracing 日志与 panic 钩子然后延迟 10 秒等待游戏场景就绪再以.with::ImguiDx12Hooks(render::MiniMap::new())构建并应用 Hook。七、编译与验证确保项目按 DLL 方式构建前置步骤详见 project setupCargo.toml需包含[lib] crate-type [cdylib, rlib] name hello_hud然后编译cargo build --release产物为target/release/hello_hud.dll可直接由注入器注入目标进程注入器编写方法见 injecting a library。对照仓库中完整的 hello-hud 示例工程包含 lib.rs 与注入器 main.rs可以端到端跑通写 DLL → 编译 → 注入 → 游戏内出现窗口的完整流程。八、实践建议与注意事项在正确线程初始化资源initialize与before_render都发生在渲染线程中纹理上传等 GPU 相关操作应放在这里而非在任意线程中执行保持 render 方法精简render每帧执行避免在其中做耗时的文件 I/O 或阻塞操作wukong-minimap 把地图切换的贴图替换放在before_render把输入事件轮询放在render开头src/render.rs#L583-L636值得参考状态结构体需满足线程约束hudhook!宏与Hudhook::builder().with(...)都要求渲染循环实现Send Sync static内部状态需用Mutex等同步原语保护参见 lib.rs#L425-L431优雅退出需要卸载时调用hudhook::eject()它会在下一次 Hook 触发时禁用全部 Hook、释放控制台并卸载 DLLlib.rs#L209-L251了解库的成熟度库作者在文档中明确提示hudhook包含大量 FFI 与unsafe代码仍待充分测试与审计用于游戏 Mod 等小型项目通常没有问题但存在导致宿主应用崩溃的可能——这正是 wukong-minimap 在 src/lib.rs#L23-L35 设置 panic 钩子把崩溃信息写入日志的原因。至此你已经掌握了 hudhook DLL 渲染循环的完整编写方法从状态结构体、ImguiRenderLoop实现到生命周期钩子与 DX12 底层调用链再到入口点接线与编译验证。下一步可以阅读 proxy-dll 了解无需外部注入器的 DLL 代理加载技术——wukong-minimap 的dwmapi.dll加载器正是这一思路的实践。赞分享游戏开发【免费下载链接】wukong-minimap黑神话内置实时地图 / Black Myth: Wukong Built-in real-time map项目地址https://gitcode.com/gh_mirrors/wu/wukong-minimap点击查看免费下载相关推荐为 hudhook 钩子库编写 DLL 入口点从 hudhook! 宏到手写 DllMain 的完整指南附 wukong-minimap 实战为 hudhook 钩子库编写 DLL 入口点从 hudhook! 宏到手写 DllMain 的完整指南附 wukong minimap 实战 导读 本文游戏开发Jspreadsheet v3 事件系统实战指南绑定回调、参数签名与数据驱动联动Jspreadsheet v3 事件系统实战指南绑定回调、参数签名与数据驱动联动 导读 Jspreadsheetv3 时代亦以 jexcel 为入口内置了游戏开发快速给 Windows 系统瘦身Win11Debloat 一键优化完整指南快速给 Windows 系统瘦身Win11Debloat 一键优化完整指南 刚装好的 Windows 里Clipchamp、3D Builder、一堆 Bi游戏开发上一篇DisUnity 项目常见问题解决方案下一篇【项目推荐】AsyncElegantOTA过去优雅地进行ESP8266与ESP32的OTA升级方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考