在 C++ 中操作 UE 属性
制作 Mod 时,很可能需要读写 UE 属性。传统但不推荐的方式,是自行定义与游戏内存匹配的结构体,或手动给基址指针加上偏移。这两种方式都容易因游戏或引擎更新而失效。
更好的方法是使用我们为 C++ Mod 提供的 API。这些 API 不依赖编译期已知的结构体,也不依赖容易过时的固定偏移,而是利用每个 UE 游戏都提供的元数据。
模板 API
我们提供模板函数,方便取得所需类型。这是访问属性值最简单的方法。
UObject::GetValuePtrByPropertyNameInChain
此函数返回指向属性值的指针。它会沿整个类继承层级查找 PropertyName,因此比不带 InChain 的版本更推荐。
// For this exmaple, assume that this UObject* is correctly retrieved in some way, and isn't nullptr.
UObject* PlayerController{};
FVector* SpawnLocation = PlayerController->GetValuePtrByPropertyNameInChain<FVector>(STR("SpawnLocation"));
Output::send(STR("{} {} {}\n"), SpawnLocation->X(), SpawnLocation->Y(), SpawnLocation->Z());
代码注释译文:假设此 UObject* 已通过某种方式正确获取,并且不是 nullptr。
UObject::GetValuePtrByPropertyName
与 UObject::GetValuePtrByPropertyNameInChain 相同,但只检查最内层的类。例如,对于 APlayerController,它只检查 APlayerController 内部,不检查 AController 或任何其他父类。
访问 UStruct 的值
不建议自行创建模拟游戏结构体的 C++ 结构体。应使用 UE 元数据,分别获取各成员的指针。这样既能避免代码随游戏更新而过时,也更容易,因为无需逆向工程。
我们提供了 FVector、FRotator 等一些不会随游戏变化而失效的结构体,但大多数结构体并不提供。
下面的示例展示如何从 UObject 内部的结构体取得数值。
// Retrieving: Engine->TinyFontName.SubPathString
// For this example, assume that this UObject* is correctly retrieved in some way, and isn't nullptr.
UObject* Engine{};
// 1. Retrieve the property for the TinyFontName variable.
// The real type as far as the game is concerned is FSoftObjectPath.
// We do provide an 'FSoftObjectPath' struct in UE4SS, but let's pretend that we don't.
auto TinyFontNameProperty = static_cast<FStructProperty*>(Engine->GetPropertyByNameInChain(STR("TinyFontName")));
// 2. Retrieve the UStruct that corresponds with the property.
UStruct* SoftObjectPath = TinyFontNameProperty->GetStruct();
// 3. Retrieve a pointer to the TinyFontName value inside the UEngine instance.
auto TinyFontName = TinyFontNameProperty->ContainerPtrToValuePtr<void>(Engine);
// 4. Retrieve the property for the SubPathString in the FSoftObjectPath UStruct.
FProperty* SubPathStringProperty = SoftObjectPath->GetPropertyByNameInChain(STR("SubPathString"));
// 5. Retrieve a pointer to the SubPathString value inside the FSoftObjectPath struct instance 'TinyFontName'.
// Note that we're supplying a template parameter here because we know that it's an FString, and UE4SS has a safe FString implementation.
// If you don't supply a template parameter, it will return a void* instead.
auto SubPathString = SubPathStringProperty->ContainerPtrToValuePtr<FString>(TinyFontName);
// 6. Output the value.
Output::send(STR("TinyFontName: {}\n"), SubPathString->GetCharArray());
代码注释译文:要取得的是 Engine->TinyFontName.SubPathString。假设 Engine 已正确取得,且不是 nullptr。
- 取得
TinyFontName变量对应的属性。在游戏中其真实类型是FSoftObjectPath。UE4SS 确实提供FSoftObjectPath结构体,但本例假设没有提供。 - 取得与该属性对应的 UStruct。
- 取得指向 UEngine 实例中
TinyFontName值的指针。 - 取得
FSoftObjectPathUStruct 中SubPathString的属性。 - 取得指向
FSoftObjectPath结构体实例TinyFontName中SubPathString值的指针。这里指定模板参数,是因为已知它是 FString,而且 UE4SS 提供安全的 FString 实现。如果不提供模板参数,则返回void*。 - 输出该值。
容器可以嵌套任意多层,因此结构体中再嵌套结构体也没有问题。例如:
// Retrieving: Engine->TinyFontName.AssetPath.AssetName
// Continuing from the above example.
// 7. Retrieve the property for the AssetPath variable in the FSoftObjectPath UStruct.
// The real type as far as the game is concerned is FTopLevelAssetPath.
auto AssetPathProperty = static_cast<FStructProperty*>(SoftObjectPath->GetPropertyByNameInChain(STR("AssetPath")));
// 8. Retrieve the UStruct that corresponds with the property.
auto TopLevelAssetPath = AssetPathProperty->GetStruct();
// 9. Retrieve a pointer to the AssetPath value inside the FSoftObjectPath struct instance 'TinyFontName'.
auto AssetPath = AssetPathProperty->ContainerPtrToValuePtr<void>(TinyFontName);
// 10. Retrieve the property for the AssetName variable in the FTopLevelAssetPath UStruct.
auto AssetNameProperty = TopLevelAssetPath->GetPropertyByNameInChain(STR("AssetName"));
// 11. Retrieve a pointer to the AssetName value inside the FTopLevelAssetPath struct instance 'AssetPath'.
auto AssetName = AssetNameProperty->ContainerPtrToValuePtr<FName>(AssetPath);
// 12. Output the value.
Output::send(STR("AssetName: {}\n"), AssetName->ToString());
代码注释译文:继续上例,取得 Engine->TinyFontName.AssetPath.AssetName。
- 取得
FSoftObjectPathUStruct 中AssetPath变量的属性。在游戏中其真实类型是FTopLevelAssetPath。 - 取得与该属性对应的 UStruct。
- 取得指向
FSoftObjectPath结构体实例TinyFontName中AssetPath值的指针。 - 取得
FTopLevelAssetPathUStruct 中AssetName变量的属性。 - 取得指向
FTopLevelAssetPath结构体实例AssetPath中AssetName值的指针。 - 输出该值。
ContainerPtrToValuePtr 始终返回指向 T 的指针。上例中 T 为 FName,因此返回 FName*。由于返回值始终是指针,修改属性值非常简单:
// Continuing from the above example.
// 13. Set the value of 'AssetName' to "THIS IS A TEST" by dereferencing the pointer and setting the value to a new FName.
*AssetName = FName(STR("THIS IS A TEST"), FNAME_Add);
// 14. Output the new value.
Output::send(STR("AssetName: {}\n"), AssetName->ToString());
代码注释译文:继续上例。第 13 步,解引用指针,并赋予一个新的 FName,将 AssetName 设为 THIS IS A TEST;第 14 步,输出新值。
原文:docs/guides/accessing-ue-properties-c++.md · 固定提交 560411f18512 · 来源与 MIT 许可