在 C++ 中操作 UE 属性

在 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。

  1. 取得 TinyFontName 变量对应的属性。在游戏中其真实类型是 FSoftObjectPath。UE4SS 确实提供 FSoftObjectPath 结构体,但本例假设没有提供。
  2. 取得与该属性对应的 UStruct。
  3. 取得指向 UEngine 实例中 TinyFontName 值的指针。
  4. 取得 FSoftObjectPath UStruct 中 SubPathString 的属性。
  5. 取得指向 FSoftObjectPath 结构体实例 TinyFontName 中 SubPathString 值的指针。这里指定模板参数,是因为已知它是 FString,而且 UE4SS 提供安全的 FString 实现。如果不提供模板参数,则返回 void*。
  6. 输出该值。

容器可以嵌套任意多层,因此结构体中再嵌套结构体也没有问题。例如:

// 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。

  1. 取得 FSoftObjectPath UStruct 中 AssetPath 变量的属性。在游戏中其真实类型是 FTopLevelAssetPath。
  2. 取得与该属性对应的 UStruct。
  3. 取得指向 FSoftObjectPath 结构体实例 TinyFontName 中 AssetPath 值的指针。
  4. 取得 FTopLevelAssetPath UStruct 中 AssetName 变量的属性。
  5. 取得指向 FTopLevelAssetPath 结构体实例 AssetPath 中 AssetName 值的指针。
  6. 输出该值。

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 许可