创建 C++ Mod
本指南介绍如何使用 UE4SS 创建 C++ Mod,分为四部分:第一部分说明先决条件;第二部分创建最基本的 C++ Mod;第三部分展示如何与 UE4SS 以及 UE 本身(通过 UE4SS)交互;第四部分说明 Mod 的安装。
本指南要求具备可用的 C++ 开发环境,并安装
CMake和git。最好采用与从源码构建 UE4SS 所需环境类似的配置。
第一部分
执行以下步骤前,务必安装 README 中列出的所有构建依赖!
- 创建 Epic 账号,并关联到 GitHub 账号。
- 检查邮箱,接受加入
@EpicGamesGitHub 组织的邀请,从而获得 Unreal 源码访问权限。 - 为 GitHub 账号配置 SSH 密钥,让 git 能访问前面步骤中取得权限的 Unreal 源码。
- 在计算机上创建目录,名称不限,本例命名为
MyMods。 - 克隆 RE-UE4SS 仓库,使路径为
MyMods/RE-UE4SS。 - 打开 CMD,进入
RE-UE4SS,执行git submodule update --init --recursive。 - 返回
MyMods目录并创建一个新目录,用于保存 Mod 源码。本例命名为MyAwesomeMod。 - 在
MyMods中创建CMakeLists.txt,写入以下内容:
cmake_minimum_required(VERSION 3.22)
project(MyMods)
add_subdirectory(RE-UE4SS)
add_subdirectory(MyAwesomeMod)
第二部分
- 在
MyMods/MyAwesomeMod中创建CMakeLists.txt,写入以下内容:
set(TARGET MyAwesomeMod)
add_library(${TARGET} SHARED
dllmain.cpp
)
target_include_directories(${TARGET} PRIVATE .)
target_link_libraries(${TARGET} PUBLIC UE4SS)
# Copy the DLL to the game's mod directory after building (optional, adjust path as needed)
# add_custom_command(TARGET ${TARGET} POST_BUILD
# COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${TARGET}> "path/to/game/Binaries/Win64/Mods/${TARGET}/dlls/"
# )
代码中的注释译文:构建完成后将 DLL 复制到游戏的 Mod 目录(可选,请根据需要调整路径)。
- 在
MyMods/MyAwesomeMod中创建dllmain.cpp,写入以下内容:
#include <stdio.h>
#include <Mod/CppUserModBase.hpp>
class MyAwesomeMod : public RC::CppUserModBase
{
public:
MyAwesomeMod() : CppUserModBase()
{
ModName = STR("MyAwesomeMod");
ModVersion = STR("1.0");
ModDescription = STR("This is my awesome mod");
ModAuthors = STR("UE4SS Team");
// Do not change this unless you want to target a UE4SS version
// other than the one you're currently building with somehow.
//ModIntendedSDKVersion = STR("2.6");
printf("MyAwesomeMod says hello\n");
}
~MyAwesomeMod() override
{
}
};
#define MY_AWESOME_MOD_API __declspec(dllexport)
extern "C"
{
MY_AWESOME_MOD_API RC::CppUserModBase* start_mod()
{
return new MyAwesomeMod();
}
MY_AWESOME_MOD_API void uninstall_mod(RC::CppUserModBase* mod)
{
delete mod;
}
}
代码中的注释译文:除非确实需要以某种方式面向与当前构建版本不同的 UE4SS 版本,否则不要修改 ModIntendedSDKVersion。
- 在命令提示符中进入
MyMods目录,执行以下任一方案。
A. 使用 Ninja 从命令行构建:
# Configure (Ninja is single-configuration)
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Game__Shipping__Win64
# Build (no --config needed for Ninja)
cmake --build build
注释译文:先配置(Ninja 为单配置生成器),再构建(Ninja 不需要 --config)。
或 B. 生成 Visual Studio 解决方案:
# Configure (MSVC is multi-configuration)
cmake -B build -G "Visual Studio 17 2022"
# Build from command line (MSVC requires --config)
cmake --build build --config Game__Shipping__Win64
注释译文:先配置(MSVC 为多配置生成器),再从命令行构建(MSVC 需要 --config)。
如果选择方案 B,VS 解决方案位于 build 目录。
- 在 Visual Studio 中打开
MyMods/build/MyMods.sln。 - 除非要调试,否则确保选择
Game__Shipping__Win64配置。 - 在解决方案资源管理器中找到项目(本例为 MyAwesomeMod),右键点击并选择
Build。
第三部分
这一部分将学习如何向文件及两个控制台写日志,按名称查找 UObject,并记录它的名称。
- 在
#include <Mod/CppUserModBase.hpp>下添加#include <DynamicOutput/DynamicOutput.hpp>。此时也可以删除#include <stdio.h>,因为接下来将移除唯一需要它的printf调用。 - 为节省时间、减少重复,并让代码更简洁,在所有 include 下添加:
using namespace RC;
- 将
MyAwesomeMod构造函数中的 printf 调用替换为:
Output::send<LogLevel::Verbose>(STR("MyAwesomeMod says hello\n"));
它比 printf 调用更长,但消息会同时发送到日志文件、普通控制台和 GUI 控制台,还可通过 LogLevel 枚举获得颜色支持。
- 在 DynamicOutput 的 include 下添加:
#include <Unreal/UObjectGlobals.hpp>
#include <Unreal/UObject.hpp>
- 再次使用
using namespace简化名称,在前一个声明下添加using namespace RC::Unreal;。 - 在 Mod 构造函数中使用
register_on_unreal_init注册on_unreal_init回调。完整构造函数如下:
MyAwesomeMod() : CppUserModBase()
{
ModName = STR("MyAwesomeMod");
ModVersion = STR("1.0");
ModDescription = STR("This is my awesome mod");
ModAuthors = STR("UE4SS Team");
// Do not change this unless you want to target a UE4SS version
// other than the one you're currently building with somehow.
//ModIntendedSDKVersion = STR("2.6");
register_on_unreal_init(&on_unreal_init);
printf("MyAwesomeMod says hello\n");
}
代码中的注释译文:除非需要面向不同于当前构建版本的 UE4SS 版本,否则不要修改 ModIntendedSDKVersion。
- 如下定义
on_unreal_init:
static auto on_unreal_init(CppModUserBase* self) -> void
{
// You are allowed to use the 'Unreal' namespace in this function and anywhere else after this function has fired.
auto Object = UObjectGlobals::StaticFindObject<UObject*>(nullptr, nullptr, STR("/Script/CoreUObject.Object"));
Output::send<LogLevel::Verbose>(STR("Object Name: {}\n"), Object->GetFullName());
}
代码中的注释译文:可以在此函数中,以及此函数触发之后的任何位置使用 Unreal 命名空间。
注意,Output::send 不要求传入 LogLevel,并且格式字符串使用 {} 而非 %s。Output::send 的底层使用 std::format,如需了解更多,请查阅 std::format 或 libfmt。
- 右键点击项目,选择
Build。
译注:本节示例按原文保留。原文在替换
printf后给出的完整构造函数仍包含printf,且回调示例参数类型写为CppModUserBase,与前文CppUserModBase拼写不同。实际编译时请核对目标 UE4SS 版本的声明并相应调整。
第四部分
原文:docs/guides/creating-a-c++-mod.md · 固定提交 560411f18512 · 来源与 MIT 许可