[{"data":1,"prerenderedAt":195},["ShallowReactive",2],{"\u002F2019-09-08-ue4-reflection-note":3},{"id":4,"title":5,"body":6,"date":172,"description":12,"extension":173,"meta":174,"navigation":177,"path":189,"seo":190,"stem":191,"tags":192,"__hash__":194},"blogs\u002F_legacy\u002F2019\u002F2019-09-08-ue4-reflection-note.md","UE4属性反射小结",{"type":7,"value":8,"toc":159},"minimark",[9,13,20,23,26,29,34,37,47,59,62,68,72,75,81,85,88,92,95,101,104,110,113,116,122,126,129,132,138,141,147,150,156],[10,11,12],"p",{},"UProperty是UE4自己建立的一个反射系统的一部分，这里稍微整理下接触到的东西。",[10,14,15,16],{},"平时使用时，如果声明使用了属性系统，就必须在头文件上包含",[17,18,19],"code",{},"*.generated.h",[10,21,22],{},"这样的话UE4就会“自动”的帮你完成反射部分的工作。这部分依赖于UBT和UHT，一般情况下不需要在意。",[10,24,25],{},"有的时候加完新的.h会提示这个文件不存在，一般情况下重新Generate一下项目就好了。",[10,27,28],{},"之所以动到这里是因为之前有个导出的需求，发现有个属性是Private的而有定义成UProperty。由于是制作插件所以不能修改引擎代码，所以只能从旁边绕路了。",[30,31,33],"h2",{"id":32},"uproperty相关读取","UProperty相关读取",[10,35,36],{},"对于指定Class里面的所有属性，可以直接使用帮助类来遍历：",[38,39,44],"pre",{"className":40,"code":42,"language":43},[41],"language-text","for (TFieldIterator\u003CUProperty> PropIt(GetClass()); PropIt; ++PropIt) \n{ \n  UProperty* Property = *PropIt; \u002F\u002F Do something with the property \n}\n","text",[17,45,42],{"__ignoreMap":46},"",[10,48,49,50,54,55,58],{},"在其之上就可以使用",[51,52,53],"strong",{},"ContainerPtrToValuePtr","或者",[51,56,57],{},"GetPropertyValue","来对属性进行读取了，在BlueprintNodeHelpers::CollectPropertyDescription中也能看到使用的实例。",[10,60,61],{},"或者也可以封装成函数：",[38,63,66],{"className":64,"code":65,"language":43},[41],"bool GetFloatByName(UObject * Target, FName VarName, float &outFloat) \n{ \n  if (Target) \u002F\u002Fmake sure Target was set in blueprints. \n  { \n    float FoundFloat; UFloatProperty* FloatProp = FindField\u003CUFloatProperty>(Target->GetClass(), VarName);  \u002F\u002F try to find float property in Target named VarName \n    if (FloatProp) \u002F\u002Fif we found variable \n    { \n      FoundFloat = FloatProp->GetPropertyValue_InContainer(Target);  \u002F\u002F get the value from \n      FloatProp outFloat = FoundFloat;  \u002F\u002F return float \n      return true; \u002F\u002F we can return \n    } \n  } \n  return false; \u002F\u002F we haven't found variable return false \n}\n",[17,67,65],{"__ignoreMap":46},[69,70,71],"h3",{"id":71},"蓝图属性",[10,73,74],{},"关于蓝图的属性载入过程，可以参考AActor::Serialize：",[38,76,79],{"className":77,"code":78,"language":43},[41],"if (const UBlueprintGeneratedClass* BPGC = Cast\u003CUBlueprintGeneratedClass>(GetClass()))\n{\n  NativeConstructedComponentToPropertyMap.Reset();\n  NativeConstructedComponentToPropertyMap.Reserve(OwnedComponents.Num());\n  for(TFieldIterator\u003CUObjectProperty> PropertyIt(BPGC, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)\n  {\n    UObjectProperty* ObjProp = *PropertyIt;\n    \u002F\u002F Ignore transient properties since they won't be serialized\n    if(!ObjProp->HasAnyPropertyFlags(CPF_Transient))\n    {\n      UActorComponent* ActorComponent = Cast\u003CUActorComponent>(ObjProp->GetObjectPropertyValue_InContainer(this));\n      if(ActorComponent != nullptr && ActorComponent->CreationMethod == EComponentCreationMethod::Native)\n      {\n        NativeConstructedComponentToPropertyMap.Add(ActorComponent->GetFName(), ObjProp);\n      }\n    }\n  }\n}\n",[17,80,78],{"__ignoreMap":46},[30,82,84],{"id":83},"fguid","FGuid",[10,86,87],{},"这里要读取的是FGuid，当时做了两种尝试",[69,89,91],{"id":90},"通过exporttextitem","通过ExportTextItem",[10,93,94],{},"这样做是因为FGuid有提供LexFromString方法，ExportTextItem本身封装在DescribeProperty中：",[38,96,99],{"className":97,"code":98,"language":43},[41],"FString DescribeProperty(const UProperty* Prop, const uint8* PropertyAddr)\n{\n  FString ExportedStringValue;\n  const UStructProperty* StructProp = Cast\u003Cconst UStructProperty>(Prop);\n  const UFloatProperty* FloatProp = Cast\u003Cconst UFloatProperty>(Prop);\n  if (StructProp && StructProp->GetCPPType(NULL, CPPF_None).Contains(GET_STRUCT_NAME_CHECKED(FBlackboardKeySelector)))\n  {\n    \u002F\u002F special case for blackboard key selectors\n    const FBlackboardKeySelector* PropertyValue = (const FBlackboardKeySelector*)PropertyAddr;\n    ExportedStringValue = PropertyValue->SelectedKeyName.ToString();\n  }\n  else if (FloatProp)\n  {\n    \u002F\u002F special case for floats to remove unnecessary zeros\n    const float FloatValue = FloatProp->GetPropertyValue(PropertyAddr);\n    ExportedStringValue = FString::SanitizeFloat(FloatValue);\n  }\n  else\n  {\n    Prop->ExportTextItem(ExportedStringValue, PropertyAddr, NULL, NULL, PPF_PropertyWindow, NULL);\n  }\n  const bool bIsBool = Prop->IsA(UBoolProperty::StaticClass());\n  return FString::Printf(TEXT(\"%s: %s\"), *FName::NameToDisplayString(Prop->GetName(), bIsBool), *ExportedStringValue);\n}\n",[17,100,98],{"__ignoreMap":46},[10,102,103],{},"实际使用的话是这样的：",[38,105,108],{"className":106,"code":107,"language":43},[41],"UProperty* tp_FindedProp = FindField\u003CUProperty>(tp_LandscapeHCC->GetClass(), TEXT(\"HeightfieldGuid\"));\nconst uint8* PropData = tp_FindedProp->ContainerPtrToValuePtr\u003Cuint8>(tp_LandscapeHCC);\nFString tstr_DProperty = DescribeProperty(tp_FindedProp, PropData);\nFGuid ts_Guid;\nLexFromString(ts_Guid, DProperty);\n",[17,109,107],{"__ignoreMap":46},[69,111,112],{"id":112},"直接转换",[10,114,115],{},"其实也可以直接强制转换类型，不过会有一定的危险性",[38,117,120],{"className":118,"code":119,"language":43},[41],"const FGuid* tp_GuidConvert = (FGuid*)PropData;\nUE_LOG(LogTemp, Log, TEXT(\"Compare to see: %s || %s\"), *tstr_DProperty, *LexToString(*tp_GuidConvert));\n",[17,121,119],{"__ignoreMap":46},[30,123,125],{"id":124},"ufunction","UFUNCTION",[10,127,128],{},"这里的另一个需求是要求目标的Object实现一个事先约定好的接口，而插件这边则负责调用改接口来实现特定的额外导出。",[10,130,131],{},"其实FindFunctionByName是引擎内本来就有的 ，蓝图节点UK2Node_CallFunction中也能看到引擎是如何将功能暴露到蓝图的。在参考上UObject::CallFunctionByNameWithArguments和UObject::FindFunction也可以得到一些帮助，最后在实现上采用了这样的形式：",[38,133,136],{"className":134,"code":135,"language":43},[41],"UE_LOG(LogWorldExport, Log, TEXT(\"Get target class\"));\nstatic FName ts_FuncName(TEXT(\"ExportOut\"));\nUFunction* tp_ExportFunc = tp_IterActor->FindFunction(ts_FuncName);\n\u002F\u002Ftp_IterActor->ProcessEvent(tp_ExportFunc, nullptr);\nif (tp_ExportFunc)\n{\n  FFrame Stack(tp_IterActor, tp_ExportFunc, nullptr);\n  tp_ExportFunc->Invoke(tp_IterActor, Stack, nullptr);\n}\nelse\n{\n  UE_LOG(LogWorldExport, Error, TEXT(\"Target class %s found but func %s is not fund\"), *tstr_ActorName, *ts_FuncName.ToString());\n}\n",[17,137,135],{"__ignoreMap":46},[10,139,140],{},"如果需要返回值的话，可以这样：",[38,142,145],{"className":143,"code":144,"language":43},[41],"const bool bHasReturnParam = Function->ReturnValueOffset != MAX_uint16;\nuint8* ReturnValueAddress = bHasReturnParam ? ((uint8*)Parms + Function->ReturnValueOffset) : nullptr;\n",[17,146,144],{"__ignoreMap":46},[30,148,149],{"id":149},"总结",[10,151,152,153,155],{},"其实在读取UStruct的时候，应当是可以使用",[51,154,53],{},"作Struct的转换的，不过不知道当时为什么没有这么做，这点只能留到下次有相关需求的时候再考证了。",[10,157,158],{},"另外，如果对UProperty的读写有兴趣的话，其实可以参考引擎的LoadConfig和SaveConfig，里面包含了所有类型的属性的读写。",{"title":46,"searchDepth":160,"depth":161,"links":162},2,3,[163,166,170,171],{"id":32,"depth":160,"text":33,"children":164},[165],{"id":71,"depth":161,"text":71},{"id":83,"depth":160,"text":84,"children":167},[168,169],{"id":90,"depth":161,"text":91},{"id":112,"depth":161,"text":112},{"id":124,"depth":160,"text":125},{"id":149,"depth":160,"text":149},"2019-09-08","md",{"layout":175,"status":176,"published":177,"author":178,"author_login":180,"author_email":181,"wordpress_id":182,"wordpress_url":183,"date_gmt":184,"excerpt":185},"post","publish",true,{"display_name":179,"login":180,"email":181,"url":46},"风铃","flinkor","flinkor@foxmail.com",2695,"\u002F?p=2695","2019-09-08 12:04:30 +0000",{"type":7,"value":186},[187],[10,188,12],{},"\u002F2019-09-08-ue4-reflection-note",{"title":5,"description":12},"_legacy\u002F2019\u002F2019-09-08-ue4-reflection-note",[193],"UE4","lzKUFzuZlc7mwr6a2SnRdYsc8_H_hkI4Ro6IpGLAPUI",1788763179066]