Skip to main content

UE类型系统

· 3 min read

问题引入

工作中遇到一个工具链任务,需要把一些蓝图的属性值,复制到对应名称的自定义DataAsset中,在获取蓝图资源后拿到了UBlueprint* BP,然后遍历Copy他的属性,

for (TFieldIterator<FProperty> PropIt(BP->GenerateClass); PropIt; ++PropIt)
{
FProperty* Property = *PropIt;
FProperty* DataAssetProperty = FindFProperty<FProperty>(DataAssetClass, Property->GetFName());
if (DataAssetProperty)
{
// Copy the value from the blueprint property to the DataAsset property
void* BlueprintPropertyValue = Property->ContainerPtrToValuePtr<void>(BP);
void* DataAssetPropertyValue = DataAssetProperty->ContainerPtrToValuePtr<void>(DataAssetInstance);
Property->CopyCompleteValue(DataAssetPropertyValue, BlueprintPropertyValue);
}
}

此时会崩溃,debug发现ContainerPtrToValuePtr中Assert Error, alt text

最终定位到问题所在 UBlueprint* BP是个资产类,他可能继承了一个自定义的类比如UWeapon, 此时BP->GenerateClass()得到的是这个蓝图生成的cpp类,Name为"BlueprintName_C",遍历这个类得到属性FProperty* Property = TFieldIterator<FProperty> PropIt(BP->GenerateClass), 此时Property->GetOwner<UClass>()的Name为蓝图真正继承的类UWeapon,而BP->GetClass()就是"Blueprint".两者自然不是IsA关系。只有在蓝图BP被真正实例化的时候,才会转为蓝图继承的那个类UWeapon

因此用下面的代码就可以实例化

UClass* BlueprintClass = InBluerprint->GeneratedClass;
UObject* BlueprintInstance = NewObject<UObject>(GetTransientPackage(), BlueprintClass);

完整的测试代码如下

void UTraceAnimInstance::TestTypeSystem(UBlueprint* InBluerprint)
{
if(!InBluerprint) return;
FString PropertyName, PropertyOuterName;
UClass* BlueprintClass = InBluerprint->GeneratedClass;
UObject* BlueprintInstance = NewObject<UObject>(GetTransientPackage(), BlueprintClass);
if (!BlueprintInstance) return;

FString BPName = BlueprintInstance->GetName();
FString ClassName = BlueprintInstance->GetClass()->GetName();

for (TFieldIterator<FProperty> PropIt(BlueprintClass); PropIt; ++PropIt)
{
FProperty* Property = *PropIt;
if (Property)
{
PropertyName = Property->GetName();
PropertyOuterName = Property->GetOwner<UClass>()->GetName();
if(PropertyName == FString("First"))
{
void* BlueprintPropertyValue = Property->ContainerPtrToValuePtr<void>(BlueprintInstance, 0);
break;
}
}
}
FString LogStr = FString::Printf(TEXT("BPName: '%s', BPClassName: '%s', PropertyName: '%s', PropertyOuterName: '%s'"),
*BPName,*ClassName, *PropertyName, *PropertyOuterName);
UE_LOG(LogTemp, Log, TEXT("%s"), *LogStr);
// GEngine->AddOnScreenDebugMessage(-1, 60.f, FColor::Yellow, Log);
}

我们还需要思考一些其他的问题:

  • GetClass()
  • GetOwner()
  • IsA()
  • GetName() 这些函数来自哪些类,他们的相互关系是什么,这就是UE的类型系统的内容。

类型系统结构

UE5的时候对UE4的系统结构进行了大改,分开来看

UE4