void AAbilitySystemDebugHUD::DrawDebugAbilitySystemComponent(UAbilitySystemComponent *Component) { UWorld *World = GetWorld(); float GameWorldTime = World->GetTimeSeconds(); UFont* Font = GEngine->GetSmallFont(); FColor Color(38, 128, 0); float X = 20.f; float Y = 20.f; FString String = FString::Printf(TEXT("%.2f"), Component->GetWorld()->GetTimeSeconds()); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); String = FString::Printf(TEXT("%s (%d)"), *Component->GetPathName(), Component->IsDefaultSubobject()); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); String = FString::Printf(TEXT("%s == %s"), *Component->GetArchetype()->GetPathName(), *Component->GetClass()->GetDefaultObject()->GetPathName()); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); for (const UAttributeSet* Set : Component->SpawnedAttributes) { if (!Set) continue; check(Set); // Draw Attribute Set DrawWithBackground(Font, FString::Printf(TEXT("%s (%d)"), *Set->GetName(), Set->IsDefaultSubobject()), Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); String = FString::Printf(TEXT("%s == %s"), *Set->GetArchetype()->GetPathName(), *Set->GetClass()->GetDefaultObject()->GetPathName()); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); for (TFieldIterator<UProperty> PropertyIt(Set->GetClass(), EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty *Prop = *PropertyIt; FString ValueString; const void *PropertyValue = Prop->ContainerPtrToValuePtr<void>(Set); Prop->ExportTextItem(ValueString, PropertyValue, NULL, NULL, 0); String = FString::Printf(TEXT("%s: %s"), *Prop->GetName(), *ValueString); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); } Y+= 25; // Draw Active GameplayEffect for (FActiveGameplayEffect& Effect : &Component->ActiveGameplayEffects) { String = FString::Printf(TEXT("%s. [%d, %d] %.2f"), *Effect.Spec.ToSimpleString(), Effect.PredictionKey.Current, Effect.PredictionKey.Base, Effect.GetTimeRemaining(GameWorldTime)); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); } } }
TArray<FPropertySoftPath> DiffUtils::GetVisiblePropertiesInOrderDeclared(const UObject* ForObj, const TArray<FName>& Scope /*= TArray<FName>()*/) { TArray<FPropertySoftPath> Ret; if (ForObj) { const UClass* Class = ForObj->GetClass(); TSet<FString> HiddenCategories = FEditorCategoryUtils::GetHiddenCategories(Class); for (TFieldIterator<UProperty> PropertyIt(Class); PropertyIt; ++PropertyIt) { FName CategoryName = FObjectEditorUtils::GetCategoryFName(*PropertyIt); if (!HiddenCategories.Contains(CategoryName.ToString())) { if (PropertyIt->PropertyFlags&CPF_Edit) { TArray<FName> NewPath(Scope); NewPath.Push(PropertyIt->GetFName()); if (const UObjectProperty* ObjectProperty = Cast<UObjectProperty>(*PropertyIt)) { const UObject* const* BaseObject = reinterpret_cast<const UObject* const*>( ObjectProperty->ContainerPtrToValuePtr<void>(ForObj) ); if (BaseObject && *BaseObject) { Ret.Append( GetVisiblePropertiesInOrderDeclared(*BaseObject, NewPath) ); } } else { Ret.Push(NewPath); } } } } } return Ret; }
void UK2Node_SpawnActorFromClass::CreatePinsForClass(UClass* InClass, TArray<UEdGraphPin*>& OutClassPins) { check(InClass != NULL); const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); const UObject* const ClassDefaultObject = InClass->GetDefaultObject(false); for (TFieldIterator<UProperty> PropertyIt(InClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; UClass* PropertyClass = CastChecked<UClass>(Property->GetOuter()); const bool bIsDelegate = Property->IsA(UMulticastDelegateProperty::StaticClass()); const bool bIsExposedToSpawn = UEdGraphSchema_K2::IsPropertyExposedOnSpawn(Property); const bool bIsSettableExternally = !Property->HasAnyPropertyFlags(CPF_DisableEditOnInstance); if( bIsExposedToSpawn && !Property->HasAnyPropertyFlags(CPF_Parm) && bIsSettableExternally && Property->HasAllPropertyFlags(CPF_BlueprintVisible) && !bIsDelegate && (NULL == FindPin(Property->GetName()) ) ) { UEdGraphPin* Pin = CreatePin(EGPD_Input, TEXT(""), TEXT(""), NULL, false, false, Property->GetName()); const bool bPinGood = (Pin != NULL) && K2Schema->ConvertPropertyToPinType(Property, /*out*/ Pin->PinType); OutClassPins.Add(Pin); if (ClassDefaultObject && Pin != NULL && K2Schema->PinDefaultValueIsEditable(*Pin)) { FString DefaultValueAsString; const bool bDefaultValueSet = FBlueprintEditorUtils::PropertyValueToString(Property, reinterpret_cast<const uint8*>(ClassDefaultObject), DefaultValueAsString); check( bDefaultValueSet ); K2Schema->TrySetDefaultValue(*Pin, DefaultValueAsString); } // Copy tooltip from the property. if (Pin != nullptr) { K2Schema->ConstructBasicPinTooltip(*Pin, Property->GetToolTipText(), Pin->PinToolTip); } } } // Change class of output pin UEdGraphPin* ResultPin = GetResultPin(); ResultPin->PinType.PinSubCategoryObject = InClass; }
bool FLuaScriptCodeGenerator::CanExportClass(UClass* Class) { bool bCanExport = FScriptCodeGeneratorBase::CanExportClass(Class); if (bCanExport) { const FString ClassNameCPP = GetClassNameCPP(Class); // No functions to export? Don't bother exporting the class. bool bHasMembersToExport = false; for (TFieldIterator<UFunction> FuncIt(Class); !bHasMembersToExport && FuncIt; ++FuncIt) { UFunction* Function = *FuncIt; if (CanExportFunction(ClassNameCPP, Class, Function)) { bHasMembersToExport = true; } } // Check properties too if (!bHasMembersToExport) { for (TFieldIterator<UProperty> PropertyIt(Class, EFieldIteratorFlags::ExcludeSuper); !bHasMembersToExport && PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; if (CanExportProperty(ClassNameCPP, Class, Property)) { bHasMembersToExport = true; } } } bCanExport = bHasMembersToExport; } return bCanExport; }
void UBehaviorTreeGraphNode::DiffProperties(UStruct* Struct, void* DataA, void* DataB, FDiffResults& Results, FDiffSingleResult& Diff) { for (TFieldIterator<UProperty> PropertyIt(Struct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Prop = *PropertyIt; // skip properties we cant see if (!Prop->HasAnyPropertyFlags(CPF_Edit|CPF_BlueprintVisible) || Prop->HasAnyPropertyFlags(CPF_Transient) || Prop->HasAnyPropertyFlags(CPF_DisableEditOnInstance) || Prop->IsA(UFunction::StaticClass()) || Prop->IsA(UDelegateProperty::StaticClass()) || Prop->IsA(UMulticastDelegateProperty::StaticClass())) { continue; } FString ValueStringA = BlueprintNodeHelpers::DescribeProperty(Prop, Prop->ContainerPtrToValuePtr<uint8>(DataA)); FString ValueStringB = BlueprintNodeHelpers::DescribeProperty(Prop, Prop->ContainerPtrToValuePtr<uint8>(DataB)); if ( ValueStringA != ValueStringB ) { if(Results) { Diff.DisplayString = FText::Format(LOCTEXT("DIF_NodePropertyFmt", "Property Changed: {0} "), FText::FromString(Prop->GetName())); Results.Add(Diff); } } } }
void UK2Node_SpawnActor::CreatePinsForClass(UClass* InClass) { check(InClass != NULL); const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); for (TFieldIterator<UProperty> PropertyIt(InClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; UClass* PropertyClass = CastChecked<UClass>(Property->GetOuter()); const bool bIsDelegate = Property->IsA(UMulticastDelegateProperty::StaticClass()); const bool bIsExposedToSpawn = UEdGraphSchema_K2::IsPropertyExposedOnSpawn(Property); const bool bIsSettableExternally = !Property->HasAnyPropertyFlags(CPF_DisableEditOnInstance); if( bIsExposedToSpawn && !Property->HasAnyPropertyFlags(CPF_Parm) && bIsSettableExternally && Property->HasAllPropertyFlags(CPF_BlueprintVisible) && !bIsDelegate ) { UEdGraphPin* Pin = CreatePin(EGPD_Input, TEXT(""), TEXT(""), NULL, false, false, Property->GetName()); const bool bPinGood = (Pin != NULL) && K2Schema->ConvertPropertyToPinType(Property, /*out*/ Pin->PinType); } } // Change class of output pin UEdGraphPin* ResultPin = GetResultPin(); ResultPin->PinType.PinSubCategoryObject = InClass; }
static bool ShouldCreateWidgetSomwhereInBranch(UProperty* InProp) { UStructProperty* StructProperty = Cast<UStructProperty>(InProp); if (!StructProperty) { UArrayProperty* ArrayProperty = Cast<UArrayProperty>(InProp); if (ArrayProperty) { StructProperty = Cast<UStructProperty>(ArrayProperty->Inner); } } if (StructProperty) { if (FEdMode::CanCreateWidgetForStructure(StructProperty->Struct) && InProp->HasMetaData(FEdMode::MD_MakeEditWidget)) { return true; } for (TFieldIterator<UProperty> PropertyIt(StructProperty->Struct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { if (ShouldCreateWidgetSomwhereInBranch(*PropertyIt)) { return true; } } } return false; }
void SGraphPinLiveEditVar::GenerateComboBoxIndexesRecurse( UStruct *InStruct, FString PropertyPrefix, TArray< TSharedPtr<int32> >& OutComboBoxIndexes ) { check( InStruct != NULL ); for (TFieldIterator<UProperty> PropertyIt(InStruct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; if ( !Property->IsA(UNumericProperty::StaticClass()) ) { if ( Property->IsA(UStructProperty::StaticClass()) ) { UStructProperty *StructProp = Cast<UStructProperty>(Property); if ( IsPropertyPermitedForLiveEditor(*StructProp) ) { if ( Property->ArrayDim > 1 ) { for ( int32 i = 0; i < Property->ArrayDim; ++i ) { FString arrayIndex = FString::Printf( TEXT("[%d]"), i ); FString NewPropertyPrefix = FString::Printf( TEXT("%s%s%s."), *PropertyPrefix, *Property->GetName(), *arrayIndex ); GenerateComboBoxIndexesRecurse( StructProp->Struct, NewPropertyPrefix, OutComboBoxIndexes ); } } else { FString NewPropertyPrefix = FString::Printf( TEXT("%s%s."), *PropertyPrefix, *Property->GetName() ); GenerateComboBoxIndexesRecurse( StructProp->Struct, NewPropertyPrefix, OutComboBoxIndexes ); } } } else if ( Property->IsA(UArrayProperty::StaticClass()) ) { UArrayProperty *ArrayProp = Cast<UArrayProperty>(Property); } continue; } if ( IsPropertyPermitedForLiveEditor(*Property) ) { if ( Property->ArrayDim > 1 ) { for ( int32 i = 0; i < Property->ArrayDim; ++i ) { FString arrayIndex = FString::Printf( TEXT("[%d]"), i ); FString Name = PropertyPrefix + Property->GetName() + arrayIndex; VariableNameList.Add( Name ); } } else { FString Name = PropertyPrefix + Property->GetName(); VariableNameList.Add( Name ); } } } }
static UProperty *GetPropertyByNameRecurse( UStruct *InStruct, const FString &TokenString, void ** hContainerPtr, int32 &OutArrayIndex ) { FString FirstToken; FString RemainingTokens; int32 SplitIndex; if ( TokenString.FindChar( '.', SplitIndex ) ) { FirstToken = TokenString.LeftChop( TokenString.Len()-SplitIndex ); RemainingTokens = TokenString.RightChop(SplitIndex+1); } else { FirstToken = TokenString; RemainingTokens = FString(TEXT("")); } //get the array index if there is any int32 ArrayIndex = 0; if ( FirstToken.FindChar( '[', SplitIndex ) ) { FString ArrayIndexString = FirstToken.RightChop( SplitIndex+1 ); ArrayIndexString = ArrayIndexString.LeftChop( 1 ); FDefaultValueHelper::ParseInt( ArrayIndexString, ArrayIndex ); FirstToken = FirstToken.LeftChop( FirstToken.Len()-SplitIndex ); } for (TFieldIterator<UProperty> PropertyIt(InStruct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; FName PropertyName = Property->GetFName(); if ( FirstToken == PropertyName.ToString() ) { if ( RemainingTokens.Len() == 0 ) { check( *hContainerPtr != NULL ); OutArrayIndex = ArrayIndex; return Property; } else { UStructProperty *StructProp = Cast<UStructProperty>(Property); if ( StructProp ) { check( *hContainerPtr != NULL ); *hContainerPtr = Property->ContainerPtrToValuePtr<void>( *hContainerPtr, ArrayIndex ); return GetPropertyByNameRecurse( StructProp->Struct, RemainingTokens, hContainerPtr, OutArrayIndex ); } } } } return NULL; }
void UK2Node_AddComponent::AllocatePinsForExposedVariables() { const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); const UActorComponent* TemplateComponent = GetTemplateFromNode(); const UClass* ComponentClass = TemplateComponent ? TemplateComponent->GetClass() : nullptr; if (ComponentClass != nullptr) { const UObject* ClassDefaultObject = ComponentClass ? ComponentClass->ClassDefaultObject : nullptr; for (TFieldIterator<UProperty> PropertyIt(ComponentClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; const bool bNotDelegate = !Property->IsA(UMulticastDelegateProperty::StaticClass()); const bool bIsExposedToSpawn = UEdGraphSchema_K2::IsPropertyExposedOnSpawn(Property); const bool bIsVisible = Property->HasAllPropertyFlags(CPF_BlueprintVisible); const bool bNotParam = !Property->HasAllPropertyFlags(CPF_Parm); if(bNotDelegate && bIsExposedToSpawn && bIsVisible && bNotParam) { FEdGraphPinType PinType; K2Schema->ConvertPropertyToPinType(Property, /*out*/ PinType); const bool bIsUnique = (NULL == FindPin(Property->GetName())); if (K2Schema->FindSetVariableByNameFunction(PinType) && bIsUnique) { UEdGraphPin* Pin = CreatePin(EGPD_Input, TEXT(""), TEXT(""), NULL, false, false, Property->GetName()); Pin->PinType = PinType; bHasExposedVariable = true; if ((ClassDefaultObject != nullptr) && K2Schema->PinDefaultValueIsEditable(*Pin)) { FString DefaultValueAsString; const bool bDefaultValueSet = FBlueprintEditorUtils::PropertyValueToString(Property, reinterpret_cast<const uint8*>(ClassDefaultObject), DefaultValueAsString); check(bDefaultValueSet); K2Schema->TrySetDefaultValue(*Pin, DefaultValueAsString); } // Copy tooltip from the property. K2Schema->ConstructBasicPinTooltip(*Pin, Property->GetToolTipText(), Pin->PinToolTip); } } } } // Hide transform and attachment pins if it is not a scene component const bool bHideTransformPins = (ComponentClass != nullptr) ? !ComponentClass->IsChildOf(USceneComponent::StaticClass()) : false; UEdGraphPin* ManualAttachmentPin = GetManualAttachmentPin(); ManualAttachmentPin->SafeSetHidden(bHideTransformPins); UEdGraphPin* TransformPin = GetRelativeTransformPin(); TransformPin->SafeSetHidden(bHideTransformPins); }
UProperty* FindScriptPropertyHelper(UClass* Class, FName PropertyName) { for (TFieldIterator<UProperty> PropertyIt(Class, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; if (Property->GetFName() == PropertyName) { return Property; } } return NULL; }
static const UProperty* Resolve( const UStruct* Class, FName PropertyName ) { for (TFieldIterator<UProperty> PropertyIt(Class); PropertyIt; ++PropertyIt) { if( PropertyIt->GetFName() == PropertyName ) { return *PropertyIt; } } return nullptr; }
//------------------------------------------------------------------------------ static bool ContextMenuTargetProfileImpl::HasAnyExposedComponents(UClass* TargetClass) { for (TFieldIterator<UObjectProperty> PropertyIt(TargetClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UObjectProperty* ObjectProperty = *PropertyIt; if (!ObjectProperty->HasAnyPropertyFlags(CPF_BlueprintVisible) || !ObjectProperty->PropertyClass->IsChildOf<UActorComponent>()) { continue; } return true; } return false; }
void FixupStruct(UStruct* Struct, void* Dest, const void* Src) { if (Struct == Struct_SlateColor) { *((FSlateColor*)Dest) = Fixup(*(FSlateColor*)Src); return; } for (TFieldIterator<UProperty> PropertyIt(Struct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { auto Property = *PropertyIt; if (UStructProperty* p = Cast<UStructProperty>(Property)) { FixupStruct(p->Struct, p->ContainerPtrToValuePtr<void>(Dest), p->ContainerPtrToValuePtr<void>(Src)); } } }
void FLuaScriptCodeGenerator::ExportClass(UClass* Class, const FString& SourceHeaderFilename, const FString& GeneratedHeaderFilename, bool bHasChanged) { if (!CanExportClass(Class)) { return; } UE_LOG(LogScriptGenerator, Log, TEXT("Exporting class %s"), *Class->GetName()); ExportedClasses.Add(Class->GetFName()); LuaExportedClasses.Add(Class); AllSourceClassHeaders.Add(SourceHeaderFilename); const FString ClassGlueFilename = GetScriptHeaderForClass(Class); AllScriptHeaders.Add(ClassGlueFilename); const FString ClassNameCPP = GetClassNameCPP(Class); FString GeneratedGlue(TEXT("#pragma once\r\n\r\n")); // Export all functions for (TFieldIterator<UFunction> FuncIt(Class /*, EFieldIteratorFlags::ExcludeSuper*/); FuncIt; ++FuncIt) { UFunction* Function = *FuncIt; if (CanExportFunction(ClassNameCPP, Class, Function)) { GeneratedGlue += ExportFunction(ClassNameCPP, Class, Function); } } // Export properties that are owned by this class int32 PropertyIndex = 0; for (TFieldIterator<UProperty> PropertyIt(Class /*, EFieldIteratorFlags::ExcludeSuper*/); PropertyIt; ++PropertyIt, ++PropertyIndex) { UProperty* Property = *PropertyIt; if (CanExportProperty(ClassNameCPP, Class, Property)) { UE_LOG(LogScriptGenerator, Log, TEXT(" %s %s"), *Property->GetClass()->GetName(), *Property->GetName()); GeneratedGlue += ExportProperty(ClassNameCPP, Class, Property, PropertyIndex); } } GeneratedGlue += ExportAdditionalClassGlue(ClassNameCPP, Class); SaveHeaderIfChanged(ClassGlueFilename, GeneratedGlue); }
static UProperty *GetPropertyByNameRecurse( UStruct *InStruct, const FString &TokenString ) { FString FirstToken; FString RemainingTokens; int32 SplitIndex; if ( TokenString.FindChar( '.', SplitIndex ) ) { FirstToken = TokenString.LeftChop( TokenString.Len()-SplitIndex ); RemainingTokens = TokenString.RightChop(SplitIndex+1); } else { FirstToken = TokenString; RemainingTokens = FString(TEXT("")); } if ( FirstToken.FindChar( '[', SplitIndex ) ) { FirstToken = FirstToken.LeftChop( FirstToken.Len()-SplitIndex ); } for (TFieldIterator<UProperty> PropertyIt(InStruct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; FName PropertyName = Property->GetFName(); if ( FirstToken == PropertyName.ToString() ) { if ( RemainingTokens.Len() == 0 ) { return Property; } else { UStructProperty *StructProp = Cast<UStructProperty>(Property); if ( StructProp ) { return GetPropertyByNameRecurse( StructProp->Struct, RemainingTokens ); } } } } return NULL; }
void SGAAttributeWidget::Construct(const FArguments& InArgs) { AttributesList.Empty(); AttributesNodes.Empty(); OnAttributeSelected = InArgs._OnAttributeSelectedIn; for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt) { UClass* Class = *ClassIt; if (Class->IsChildOf(UGAAttributesBase::StaticClass()) && !FKismetEditorUtilities::IsClassABlueprintSkeleton(Class)) { FString className = Class->GetName(); if (!className.Contains(TEXT("REINST_"))) { TSharedPtr<FGAAttributeNode> attributeNode = MakeShareable(new FGAAttributeNode()); attributeNode->Attribute = className; for (TFieldIterator<UProperty> PropertyIt(Class, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt) { UProperty* Prop = *PropertyIt; //I need array within array, one for list of attributes, and one for class names. // TSharedPtr<FString> attribute = MakeShareable(new FString(Prop->GetName())); attributeNode->AttributeNames.Add(Prop->GetName()); //AttributesList.Add(attribute); } AttributesNodes.Add(attributeNode); } } } ChildSlot [ SAssignNew(AttributeTreeWidget, STreeView<TSharedPtr<FGAAttributeNode>>) .OnSelectionChanged(this, &SGAAttributeWidget::OnItemSelected) .TreeItemsSource(&AttributesNodes) .OnGenerateRow(this, &SGAAttributeWidget::OnGenerateRow) .OnGetChildren(this, &SGAAttributeWidget::OnGetChildren) .OnExpansionChanged(this, &SGAAttributeWidget::OnExpansionChanged) .SelectionMode(ESelectionMode::Single) ]; }
void UK2Node_LatentAbilityCall::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const { Super::ValidateNodeDuringCompilation(MessageLog); UFunction* DelegateSignatureFunction = NULL; for (TFieldIterator<UProperty> PropertyIt(ProxyClass); PropertyIt; ++PropertyIt) { if (UMulticastDelegateProperty* Property = Cast<UMulticastDelegateProperty>(*PropertyIt)) { if (Property->GetBoolMetaData(FK2Node_LatentAbilityCallHelper_RequiresConnection)) { if (UEdGraphPin* DelegateExecPin = FindPin(Property->GetName())) { if (DelegateExecPin->LinkedTo.Num() < 1) { const FText MessageText = FText::Format(LOCTEXT("NoConnectionToRequiredExecPin", "@@ - Unhandled event. You need something connected to the '{0}' pin"), FText::FromName(Property->GetFName())); MessageLog.Warning(*MessageText.ToString(), this); } } } } } }
//------------------------------------------------------------------------------ static int32 GetHiddenProperties(uint32 Indent, UClass* Class, FString& JsonOut) { int32 HiddenPropCount = 0; FString IndentString = GetIndentString(Indent); JsonOut += IndentString + TEXT("\"HiddenProperties\" : ["); for (TFieldIterator<UProperty> PropertyIt(Class, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; if (FObjectEditorUtils::IsVariableCategoryHiddenFromClass(Property, Class)) { ++HiddenPropCount; JsonOut += TEXT("\n\t") + IndentString + TEXT("\"") + Property->GetPathName() + TEXT("\","); } } if (HiddenPropCount > 0) { JsonOut.RemoveAt(JsonOut.Len() - 1); // remove the last comma } JsonOut += TEXT("\n") + IndentString + TEXT("]"); return HiddenPropCount; }
/** * This is essentially a mix of K2Node_BaseAsyncTask::ExpandNode and K2Node_SpawnActorFromClass::ExpandNode. * Several things are going on here: * -Factory call to create proxy object (K2Node_BaseAsyncTask) * -Task return delegates are created and hooked up (K2Node_BaseAsyncTask) * -A BeginSpawn function is called on proxyu object (similiar to K2Node_SpawnActorFromClass) * -BeginSpawn can choose to spawn or not spawn an actor (and return it) * -If spawned: * -SetVars are run on the newly spawned object (set expose on spawn variables - K2Node_SpawnActorFromClass) * -FinishSpawn is called on the proxy object * * * Also, a K2Node_SpawnActorFromClass could not be used directly here, since we want the proxy object to implement its own * BeginSpawn/FinishSpawn function (custom game logic will often be performed in the native implementation). K2Node_SpawnActorFromClass also * requires a SpawnTransform be wired into it, and in most ability task cases, the spawn transform is implied or not necessary. * * */ void UK2Node_LatentAbilityCall::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { bool validatedActorSpawn = ValidateActorSpawning(CompilerContext, false); bool validatedActorArraySpawn = ValidateActorArraySpawning(CompilerContext, false); UEdGraphPin* ClassPin = GetClassPin(); if (ClassPin == nullptr) { // Nothing special about this task, just call super Super::ExpandNode(CompilerContext, SourceGraph); return; } UK2Node::ExpandNode(CompilerContext, SourceGraph); if (!validatedActorSpawn && !validatedActorArraySpawn) { ValidateActorSpawning(CompilerContext, true); ValidateActorArraySpawning(CompilerContext, true); } const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); check(SourceGraph && Schema); bool bIsErrorFree = true; // ------------------------------------------------------------------------------------------ // CREATE A CALL TO FACTORY THE PROXY OBJECT // ------------------------------------------------------------------------------------------ UK2Node_CallFunction* const CallCreateProxyObjectNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallCreateProxyObjectNode->FunctionReference.SetExternalMember(ProxyFactoryFunctionName, ProxyFactoryClass); CallCreateProxyObjectNode->AllocateDefaultPins(); bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(Schema->PN_Execute), *CallCreateProxyObjectNode->FindPinChecked(Schema->PN_Execute)).CanSafeConnect(); for (auto CurrentPin : Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Input, Schema)) { UEdGraphPin* DestPin = CallCreateProxyObjectNode->FindPin(CurrentPin->PinName); // match function inputs, to pass data to function from CallFunction node // NEW: if no DestPin, assume it is a Class Spawn PRoperty - not an error if (DestPin) { bIsErrorFree &= CompilerContext.CopyPinLinksToIntermediate(*CurrentPin, *DestPin).CanSafeConnect(); } } } // ------------------------------------------------------------------------------------------ // GATHER OUTPUT PARAMETERS AND PAIR THEM WITH LOCAL VARIABLES // ------------------------------------------------------------------------------------------ TArray<FBaseAsyncTaskHelper::FOutputPinAndLocalVariable> VariableOutputs; for (auto CurrentPin : Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Output, Schema)) { const FEdGraphPinType& PinType = CurrentPin->PinType; UK2Node_TemporaryVariable* TempVarOutput = CompilerContext.SpawnInternalVariable( this, PinType.PinCategory, PinType.PinSubCategory, PinType.PinSubCategoryObject.Get(), PinType.bIsArray); bIsErrorFree &= TempVarOutput->GetVariablePin() && CompilerContext.MovePinLinksToIntermediate(*CurrentPin, *TempVarOutput->GetVariablePin()).CanSafeConnect(); VariableOutputs.Add(FBaseAsyncTaskHelper::FOutputPinAndLocalVariable(CurrentPin, TempVarOutput)); } } // ------------------------------------------------------------------------------------------ // FOR EACH DELEGATE DEFINE EVENT, CONNECT IT TO DELEGATE AND IMPLEMENT A CHAIN OF ASSIGMENTS // ------------------------------------------------------------------------------------------ UEdGraphPin* LastThenPin = CallCreateProxyObjectNode->FindPinChecked(Schema->PN_Then); UEdGraphPin* const ProxyObjectPin = CallCreateProxyObjectNode->GetReturnValuePin(); for (TFieldIterator<UMulticastDelegateProperty> PropertyIt(ProxyClass, EFieldIteratorFlags::ExcludeSuper); PropertyIt && bIsErrorFree; ++PropertyIt) { bIsErrorFree &= FBaseAsyncTaskHelper::HandleDelegateImplementation(*PropertyIt, VariableOutputs, ProxyObjectPin, LastThenPin, this, SourceGraph, CompilerContext); } if (CallCreateProxyObjectNode->FindPinChecked(Schema->PN_Then) == LastThenPin) { CompilerContext.MessageLog.Error(*LOCTEXT("MissingDelegateProperties", "BaseAsyncTask: Proxy has no delegates defined. @@").ToString(), this); return; } // ------------------------------------------------------------------------------------------ // NEW: CREATE A CALL TO THE PRESPAWN FUNCTION, IF IT RETURNS TRUE, THEN WE WILL SPAWN THE NEW ACTOR // ------------------------------------------------------------------------------------------ FName ProxyPrespawnFunctionName = validatedActorArraySpawn ? *FK2Node_LatentAbilityCallHelper::BeginSpawnArrayFuncName : *FK2Node_LatentAbilityCallHelper::BeginSpawnFuncName; UFunction* PreSpawnFunction = ProxyFactoryClass->FindFunctionByName(ProxyPrespawnFunctionName); FName ProxyPostpawnFunctionName = validatedActorArraySpawn ? *FK2Node_LatentAbilityCallHelper::FinishSpawnArrayFuncName : *FK2Node_LatentAbilityCallHelper::FinishSpawnFuncName; UFunction* PostSpawnFunction = ProxyFactoryClass->FindFunctionByName(ProxyPostpawnFunctionName); if (PreSpawnFunction == nullptr) { if (validatedActorArraySpawn) { CompilerContext.MessageLog.Error(*LOCTEXT("MissingBeginSpawningActorArrayFunction", "AbilityTask: Proxy is missing BeginSpawningActorArray native function. @@").ToString(), this); } else { CompilerContext.MessageLog.Error(*LOCTEXT("MissingBeginSpawningActorFunction", "AbilityTask: Proxy is missing BeginSpawningActor native function. @@").ToString(), this); } return; } if (PostSpawnFunction == nullptr) { if (validatedActorArraySpawn) { CompilerContext.MessageLog.Error(*LOCTEXT("MissingFinishSpawningActorArrayFunction", "AbilityTask: Proxy is missing FinishSpawningActorArray native function. @@").ToString(), this); } else { CompilerContext.MessageLog.Error(*LOCTEXT("MissingFinishSpawningActorFunction", "AbilityTask: Proxy is missing FinishSpawningActor native function. @@").ToString(), this); } return; } UK2Node_CallFunction* const CallPrespawnProxyObjectNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallPrespawnProxyObjectNode->FunctionReference.SetExternalMember(ProxyPrespawnFunctionName, ProxyClass); CallPrespawnProxyObjectNode->AllocateDefaultPins(); // Hook up the self connection UEdGraphPin* PrespawnCallSelfPin = Schema->FindSelfPin(*CallPrespawnProxyObjectNode, EGPD_Input); check(PrespawnCallSelfPin); bIsErrorFree &= Schema->TryCreateConnection(ProxyObjectPin, PrespawnCallSelfPin); // Hook up input parameters to PreSpawn for (auto CurrentPin : Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Input, Schema)) { UEdGraphPin* DestPin = CallPrespawnProxyObjectNode->FindPin(CurrentPin->PinName); if (DestPin) { bIsErrorFree &= CompilerContext.CopyPinLinksToIntermediate(*CurrentPin, *DestPin).CanSafeConnect(); } } } // Hook the activate node up in the exec chain UEdGraphPin* PrespawnExecPin = CallPrespawnProxyObjectNode->FindPinChecked(Schema->PN_Execute); UEdGraphPin* PrespawnThenPin = CallPrespawnProxyObjectNode->FindPinChecked(Schema->PN_Then); UEdGraphPin* PrespawnReturnPin = CallPrespawnProxyObjectNode->FindPinChecked(Schema->PN_ReturnValue); UEdGraphPin* SpawnedActorReturnPin = CallPrespawnProxyObjectNode->FindPinChecked(FK2Node_LatentAbilityCallHelper::SpawnedActorPinName); bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, PrespawnExecPin); LastThenPin = PrespawnThenPin; // ------------------------------------------- // Branch based on return value of Prespawn // ------------------------------------------- UK2Node_IfThenElse* BranchNode = SourceGraph->CreateBlankNode<UK2Node_IfThenElse>(); BranchNode->AllocateDefaultPins(); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(BranchNode, this); // Link return value of prespawn with the branch condtional bIsErrorFree &= Schema->TryCreateConnection(PrespawnReturnPin, BranchNode->GetConditionPin()); // Link our Prespawn call to the branch node bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, BranchNode->GetExecPin()); UEdGraphPin* BranchElsePin = BranchNode->GetElsePin(); LastThenPin = BranchNode->GetThenPin(); UClass* ClassToSpawn = GetClassToSpawn(); if (validatedActorArraySpawn && ClassToSpawn) { //Branch for main loop control UK2Node_IfThenElse* Branch = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(this, SourceGraph); Branch->AllocateDefaultPins(); //Create int Iterator UK2Node_TemporaryVariable* IteratorVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph); IteratorVar->VariableType.PinCategory = Schema->PC_Int; IteratorVar->AllocateDefaultPins(); //Iterator assignment (initialization to zero) UK2Node_AssignmentStatement* IteratorInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph); IteratorInitialize->AllocateDefaultPins(); IteratorInitialize->GetValuePin()->DefaultValue = TEXT("0"); //Iterator assignment (incrementing) UK2Node_AssignmentStatement* IteratorAssign = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph); IteratorAssign->AllocateDefaultPins(); //Increment iterator command UK2Node_CallFunction* Increment = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); Increment->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(TEXT("Add_IntInt"))); Increment->AllocateDefaultPins(); Increment->FindPinChecked(TEXT("B"))->DefaultValue = TEXT("1"); //Array length UK2Node_CallArrayFunction* ArrayLength = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(this, SourceGraph); ArrayLength->SetFromFunction(UKismetArrayLibrary::StaticClass()->FindFunctionByName(TEXT("Array_Length"))); ArrayLength->AllocateDefaultPins(); //Array element retrieval UK2Node_CallArrayFunction* GetElement = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(this, SourceGraph); GetElement->SetFromFunction(UKismetArrayLibrary::StaticClass()->FindFunctionByName(TEXT("Array_Get"))); GetElement->AllocateDefaultPins(); //Check node for iterator versus array length UK2Node_CallFunction* Condition = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); Condition->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(TEXT("Less_IntInt"))); Condition->AllocateDefaultPins(); //Connections to set up the loop UEdGraphSchema_K2 const* const K2Schema = Cast<const UEdGraphSchema_K2>(Schema); bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, IteratorInitialize->GetExecPin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), IteratorInitialize->GetVariablePin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorInitialize->GetThenPin(), Branch->GetExecPin()); bIsErrorFree &= Schema->TryCreateConnection(SpawnedActorReturnPin, ArrayLength->GetTargetArrayPin()); bIsErrorFree &= Schema->TryCreateConnection(Condition->GetReturnValuePin(), Branch->GetConditionPin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), Condition->FindPinChecked(TEXT("A"))); bIsErrorFree &= Schema->TryCreateConnection(ArrayLength->FindPin(K2Schema->PN_ReturnValue), Condition->FindPinChecked(TEXT("B"))); //Connections to establish loop iteration bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), Increment->FindPinChecked(TEXT("A"))); bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), IteratorAssign->GetVariablePin()); bIsErrorFree &= Schema->TryCreateConnection(Increment->GetReturnValuePin(), IteratorAssign->GetValuePin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorAssign->GetThenPin(), Branch->GetExecPin()); //This is the inner loop LastThenPin = Branch->GetThenPin(); //Connect the loop branch to the spawn-assignment code block bIsErrorFree &= Schema->TryCreateConnection(SpawnedActorReturnPin, GetElement->GetTargetArrayPin()); bIsErrorFree &= Schema->TryCreateConnection(IteratorVar->GetVariablePin(), GetElement->FindPinChecked(K2Schema->PN_Index)); bIsErrorFree &= ConnectSpawnProperties(ClassToSpawn, Schema, CompilerContext, SourceGraph, LastThenPin, GetElement->FindPinChecked(K2Schema->PN_Item)); //Last argument is the array element bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, IteratorAssign->GetExecPin()); //Connect the spawn-assignment code block to the iterator increment //Finish by providing the proper path out LastThenPin = Branch->GetElsePin(); } // ------------------------------------------- // Set spawn variables // Borrowed heavily from FKismetCompilerUtilities::GenerateAssignmentNodes // ------------------------------------------- if (validatedActorSpawn && ClassToSpawn) { bIsErrorFree &= ConnectSpawnProperties(ClassToSpawn, Schema, CompilerContext, SourceGraph, LastThenPin, SpawnedActorReturnPin); } // ------------------------------------------- // Call FinishSpawning // ------------------------------------------- UK2Node_CallFunction* const CallPostSpawnnProxyObjectNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallPostSpawnnProxyObjectNode->FunctionReference.SetExternalMember(ProxyPostpawnFunctionName, ProxyClass); CallPostSpawnnProxyObjectNode->AllocateDefaultPins(); // Hook up the self connection UEdGraphPin* PostspawnCallSelfPin = Schema->FindSelfPin(*CallPostSpawnnProxyObjectNode, EGPD_Input); check(PostspawnCallSelfPin); bIsErrorFree &= Schema->TryCreateConnection(ProxyObjectPin, PostspawnCallSelfPin); // Link our Postspawn call in bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, CallPostSpawnnProxyObjectNode->FindPinChecked(Schema->PN_Execute)); // Hook up any other input parameters to PostSpawn for (auto CurrentPin : Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Input, Schema)) { UEdGraphPin* DestPin = CallPostSpawnnProxyObjectNode->FindPin(CurrentPin->PinName); if (DestPin) { bIsErrorFree &= CompilerContext.CopyPinLinksToIntermediate(*CurrentPin, *DestPin).CanSafeConnect(); } } } UEdGraphPin* InSpawnedActorPin = CallPostSpawnnProxyObjectNode->FindPin(TEXT("SpawnedActor")); if (InSpawnedActorPin == nullptr) { CompilerContext.MessageLog.Error(*LOCTEXT("MissingSpawnedActorInputPin", "AbilityTask: Proxy is missing SpawnedActor input pin in FinishSpawningActor. @@").ToString(), this); return; } bIsErrorFree &= Schema->TryCreateConnection(SpawnedActorReturnPin, InSpawnedActorPin); LastThenPin = CallPostSpawnnProxyObjectNode->FindPinChecked(Schema->PN_Then); // Move the connections from the original node then pin to the last internal then pin bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(Schema->PN_Then), *LastThenPin).CanSafeConnect(); bIsErrorFree &= CompilerContext.CopyPinLinksToIntermediate(*LastThenPin, *BranchElsePin).CanSafeConnect(); if (!bIsErrorFree) { CompilerContext.MessageLog.Error(*LOCTEXT("InternalConnectionError", "BaseAsyncTask: Internal connection error. @@").ToString(), this); } // Make sure we caught everything BreakAllNodeLinks(); }
void UK2Node_LatentAbilityCall::CreatePinsForClass(UClass* InClass) { check(InClass != NULL); const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); const UObject* const ClassDefaultObject = InClass->GetDefaultObject(false); SpawnParmPins.Empty(); // Tasks can hide spawn parameters by doing meta = (HideSpawnParms="PropertyA,PropertyB") // (For example, hide Instigator in situations where instigator is not relevant to your task) TArray<FString> IgnorePropertyList; { UFunction* ProxyFunction = ProxyFactoryClass->FindFunctionByName(ProxyFactoryFunctionName); FString IgnorePropertyListStr = ProxyFunction->GetMetaData(FName(TEXT("HideSpawnParms"))); if (!IgnorePropertyListStr.IsEmpty()) { IgnorePropertyListStr.ParseIntoArray(IgnorePropertyList, TEXT(","), true); } } for (TFieldIterator<UProperty> PropertyIt(InClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; UClass* PropertyClass = CastChecked<UClass>(Property->GetOuter()); const bool bIsDelegate = Property->IsA(UMulticastDelegateProperty::StaticClass()); const bool bIsExposedToSpawn = UEdGraphSchema_K2::IsPropertyExposedOnSpawn(Property); const bool bIsSettableExternally = !Property->HasAnyPropertyFlags(CPF_DisableEditOnInstance); if (bIsExposedToSpawn && !Property->HasAnyPropertyFlags(CPF_Parm) && bIsSettableExternally && Property->HasAllPropertyFlags(CPF_BlueprintVisible) && !bIsDelegate && !IgnorePropertyList.Contains(Property->GetName()) && (FindPin(Property->GetName()) == nullptr) ) { UEdGraphPin* Pin = CreatePin(EGPD_Input, TEXT(""), TEXT(""), NULL, false, false, Property->GetName()); const bool bPinGood = (Pin != NULL) && K2Schema->ConvertPropertyToPinType(Property, /*out*/ Pin->PinType); SpawnParmPins.Add(Pin); if (ClassDefaultObject && Pin && K2Schema->PinDefaultValueIsEditable(*Pin)) { FString DefaultValueAsString; const bool bDefaultValueSet = FBlueprintEditorUtils::PropertyValueToString(Property, reinterpret_cast<const uint8*>(ClassDefaultObject), DefaultValueAsString); check(bDefaultValueSet); K2Schema->TrySetDefaultValue(*Pin, DefaultValueAsString); } // Copy tooltip from the property. if (Pin != nullptr) { K2Schema->ConstructBasicPinTooltip(*Pin, Property->GetToolTipText(), Pin->PinToolTip); } } } }
void SGAAttributeWidget::Construct(const FArguments& InArgs) { AttributesList.Empty(); AttributesNodes.Empty(); OnAttributeSelected = InArgs._OnAttributeSelectedIn; /* Intended look: ClassName --CategoryName ----AttributeName Or: ClassName --CategoryName ----AttributeType ------AttributeName + search. We really need search! */ for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt) { UClass* Class = *ClassIt; if (Class->IsChildOf(UGAAttributesBase::StaticClass()) && !FKismetEditorUtilities::IsClassABlueprintSkeleton(Class)) { FString className = Class->GetName(); if (!className.Contains(TEXT("REINST_"))) { TSharedPtr<FGAAttributeNode> classRootNode = MakeShareable(new FGAAttributeNode()); classRootNode->Attribute = className; //first let's find root of tree, which is class name classRootNode->NodeName = className; FString Category; //now let's categories as childs for (TFieldIterator<UProperty> PropertyIt(Class, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt) { UProperty* Prop = *PropertyIt; Category = Prop->GetMetaData("Category"); //check if we already have this category added.. bool bFoundExistingCategory = false; if (!Category.IsEmpty()) { for (TSharedPtr<FGAAttributeNode> childNode : classRootNode->ChildNodes) { if (childNode->NodeName == Category) { bFoundExistingCategory = true; TSharedPtr<FGAAttributeNode> attrNode = MakeShareable(new FGAAttributeNode()); attrNode->NodeName = Prop->GetName(); attrNode->ParentNode = childNode; childNode->ChildNodes.Add(attrNode); } } if (!bFoundExistingCategory) { TSharedPtr<FGAAttributeNode> categoryNode = MakeShareable(new FGAAttributeNode()); categoryNode->NodeName = Category; categoryNode->ParentNode = classRootNode; classRootNode->ChildNodes.Add(categoryNode); TSharedPtr<FGAAttributeNode> attrNode = MakeShareable(new FGAAttributeNode()); attrNode->NodeName = Prop->GetName(); attrNode->ParentNode = categoryNode; categoryNode->ChildNodes.Add(attrNode); } } } AttributesNodes.Add(classRootNode); } } } ChildSlot [ SAssignNew(AttributeTreeWidget, STreeView<TSharedPtr<FGAAttributeNode>>) .OnSelectionChanged(this, &SGAAttributeWidget::OnItemSelected) .TreeItemsSource(&AttributesNodes) .OnGenerateRow(this, &SGAAttributeWidget::OnGenerateRow) .OnGetChildren(this, &SGAAttributeWidget::OnGetChildren) .OnExpansionChanged(this, &SGAAttributeWidget::OnExpansionChanged) .SelectionMode(ESelectionMode::Single) ]; }
static void RecursiveGet(const FEdMode& EdMode, const UStruct* InStruct, const void* InContainer, TArray<FEdMode::FPropertyWidgetInfo>& OutInfos, TArray<FPropertyWidgetInfoChainElement>& Chain) { for (TFieldIterator<UProperty> PropertyIt(InStruct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* CurrentProp = *PropertyIt; check(CurrentProp); if (EdMode.ShouldCreateWidgetForProperty(CurrentProp)) { if (UArrayProperty* ArrayProp = Cast<UArrayProperty>(CurrentProp)) { check(InContainer); FScriptArrayHelper_InContainer ArrayHelper(ArrayProp, InContainer); // See how many widgets we need to make for the array property const uint32 ArrayDim = ArrayHelper.Num(); for (uint32 Index = 0; Index < ArrayDim; ++Index) { OutInfos.Add(FPropertyWidgetInfoChainElement::CreateWidgetInfo(Chain, IsTransformProperty(ArrayProp->Inner), CurrentProp, Index)); } } else { OutInfos.Add(FPropertyWidgetInfoChainElement::CreateWidgetInfo(Chain, IsTransformProperty(CurrentProp), CurrentProp)); } } else if (UStructProperty* StructProp = Cast<UStructProperty>(CurrentProp)) { // Recursively traverse into structures, looking for additional vector properties to expose Chain.Push(FPropertyWidgetInfoChainElement(StructProp)); RecursiveGet(EdMode , StructProp->Struct , StructProp->ContainerPtrToValuePtr<void>(InContainer) , OutInfos , Chain); Chain.Pop(false); } else if (UArrayProperty* ArrayProp = Cast<UArrayProperty>(CurrentProp)) { // Recursively traverse into arrays of structures, looking for additional vector properties to expose UStructProperty* InnerStructProp = Cast<UStructProperty>(ArrayProp->Inner); if (InnerStructProp) { FScriptArrayHelper_InContainer ArrayHelper(ArrayProp, InContainer); // If the array is not empty the do additional check to tell if iteration is necessary if (ArrayHelper.Num() && ShouldCreateWidgetSomwhereInBranch(InnerStructProp)) { for (int32 ArrayIndex = 0; ArrayIndex < ArrayHelper.Num(); ++ArrayIndex) { Chain.Push(FPropertyWidgetInfoChainElement(ArrayProp, ArrayIndex)); RecursiveGet(EdMode , InnerStructProp->Struct , ArrayHelper.GetRawPtr(ArrayIndex) , OutInfos , Chain); Chain.Pop(false); } } } } } }