{"id":267,"date":"2024-08-10T19:59:55","date_gmt":"2024-08-10T19:59:55","guid":{"rendered":"https:\/\/www.thegames.dev\/?p=267"},"modified":"2024-08-13T10:51:53","modified_gmt":"2024-08-13T10:51:53","slug":"custom-k2-node-and-thunk-for-an-array-of-finstancedstruct","status":"publish","type":"post","link":"https:\/\/www.thegames.dev\/?p=267","title":{"rendered":"Custom K2 Node and Thunk for an Array of FInstancedStruct."},"content":{"rendered":"\n<p>FYI: This is good for stuff which is derived from a base struct type and you want to filter it.<\/p>\n\n\n\n<p>So there is going to be a lot of code here, and a few overrides of the engine, plus a custom module which is UncookedOnly for the K2Node and its stuff to live in. <\/p>\n\n\n\n<p class=\"has-large-font-size\">The Basics<\/p>\n\n\n\n<p>We will define a struct which will be our base, and 2 derived structs. We will hide the base struct from being selected as it should contain no properties.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Relevant Code:; notranslate\" title=\"Relevant Code:\">\nUSTRUCT(BlueprintType)\nstruct FMyBaseInstancedStruct\n{\n    GENERATED_BODY()\n};\n\nUSTRUCT(BlueprintType)\nstruct FMyChildAInstancedStruct : public FMyBaseInstancedStruct\n{\n   GENERATED_BODY();\n\npublic:\n    UPROPERTY(EditAnywhere, BlueprintReadOnly)\n    FText SomeText;\n};\n\nUSTRUCT(BlueprintType)\nstruct FMyChildBInstancedStruct : public FMyBaseInstancedStruct\n{\n   GENERATED_BODY();\n\npublic:\n    UPROPERTY(EditAnywhere, BlueprintReadOnly)\n    bool SomeBool;\n};\n<\/pre><\/div>\n\n\n<p>With these structs created and in your main module, lets make a DataAsset that will hold our TArray of FInstancedStruct.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Relevant Code:; notranslate\" title=\"Relevant Code:\">\nUENUM()\nenum class EMyFindInstanceStructResult : uint8\n{\n\tValid,\n\tNotValid,\n};\n\nUCLASS()\nclass UMyDataAsset : public UDataAsset\n{\n   GENERATED_BODY()\n\nprotected:\n    \/\/ Here we define the Array of FInstancedStructs we want, and we use two meta\n    \/\/ properties to define the type that can be used in this array, and exclude the base\n   \/\/ struct from being used.\n    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Data, meta = (BaseStruct = &quot;\/Script\/MyGameModule.MyBaseInstancedStruct&quot;, ExcludeBaseStruct))\n\tTArray&lt;FInstancedStruct&gt; MyData;\n\nprivate:\n    \/\/ Function called from our custom K2 Node. Uses a custom thunk.\n    \/\/ The Thunk will use the InstancedStructType to find the instanced struct\n    \/\/ in the array and set the value of Value. This will get converted to the\n    \/\/ the correct custom type (not here but in our K2Node), but the data in Value\n    \/\/\/ will be valid if the Instanced Struct was found.\n    UFUNCTION(BlueprintCallable, CustomThunk, Category = &quot;MyData&quot;, meta = (DisplayName = &quot;GetMyData&quot;, CustomStructureParam = &quot;Value&quot;, ExpandEnumAsExecs = &quot;FindResult&quot;, BlueprintInternalUseOnly=&quot;true&quot;))\n    void GetMyDataBP(EMyFindInstanceStructResult&amp; FindResult, UScriptStruct* InstancedStructType, int32&amp; Value);\n\n    \/\/Needed to declare our custom thunk\n\tDECLARE_FUNCTION(execGetMyDataBP);\n\n    \/\/ So our K2Node can access this private function, we don&#039;t want the function above\n    \/\/ called by anything BUT the K2Node.\n\tfriend class UK2Node_GetMyData;\n};\n<\/pre><\/div>\n\n\n<p>Now we need to implement our CustomThunk in the cpp file<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Relevant Code:; notranslate\" title=\"Relevant Code:\">\nvoid UMyDataAsset ::GetItemDataBP(EMyFindInstanceStructResult&amp; FindResult, UScriptStruct* InstancedStructType, int32&amp; Value)\n{\n    \/\/ We should never hit this! stubs to avoid NoExport on the class.\n    checkNoEntry();\n}\n\nDEFINE_FUNCTION(UMyDataAsset::execGetMyDataBP)\n{\n    \/\/Get the result enum (out ref)\n\tP_GET_ENUM_REF(EMyFindInstanceStructResult, FindResult);\n    \/\/ Get the struct type we want to match\n\tP_GET_OBJECT_REF(UScriptStruct, InstancedStructType);\n\n\t\/\/ Read wildcard Value input.\n\tStack.MostRecentPropertyAddress = nullptr;\n\tStack.MostRecentPropertyContainer = nullptr;\n\tStack.StepCompiledIn&lt;FStructProperty&gt;(nullptr);\n\t\n\tconst FStructProperty* ValueProp = CastField&lt;FStructProperty&gt;(Stack.MostRecentProperty);\n\tvoid* ValuePtr = Stack.MostRecentPropertyAddress;\n\n\tP_FINISH;\n\n    \/\/Set the result as Not Valid for starters\n\tFindResult = EMyFindInstanceStructResult::NotValid;\n\tP_NATIVE_BEGIN;\n\tfor (auto&amp; Item : P_THIS-&gt;MyData) \/\/Loop through our array on structs\n\t{\n        \/\/If its valid and its the correct type\n\t\tif (Item.IsValid() &amp;&amp; Item.GetScriptStruct() == ItemDataType)\n\t\t{\n            \/\/Copy the memory (data) to the Value out param\n\t\t\tValueProp-&gt;Struct-&gt;CopyScriptStruct(ValuePtr, Item.GetMemory());\n            \/\/ Set result as valid\n\t\t\tFindResult = EMyFindInstanceStructResult::Valid;\n            \/\/No more need to loop. break out.\n\t\t\tbreak;\n\t\t}\n\t}\n\tP_NATIVE_END;\n}\n<\/pre><\/div>\n\n\n<p class=\"has-x-large-font-size\">The K2Node<\/p>\n\n\n\n<p>You first need to make an UncookedOnly module in your plugin or project. You can refer to <a href=\"https:\/\/dev.epicgames.com\/documentation\/en-us\/unreal-engine\/unreal-engine-modules?application_version=4.27\">https:\/\/dev.epicgames.com\/documentation\/en-us\/unreal-engine\/unreal-engine-modules?application_version=4.27<\/a> if you need info on modules.<\/p>\n\n\n\n<p>Create 2 classes in your uncooked modules, one called <br>MyDataGraphPin and another called: K2Node_GetMyData.<br><br>MyDataGraphPin will define some filters for our K2Node pin and the K2Node will contain a very small amount of code to glue it all together.<\/p>\n\n\n\n<p class=\"has-large-font-size\">MyDataGraphPin<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Relevant Code:; notranslate\" title=\"Relevant Code:\">\n#include &quot;Framework\/SlateDelegates.h&quot;\n#include &quot;Input\/Reply.h&quot;\n#include &quot;Internationalization\/Text.h&quot;\n#include &quot;KismetPins\/SGraphPinObject.h&quot;\n#include &quot;Templates\/SharedPointer.h&quot;\n#include &quot;Widgets\/DeclarativeSyntaxSupport.h&quot;\n\nclass SWidget;\nclass UEdGraphPin;\nclass UScriptStruct;\n\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ SGraphPinStruct\n\nclass SMyDataGraphPin : public SGraphPinObject\n{\npublic:\n\tSLATE_BEGIN_ARGS(SMyDataGraphPin ) {}\n\tSLATE_END_ARGS()\n\n\tvoid Construct(const FArguments&amp; InArgs, UEdGraphPin* InGraphPinObj);\n\nprotected:\n\t\/\/ Called when a new struct was picked via the asset picker\n\tvoid OnPickedNewStruct(const UScriptStruct* ChosenStruct);\n\n\t\/\/~ Begin SGraphPinObject Interface\n\tvirtual FReply OnClickUse() override;\n\tvirtual bool AllowSelfPinWidget() const override { return false; }\n\tvirtual TSharedRef&lt;SWidget&gt; GenerateAssetPicker() override;\n\tvirtual FText GetDefaultComboText() const override;\n\tvirtual FOnClicked GetOnUseButtonDelegate() override;\n\t\/\/~ End SGraphPinObject Interface\n};\n<\/pre><\/div>\n\n\n<p>CPP File<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Relevant Code:; notranslate\" title=\"Relevant Code:\">\n#include &quot;Containers\/UnrealString.h&quot;\n#include &quot;Delegates\/Delegate.h&quot;\n#include &quot;EdGraph\/EdGraphPin.h&quot;\n#include &quot;EdGraph\/EdGraphSchema.h&quot;\n#include &quot;Editor.h&quot;\n#include &quot;InstancedStructDetails.h&quot;\n#include &quot;Editor\/EditorEngine.h&quot;\n#include &quot;Engine\/UserDefinedStruct.h&quot;\n#include &quot;Internationalization\/Internationalization.h&quot;\n#include &quot;Layout\/Margin.h&quot;\n#include &quot;Misc\/Attribute.h&quot;\n#include &quot;Modules\/ModuleManager.h&quot;\n#include &quot;SGraphPin.h&quot;\n#include &quot;ScopedTransaction.h&quot;\n#include &quot;Selection.h&quot;\n#include &quot;SlotBase.h&quot;\n#include &quot;StructViewerFilter.h&quot;\n#include &quot;StructViewerModule.h&quot;\n#include &quot;Styling\/AppStyle.h&quot;\n#include &quot;Types\/SlateStructs.h&quot;\n#include &quot;UObject\/Class.h&quot;\n#include &quot;UObject\/NameTypes.h&quot;\n#include &quot;Widgets\/Input\/SMenuAnchor.h&quot;\n#include &quot;Widgets\/Layout\/SBorder.h&quot;\n#include &quot;Widgets\/Layout\/SBox.h&quot;\n#include &quot;Widgets\/SBoxPanel.h&quot;\n\nclass SWidget;\nclass UObject;\n\n#define LOCTEXT_NAMESPACE &quot;SMyDataGraphPin &quot;\n\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ SMyDataGraphPin \n\nvoid SMyDataGraphPin::Construct(const FArguments&amp; InArgs, UEdGraphPin* InGraphPinObj)\n{\n\tSGraphPin::Construct(SGraphPin::FArguments(), InGraphPinObj);\n}\n\nFReply SMyDataGraphPin::OnClickUse()\n{\n\tFEditorDelegates::LoadSelectedAssetsIfNeeded.Broadcast();\n\n\tUObject* SelectedObject = GEditor-&gt;GetSelectedObjects()-&gt;GetTop(UScriptStruct::StaticClass());\n\tif (SelectedObject)\n\t{\n\t\tconst FScopedTransaction Transaction(NSLOCTEXT(&quot;GraphEditor&quot;, &quot;ChangeStructPinValue&quot;, &quot;Change Struct Pin Value&quot;));\n\t\tGraphPinObj-&gt;Modify();\n\n\t\tGraphPinObj-&gt;GetSchema()-&gt;TrySetDefaultObject(*GraphPinObj, SelectedObject);\n\t}\n\n\treturn FReply::Handled();\n}\n\nTSharedRef&lt;SWidget&gt; SMyDataGraphPin::GenerateAssetPicker()\n{\n\tFStructViewerModule&amp; StructViewerModule = FModuleManager::LoadModuleChecked&lt;FStructViewerModule&gt;(&quot;StructViewer&quot;);\n\n\t\/\/ Fill in options\n\tFStructViewerInitializationOptions Options;\n\tOptions.Mode = EStructViewerMode::StructPicker;\n\tOptions.bShowNoneOption = true;\n\n\t\/\/ Set your instanced struct here!\n\tconst UScriptStruct* MetaStruct = FMyBaseInstancedStruct::StaticStruct();\n\n    \/\/We use FInstancedStructFilter cause its convienient\n\tTSharedRef&lt;FInstancedStructFilter&gt; StructFilter = MakeShared&lt;FInstancedStructFilter&gt;();\n\tOptions.StructFilter = StructFilter;\n\tStructFilter-&gt;BaseStruct = MetaStruct;\n\tStructFilter-&gt;bAllowBaseStruct = false;\n\n\treturn\n\t\tSNew(SBox)\n\t\t.WidthOverride(280)\n\t\t&#x5B;\n\t\t\tSNew(SVerticalBox)\n\n\t\t\t+ SVerticalBox::Slot()\n\t\t\t.FillHeight(1.0f)\n\t\t\t.MaxHeight(500)\n\t\t\t&#x5B; \n\t\t\t\tSNew(SBorder)\n\t\t\t\t.Padding(4)\n\t\t\t\t.BorderImage( FAppStyle::GetBrush(&quot;ToolPanel.GroupBorder&quot;) )\n\t\t\t\t&#x5B;\n\t\t\t\t\tStructViewerModule.CreateStructViewer(Options, FOnStructPicked::CreateSP(this, &amp;SMyDataGraphPin::OnPickedNewStruct))\n\t\t\t\t]\n\t\t\t]\t\t\t\n\t\t];\n}\n\nFOnClicked SMyDataGraphPin::GetOnUseButtonDelegate()\n{\n\treturn FOnClicked::CreateSP(this, &amp;SMyDataGraphPin::OnClickUse);\n}\n\nvoid SMyDataGraphPin::OnPickedNewStruct(const UScriptStruct* ChosenStruct)\n{\n\tif(GraphPinObj-&gt;IsPendingKill())\n\t{\n\t\treturn;\n\t}\n\n\tFString NewPath;\n\tif (ChosenStruct)\n\t{\n\t\tNewPath = ChosenStruct-&gt;GetPathName();\n\t}\n\n\tif (GraphPinObj-&gt;GetDefaultAsString() != NewPath)\n\t{\n\t\tconst FScopedTransaction Transaction( NSLOCTEXT(&quot;GraphEditor&quot;, &quot;ChangeStructPinValue&quot;, &quot;Change Struct Pin Value&quot; ) );\n\t\tGraphPinObj-&gt;Modify();\n\n\t\tAssetPickerAnchor-&gt;SetIsOpen(false);\n\t\tGraphPinObj-&gt;GetSchema()-&gt;TrySetDefaultObject(*GraphPinObj, const_cast&lt;UScriptStruct*&gt;(ChosenStruct));\n\t}\n}\n\nFText SMyDataGraphPin::GetDefaultComboText() const\n{ \n\treturn LOCTEXT(&quot;DefaultComboText&quot;, &quot;Select Struct&quot;);\n}\n\n#undef LOCTEXT_NAMESPACE\n<\/pre><\/div>\n\n\n<p>I won&#8217;t go over every detail in the above, but inside the function GenerateAssetPicker you will see the struct defined here.<\/p>\n\n\n\n<p class=\"has-large-font-size\">K2Node_GetMyData<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Relevant Code:; notranslate\" title=\"Relevant Code:\">\nstruct FMyInstancedStructPinFactory : public FGraphPanelPinFactory\n{\npublic:\n\tvirtual TSharedPtr&lt;class SGraphPin&gt; CreatePin(class UEdGraphPin* Pin) const override;\n};\n\n\/**\n * \n *\/\nUCLASS()\nclass UK2Node_GetMyData : public UK2Node_CallFunction\n{\n\tGENERATED_BODY()\npublic:\n\tvirtual void GetMenuActions(FBlueprintActionDatabaseRegistrar&amp; ActionRegistrar) const override;\n\tvirtual void PinDefaultValueChanged(UEdGraphPin* ChangedPin) override;\n\tvirtual void PostReconstructNode() override;\n\tvirtual void ClearCachedBlueprintData(UBlueprint* Blueprint) override;\n\tvoid RefreshOutputStructType();\n};\n<\/pre><\/div>\n\n\n<p>The CPP file<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Relevant Code:; notranslate\" title=\"Relevant Code:\">\nTSharedPtr&lt;class SGraphPin&gt; FMyInstancedStructPinFactory::CreatePin(class UEdGraphPin* Pin) const\n{\n\t if (Pin-&gt;PinType.PinCategory == UEdGraphSchema_K2::PC_Object)\n\t{\n        \/\/Only if the node is our special custom K2Node.\n\t\tif (UK2Node_GetMyData* Node = Cast&lt;UK2Node_GetMyData&gt;(Pin-&gt;GetOwningNode()))\n\t\t{\n            \/\/Only use this custom graph slate if its the pin we want to use it on\n            \/\/this must be the same name as your UScriptStruct* pointer name in your\n            \/\/custom thunk!\n\t\t\tif (Pin-&gt;PinName == &quot;InstancedStructType&quot;)\n\t\t\t{\n               \/\/Return our custom GraphPin we made earlier.\n\t\t\t\treturn SNew(SMyDataGraphPin, Pin);\n\t\t\t}\n\t\t}\n\t}\n    \/\/Let other filters try we dont want it.\n\treturn nullptr;\n}\n\n\nvoid UK2Node_GetMyData::GetMenuActions(FBlueprintActionDatabaseRegistrar&amp; ActionRegistrar) const\n{\n\tSuper::GetMenuActions(ActionRegistrar);\n\tUClass* Action = GetClass();\n\tif (ActionRegistrar.IsOpenForRegistration(Action))\n\t{\n\t\tauto CustomizeLambda = &#x5B;](UEdGraphNode* NewNode, bool bIsTemplateNode, const FName FunctionName)\n\t\t{\n\t\t\tUK2Node_GetMyData* Node = CastChecked&lt;UK2Node_GetMyData&gt;(NewNode);\n\t\t\tUFunction* Function = UMyData::StaticClass()-&gt;FindFunctionByName(FunctionName);\n\t\t\tcheck(Function);\n\t\t\tNode-&gt;SetFromFunction(Function);\n\t\t};\n\t\t\n\t\t\/\/ Our custom thunk\n\t\tUBlueprintNodeSpawner* GetNodeSpawner = UBlueprintNodeSpawner::Create(GetClass());\n\t\tcheck(GetNodeSpawner != nullptr);\n\t\tGetNodeSpawner-&gt;CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeLambda, GET_FUNCTION_NAME_CHECKED(UMyData, GetMyDataBP));\n\t\tActionRegistrar.AddBlueprintAction(Action, GetNodeSpawner);\n\t}\n}\n\nvoid UK2Node_GetMyData::PinDefaultValueChanged(UEdGraphPin* ChangedPin)\n{\n\tSuper::PinDefaultValueChanged(ChangedPin);\n\t\/\/Refresh our wildcard pin if the default value is changed\n\tif (ChangedPin-&gt;PinName == &quot;InstancedStructType&quot;)\n\t{\n\t\tif (ChangedPin-&gt;LinkedTo.Num() == 0)\n\t\t{\n\t\t\tRefreshOutputStructType();\n\t\t}\n\t}\n}\n\nvoid UK2Node_GetMyData::PostReconstructNode()\n{\n\tSuper::PostReconstructNode();\n    \/\/Refresh our wildcard pin if the node has been rebuilt\n\tRefreshOutputStructType();\n}\n\nvoid UK2Node_GetMyData::ClearCachedBlueprintData(UBlueprint* Blueprint)\n{\n\tSuper::ClearCachedBlueprintData(Blueprint);\n    \/\/Refresh our wildcard pin if the cached data has been cleared\n\tRefreshOutputStructType();\n}\n\nvoid UK2Node_GetMyData::RefreshOutputStructType()\n{\n\tauto GetPinForMe = &#x5B;this] (FName PinName) { \n\t\tUEdGraphPin* Pin = FindPinChecked(PinName);\n\t\treturn Pin;\n\t};\n\n    \/\/Grab the value pin (the return pin with our data\n\tUEdGraphPin* ValuePin = GetPinForMe(&quot;Value&quot;);\n\n    \/\/Our type struct pin\n\tUEdGraphPin* StructTypePin =  GetPinForMe(&quot;InstancedStructType&quot;);\n\n\tif (StructTypePin-&gt;DefaultObject != ValuePin-&gt;PinType.PinSubCategoryObject)\n\t{\n\t\tif (ValuePin-&gt;SubPins.Num() &gt; 0)\n\t\t{    \/\/If the pin has been broken (split), recombine it.\n             GetSchema()-&gt;RecombinePin(ValuePin);\n\t\t}\n       \/\/Set the value of our value pin to your selected InstancedStructType\n\t\tValuePin-&gt;PinType.PinSubCategoryObject = StructTypePin-&gt;DefaultObject;\n\t\tValuePin-&gt;PinType.PinCategory = (StructTypePin-&gt;DefaultObject == nullptr) ? UEdGraphSchema_K2::PC_Wildcard : UEdGraphSchema_K2::PC_Struct;\n\t}\n}\n<\/pre><\/div>\n\n\n<p>One last step is to register our custom pin factory, in your uncooked only modules StartupModule and ShutdownModule<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Relevant Code:; notranslate\" title=\"Relevant Code:\">\nvoid FMyUncookedOnlyModule::StartupModule()\n{\n\tMyInstancedStructPinFactory = MakeShared&lt;FMyInstancedStructPinFactory&gt;();\n\tFEdGraphUtilities::RegisterVisualPinFactory(MyInstancedStructPinFactory );\n}\n\nvoid FMyUncookedOnlyModule::ShutdownModule()\n{\n\tFEdGraphUtilities::UnregisterVisualPinFactory(MyInstancedStructPinFactory);\n}\n\n\/\/Place the following in the header:\nTSharedPtr&lt;FMyInstancedStructPinFactory&gt; MyInstancedStructPinFactory;\n<\/pre><\/div>\n\n\n<p>Well that was a lot but, that should be everything to make it work.<\/p>\n\n\n\n<p>I will provide some screenshots in a bit of it in action \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>FYI: This is good for stuff which is derived from a base struct type and you want to filter it. So there is going to be a lot of code here, and a few overrides of the engine, plus a custom module which is UncookedOnly for the K2Node and its stuff to live in. The <a href=\"https:\/\/www.thegames.dev\/?p=267\" class=\"more-link\">&#8230;<span class=\"screen-reader-text\">  Custom K2 Node and Thunk for an Array of FInstancedStruct.<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,2],"tags":[],"class_list":["post-267","post","type-post","status-publish","format-standard","hentry","category-game-framework","category-unreal-engine"],"rise-blocks_total_comments":0,"rise-blocks_categories":[{"term_id":3,"name":"Game Framework","slug":"game-framework","term_group":0,"term_taxonomy_id":3,"taxonomy":"category","description":"Unreal Engine Game Framework","parent":2,"count":2,"filter":"raw","cat_ID":3,"category_count":2,"category_description":"Unreal Engine Game Framework","cat_name":"Game Framework","category_nicename":"game-framework","category_parent":2},{"term_id":2,"name":"Unreal Engine","slug":"unreal-engine","term_group":0,"term_taxonomy_id":2,"taxonomy":"category","description":"All unreal engine related tutorials, tips and tricks.","parent":0,"count":5,"filter":"raw","cat_ID":2,"category_count":5,"category_description":"All unreal engine related tutorials, tips and tricks.","cat_name":"Unreal Engine","category_nicename":"unreal-engine","category_parent":0}],"rise-blocks_excerpt":"FYI: This is good for stuff which is derived from a base struct type and you want to filter it. So there is going to be a lot of code here, and a few overrides of the engine, plus a custom module which is UncookedOnly for the K2Node and its stuff to live in. The Basics We will define a..","_links":{"self":[{"href":"https:\/\/www.thegames.dev\/index.php?rest_route=\/wp\/v2\/posts\/267","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.thegames.dev\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.thegames.dev\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.thegames.dev\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.thegames.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=267"}],"version-history":[{"count":8,"href":"https:\/\/www.thegames.dev\/index.php?rest_route=\/wp\/v2\/posts\/267\/revisions"}],"predecessor-version":[{"id":275,"href":"https:\/\/www.thegames.dev\/index.php?rest_route=\/wp\/v2\/posts\/267\/revisions\/275"}],"wp:attachment":[{"href":"https:\/\/www.thegames.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=267"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.thegames.dev\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=267"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.thegames.dev\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=267"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}