[游戏实时地图] 地图数据 | 兴趣点数据 | 虚幻引擎SDK接口
黑神话·悟空-内置实时地图
一个为《黑神话:悟空》设计的小地图插件,提供实时位置追踪和导航功能。
- GitHub 源代码: https://github.com/jaskang/wukong-minimap
更新日志
- v1.7
- 调整 UI
- 添加大量点位
- v1.6
- 修复 AMD 显卡渲染问题
- 添加点位
按键说明:
+
放大 小地图窗口-
缩小 小地图窗口Shift
++
放大 小地图比例Shift
+-
缩小 小地图比例0
显示/隐藏 地图
演示截图
安装说明
将 wukong-minimap.zip
直接解压至黑神话的安装文件夹下面的 b1\Binaries\Win64
中 (steam 的安装文件夹可以通过右键黑神话 -> 管理 -> 浏览本地文件找到)
本插件包含以下文件:
wukong_minimap.dll
插件功能核心文件dwmapi.dll
加载器 - 通过代理系统功能来加载 wukong_minimap.dllmaps
地图文件夹
使用 UE4SS 的用户
由于 ue4ss 自带的 dwmapi.dll
拦截了系统 api 会导致插件无法顺利加载,我们使用 wukong-minimap 中的 dwmapi.dll 就行了。
卸载
删除 wukong_minimap.dll
文件即可
学习点:rust跨语言操作cpp,通过调库接口
教程:wukong-minimap
本项目为游戏《黑神话:悟空》提供小地图覆盖层。
它通过钩取游戏渲染系统展示实时地图,显示玩家位置与朝向,以及从外部数据收集的各类兴趣点。
使用游戏内部*SDK*读取玩家信息,并通过*钩子基础设施*与图形API交互绘制覆盖层
。
视觉概览
章节列表
- 地图数据
- 兴趣点数据
- 虚幻引擎SDK接口
- 游戏状态
- 小地图渲染器
- 钩子基础设施
第一章:地图数据
欢迎来到wukong-minimap教程的第一章!
-
本章我们将通过理解插件如何识别游戏中的不同地图,探索小地图插件的基础构建模块。
-
想象你有
一叠
纸质地图,每张显示不同区域或城市。 -
要找到当前位置,首先需要确定
哪张
纸质地图对应当前位置。本插件的"地图数据"正是实现这一功能。
地图数据解决了什么问题?
小地图最基本的功能是展示当前位置在周边区域地图上的位置。但游戏世界庞大,可能包含多个关卡
、地牢或开放区域。插件如何知道何时显示哪张
地图?
这正是地图数据的作用!这是一组信息集合,告知插件游戏中每个特定区域对应的地图图像。
游戏世界可能如下结构:
每个位置(城镇广场、森林小径等)可能有专属地图。插件需要的数据应包含:
- “城镇广场"地图对应游戏区域"Level_Town_01”
- "黑暗洞穴"地图对应游戏区域"Level_Cave_01"特定XYZ坐标范围
地图数据建立了游戏内位置与地图图像的关联。
地图数据结构
插件通过MapInfo
结构体存储每个地图信息(见src/utils.rs
):
// src/utils.rs(简化版)#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Area {pub start: [f32; 3], // X,Y,Z最小坐标pub end: [f32; 3], // X,Y,Z最大坐标
}#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MapInfo {pub key: String, // 唯一标识符(常为图片文件名)pub level: String, // 所属游戏关卡名称pub range: Area, // 主地理边界(游戏坐标系)pub scale: f32, // 游戏坐标与图像像素比例pub areas: Vec<Area>, // 可选:主范围内的子区域pub url: String, // 地图图像文件路径
}
关键字段解析:
key
:地图条目唯一名称(如"forest_map.webp")level
:游戏引擎内部关卡名(如"Town_Level_01")range
:3D游戏世界的矩形边界框areas
:关卡内具体子区域(如城镇中的建筑)url
:地图图像文件相对路径
插件启动时通过load_data()
加载数据:
// src/utils.rs(简化版)pub fn load_data() -> Vec<MapInfo> {let data = include_str!("./../includes/data.json"); // 从嵌入式文件读取let maps: Vec<MapInfo> = serde_json::from_str(data).unwrap(); // 解析JSONmaps // 返回地图列表
}
地图匹配逻辑
插件通过以下流程确定当前适用地图:
核心匹配逻辑见src/render.rs
:
// src/render.rs(简化版)impl MiniMap {fn update_map(&mut self) -> Option<MapInfo> {self.game = wukong::game_state(); // 获取游戏状态self.maps.iter().rev().find(|map| {// 检查关卡名称匹配self.game.level == map.level &&// 检查坐标是否在主范围self.game.x >= map.range.start[0] && ... // 检查子区域(如有)map.areas.is_empty() || map.areas.iter().any(|area| ... )}).cloned()}
}
总结
地图数据是wukong-minimap插件理解游戏世界布局的基础,通过:
- 定义游戏位置与地图图像的关联
- 使用三维坐标范围精确定位
- 支持多层次区域划分
下一章我们将探讨如何通过**兴趣点数据**标记地图上的关键位置。
第二章:兴趣点数据
欢迎回来!
-
在第一章:地图数据中,我们学习了wukong-minimap插件如何根据玩家在游戏世界中的位置确定显示哪个地图图像。
-
现在既然已显示正确的地图,我们需要知道地图上有什么内容!
-
想象您正在使用手机地图应用。它不只显示道路,还会显示餐厅、地标、商店和其他重要地点。这些就是"兴趣点"。
兴趣点数据解决了什么问题?
游戏中充满了重要位置:下一任务点、强大BOSS所在地、隐藏宝藏点或材料采集点。在空白地图上仅显示玩家位置对寻找这些目标并无帮助。
**兴趣点数据(POI Data)**是告知插件地图上特定重要位置的信息集合。通过该数据,插件能绘制以下图标:
- 需要击败的BOSS
- 快速传送点
- 特殊收集品(葫芦、酒食、仙丹)
- 活动点(如打坐点)
若无POI数据,小地图仅是带玩家标记的图片。有了它,则成为实用的导航工具
。
兴趣点的定义
在本插件中,兴趣点是通过以下要素表示的简单概念:
- 位置:3D游戏世界坐标(X, Y, Z)
- 名称:描述性标签(如"森林BOSS"、“隐藏宝藏”)
- 类别:决定图标类型的分类(如"boss"、"baoxiang"宝箱、"teleport"传送)
代码中通过src/utils.rs
的Point
结构体表示:
// src/utils.rs(简化版)#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Point {pub name: String,pub category: String, // 决定图标类型pub x: f32, // 游戏世界X坐标pub y: f32, // 游戏世界Y坐标pub z: f32, // 游戏世界Z坐标
}
数据加载机制
插件启动时通过src/utils.rs
的load_points()
函数加载兴趣点数据:
// src/utils.rs(简化版)pub fn load_points() -> HashMap<String, Vec<Point>> {let data = include_str!("./../includes/data_points.json");let points: HashMap<String, Vec<Point>> = serde_json::from_str(data).unwrap();points // 返回按关卡分组的兴趣点
}
数据存储结构说明:
- 哈希映射键:游戏关卡名(如"Level_Forest_01")
- 哈希映射值:该关卡所有兴趣点的
Point
结构体列表
插件通过MiniMap
结构体的points
字段存储数据:
// src/render.rs(MiniMap结构体部分)pub struct MiniMap {// ... 其他字段 ...points: HashMap<String, Vec<Point>>, // 按关卡分组的兴趣点// ... 其他字段 ...
}
兴趣点渲染流程
- 确定当前地图:使用第一章逻辑获取匹配玩家位置的
MapInfo
- 获取关联兴趣点:根据
MapInfo.level
从points
哈希映射提取当前关卡所有兴趣点 - 区域过滤:通过
is_in_map()
函数筛选出当前地图范围内的兴趣点 - 图标选择:根据
point.category
匹配预加载的图标纹理 - 坐标转换:结合
MapInfo.range
和scale
将游戏坐标转换为屏幕坐标 - 图形绘制:在地图背景上绘制图标
核心渲染代码段(src/render.rs
):
// src/render.rs(简化版)if let Some(map) = self.map.as_ref() {self.points.get(map.level.as_str()).unwrap_or(&vec![]).iter().filter(|point| is_in_map(point, &map)).for_each(|point| {let icon = match point.category.as_str() {"teleport" => self.textures.teleport.id,"boss" => self.textures.boss.id,// ... 其他类别处理 ..._ => None,};if let Some(id) = icon {// 坐标转换与绘制逻辑draw_list.add_image(id, ...).build();}});
}
区域验证函数:
// src/utils.rs(简化版)pub fn is_in_map(point: &Point, map: &MapInfo) -> bool {if map.areas.is_empty() {point.x >= map.range.start[0] && ... // 主区域验证} else {map.areas.iter().any(|area| ... ) // 子区域验证}
}
总结
-
兴趣点数据通过精准的坐标绑定和分类系统,将游戏内关键位置可视化为导航图标。
-
结合地图数据的位置过滤机制,实现了动态的关卡兴趣点加载与渲染。
-
下一章将深入解析如何通过**虚幻引擎SDK接口**获取实时游戏状态。
第三章:虚幻引擎SDK接口
在前两章中,我们探讨了wukong-minimap插件的静态数据基础:
- 第一章:地图数据揭示了
不同区域对应的地图图像
- 第二章:兴趣点数据则解析了地图上的
关键标记
。然而这些数据都是静态的(运行时不会改变) - 插件需要获取游戏实时动态信息才能发挥作用——这正是虚幻引擎SDK接口的核心使命。
SDK接口解决的问题
-
想象
游戏如同精密运转的机械系统,而我们的插件则是需要与之交互的外部设备
。 -
如何询问"主齿轮位置"或"引擎转速"?
虚幻引擎SDK接口正是实现这种跨语言(Rust插件与C++游戏引擎)、跨进程通信的关键桥梁
其核心功能可概括为:
- 解析引擎结构:通过
逆向工程
生成的SDK代码,识别游戏对象(如玩家角色、关卡实例)的类定义与内存布局 - 实时数据捕获:
获取玩家坐标
(X/Y/Z)、朝向角度、当前关卡名称等动态信息 - 引擎功能调用:控制鼠标显隐、调整输入模式等
交互操作
。
核心应用场景:玩家状态捕获
插件最频繁的SDK调用是获取玩家实时状态,数据结构定义如下(b1sdk/src/b1sdk.h
):
// b1sdk/src/b1sdk.h(简化版)
struct PlayerInfo {float x, y, z; // 三维坐标float angle; // 朝向角度uint8_t bShowMouse; // 鼠标显隐状态char level[256]; // 当前关卡名
};
该结构通过FFI(外部函数接口)在C++与Rust间传递,需确保内存布局一致。
插件与SDK的交互架构
关键步骤解析
- Rust FFI调用:通过
b1sdk_sys
模块触发C++函数getPlayerInfo()
。实现跨语言操作 - 引擎对象获取:
- 使用
SDK::UWorld::GetWorld()
获取游戏世界实例 - 通过
UGameplayStatics
获取玩家控制器(PlayerController)
- 使用
- 数据采集:
// 获取玩家坐标 SDK::FVector loc = playerCharacter->K2_GetActorLocation(); // 获取关卡名称 std::string level = GameplayStatics->GetCurrentLevelName();
- 状态控制:
// 设置鼠标显隐(示例代码) playerController->bShowMouseCursor = show ? 1 : 0; SDK::UGSE_EngineFuncLib::SetInputModeUIOnly(...);
SDK代码生成机制
逆向工程
工具通过分析游戏内存结构,自动生成包含数千个类/函数的SDK代码库,例如:
// SDK/CoreUObject_functions.cpp(片段)
class UObject* FindObjectFastImpl(const std::string& Name) {// 遍历GObjects全局数组匹配对象
}
这些生成代码为插件提供了与虚幻引擎交互的"指令手册"
总结
虚幻引擎SDK接口通过三层架构实现数据贯通:
- 生成层:逆向工程产出SDK定义文件(
.h
/.cpp
) - 适配层:C++包装代码(
b1sdk
)调用SDK函数 - 应用层:Rust插件通过FFI获取结构化数据,实现插件操作
此设计使得插件既能实时捕获玩家位置(用于地图渲染),又能控制游戏交互状态(如鼠标锁定)。下一章将深入探讨如何利用这些数据构建完整的**游戏状态系统**。