関連ブログ
- [UE4][UE5]開発環境の容量を少しでも減らす 2024.08.14UE
- [UE5] PushModel型のReplicationを使い、ネットワーク最適化を図る 2024.05.29UE
- [UE5]マテリアルでメッシュをスケールする方法 2024.01.17UE
CATEGORY
2015.12.04UE4UE/ C++
改訂バージョン: Unreal Engine 4.21 |
こちらは UE4 Advent Calendar 2015 4日目の記事です。
プログラマ向けの内容となります。
解説編をまだ読んでいない方はコチラ。
今回は実装サンプルとして、以下のようなカスタムノードを作ってみました。
こんなノードがあって、
SampleDataA を選択すると、SampleDataSettingA を引数に取る設定コピーノードになります。
出力の型は SampleDataA のリファレンスです。
同じような SampleDataB もあります。
もちろん SampleDataA に対応するものに対して SampleDataB のものは入力できません。
これを実装すると、以下のようになります。
詳しい解説は大きくなりすぎるので割愛しますが、気になる方は読んでみてください。
まず、SampleData 系の定義です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include “CoreMinimal.h” #include “Object.h” #include “SampleData.generated.h” UCLASS() class MYPROJECT_API USampleData : public UObject { GENERATED_BODY() public: // 設定クラス取得 virtual UClass* GetSettingClass() const { return nullptr; } }; //////////////////////////////////////////////////////////////////////////////// // サンプルデータA UCLASS() class MYPROJECT_API USampleDataSettingA : public UObject { GENERATED_BODY() public: int32 PropertyA; }; UCLASS() class MYPROJECT_API USampleDataA : public USampleData { GENERATED_BODY() virtual void PostInitProperties() override { Super::PostInitProperties(); if (!HasAnyFlags(RF_ClassDefaultObject)) { Setting = NewObject(GetTransientPackage(), USampleDataSettingA::StaticClass()); } } public: // 設定コピー関数 void CopySettingA(const USampleDataSettingA& InSetting) { UE_LOG(LogTemp, Log, TEXT(“ * ******Copy SettingA”)); Setting->PropertyA = InSetting.PropertyA; } // 静的設定コピー関数(IntermediateNodeによる呼び出し用) UFUNCTION(BlueprintCallable, Category = “Sample”, meta = (BlueprintInternalUseOnly = “true”)) static USampleDataA* CopySettingA_Static(USampleDataA* Target, const USampleDataSettingA* Setting) { if (Target == nullptr || Setting == nullptr) { return Target; } Target->CopySettingA(*Setting); return Target; } // 設定クラス取得 virtual UClass* GetSettingClass() const override { return USampleDataSettingA::StaticClass(); } private: UPROPERTY() USampleDataSettingA* Setting; }; //////////////////////////////////////////////////////////////////////////////// // サンプルデータB UCLASS() class MYPROJECT_API USampleDataSettingB : public UObject { GENERATED_BODY() public: float PropertyB; }; UCLASS() class MYPROJECT_API USampleDataB : public USampleData { GENERATED_BODY() virtual void PostInitProperties() override { Super::PostInitProperties(); if (!HasAnyFlags(RF_ClassDefaultObject)) { Setting = NewObject(GetTransientPackage(), USampleDataSettingB::StaticClass()); } } public: // 設定コピー関数 void CopySettingB(const USampleDataSettingB& InSetting) { UE_LOG(LogTemp, Log, TEXT(“ * ******Copy SettingB”)); Setting->PropertyB = InSetting.PropertyB; } // 静的設定コピー関数(IntermediateNodeによる呼び出し用) UFUNCTION(BlueprintCallable, Category = “Sample”, meta = (BlueprintInternalUseOnly = “true”)) static USampleDataB* CopySettingB_Static(USampleDataB* Target, const USampleDataSettingB* Setting) { if (Target == nullptr || Setting == nullptr) { return Target; } Target->CopySettingB(*Setting); return Target; } // 設定クラス取得 virtual UClass* GetSettingClass() const override { return USampleDataSettingB::StaticClass(); } private: UPROPERTY() USampleDataSettingB* Setting; }; |
そして上で例に挙げたコピーノードを作成するノードクラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include “CoreMinimal.h” #include “K2Node.h” #include “EdGraph/EdGraphNodeUtils.h” // for FNodeTextCache #include “K2Node_CopySampleDataSetting.generated.h” UCLASS() class MYPROJECTEDITOR_API UK2Node_CopySampleDataSetting : public UK2Node { GENERATED_UCLASS_BODY() // Begin UEdGraphNode interface. virtual void AllocateDefaultPins() override; virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; virtual void PinDefaultValueChanged(UEdGraphPin* Pin) override; virtual FText GetTooltipText() const override; virtual bool HasExternalDependencies(TArray* OptionalOutput) const override; virtual bool IsCompatibleWithGraph(const UEdGraph* TargetGraph) const override; virtual void PinConnectionListChanged(UEdGraphPin* Pin); // End UEdGraphNode interface. // Begin UK2Node interface virtual bool IsNodeSafeToIgnore() const override { return true; } virtual void ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins) override; virtual void GetNodeAttributes(TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes) const override; virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override; virtual FText GetMenuCategory() const override; virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override; // End UK2Node interface private: void CreatePinsForClass(UClass* InClass, TArray<UEdGraphPin*>* OutClassPins = nullptr); UEdGraphPin* GetClassPin(const TArray<UEdGraphPin*>* InPinsToSearch = NULL) const; UEdGraphPin* GetTargetPin() const; UEdGraphPin* GetSettingPin() const; UEdGraphPin* GetThenPin() const; UEdGraphPin* GetResultPin() const; UClass* GetSampleDataClass(const TArray<UEdGraphPin*>* InPinsToSearch = NULL) const; UClass* GetClassPinBaseClass() const; bool IsSpawnVarPin(UEdGraphPin* Pin); void SetPinToolTip(UEdGraphPin& MutatablePin, const FText& PinDescription) const; void OnClassPinChanged(); private: FNodeTextCache CachedNodeTitle; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
// Fill out your copyright notice in the Description page of Project Settings. // Fill out your copyright notice in the Description page of Project Settings. #include “K2Node_CopySampleDataSetting.h” #include “MyProjectEditor.h” #include “GraphEditorSettings.h” #include “BlueprintGraphDefinitions.h” #include “BlueprintUtilities.h” #include “Kismet2/BlueprintEditorUtils.h” #include “UnrealEd.h” #include “EditorStyle.h” #include “KismetCompiler.h” #include “BlueprintNodeSpawner.h” #include “EditorCategoryUtils.h” #include “BlueprintActionDatabaseRegistrar.h” #include “SampleData.h” #define LOCTEXT_NAMESPACE “SampleData” struct FK2Node_CopySampleDataSettingHelper { static FString ClassPinName; static FString TargetPinName; static FString SettingPinName; }; FString FK2Node_CopySampleDataSettingHelper::ClassPinName(TEXT(“Class”)); FString FK2Node_CopySampleDataSettingHelper::TargetPinName(TEXT(“Target”)); FString FK2Node_CopySampleDataSettingHelper::SettingPinName(TEXT(“Setting”)); UK2Node_CopySampleDataSetting::UK2Node_CopySampleDataSetting(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } void UK2Node_CopySampleDataSetting::AllocateDefaultPins() { const UEdGraphSchema_K2* K2Schema = GetDefault(); // 入力クラスによって変更されないピン情報はここに記述 // 入力実行ピン CreatePin(EGPD_Input, K2Schema->PC_Exec, TEXT(“”), NULL, false, false, K2Schema->PN_Execute); // 出力実行ピン CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(“”), NULL, false, false, K2Schema->PN_Then); // コピー対象クラス UEdGraphPin* ClassPin = CreatePin(EGPD_Input, K2Schema->PC_Class, TEXT(“”), GetClassPinBaseClass(), false, false, FK2Node_CopySampleDataSettingHelper::ClassPinName); SetPinToolTip(*ClassPin, LOCTEXT(“ClassPinDescription”, “Copy target class”)); Super::AllocateDefaultPins(); } FText UK2Node_CopySampleDataSetting::GetNodeTitle(ENodeTitleType::Type TitleType) const { if (TitleType == ENodeTitleType::ListView || TitleType == ENodeTitleType::MenuTitle) { return LOCTEXT(“GetNodeTitleMenu”, “Copy Sample Data Setting”); } else if (auto SampleDataClass = GetSampleDataClass()) { if (CachedNodeTitle.IsOutOfDate(this)) { FFormatNamedArguments Args; Args.Add(TEXT(“ClassName”), SampleDataClass->GetDisplayNameText()); CachedNodeTitle.SetCachedText(FText::Format(LOCTEXT(“GetNodeTitleFormat”, “{ ClassName } Copy the settings”), Args), this); } return CachedNodeTitle; } return LOCTEXT(“GetNodeTitleDefault”, “Copy sample data setting”); } void UK2Node_CopySampleDataSetting::PinDefaultValueChanged(UEdGraphPin* ChangedPin) { if (ChangedPin && (ChangedPin->PinName == FName(*FK2Node_CopySampleDataSettingHelper::ClassPinName))) { OnClassPinChanged(); } } FText UK2Node_CopySampleDataSetting::GetTooltipText() const { return LOCTEXT(“NodeTooltip”, “Copy sample data setting”); } bool UK2Node_CopySampleDataSetting::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const { UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph); return Super::IsCompatibleWithGraph(TargetGraph) && (!Blueprint || FBlueprintEditorUtils::FindUserConstructionScript(Blueprint) != TargetGraph); /*return false;*/ } bool UK2Node_CopySampleDataSetting::HasExternalDependencies(TArray* OptionalOutput) const { UClass* SourceClass = GetSampleDataClass(); const UBlueprint* SourceBlueprint = GetBlueprint(); const bool bResult = (SourceClass != NULL) && (SourceClass->ClassGeneratedBy != SourceBlueprint); if (bResult && OptionalOutput) { OptionalOutput->AddUnique(SourceClass); } const bool bSuperResult = Super::HasExternalDependencies(OptionalOutput); return bSuperResult || bResult; /*return false;*/ } void UK2Node_CopySampleDataSetting::PinConnectionListChanged(UEdGraphPin* Pin) { if (Pin && (Pin->PinName == FName(*FK2Node_CopySampleDataSettingHelper::ClassPinName))) { OnClassPinChanged(); } } void UK2Node_CopySampleDataSetting::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins) { AllocateDefaultPins(); UClass* SampleDataClass = GetSampleDataClass(&OldPins); if (SampleDataClass != NULL) { CreatePinsForClass(SampleDataClass); } } void UK2Node_CopySampleDataSetting::GetNodeAttributes(TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes) const { UClass* SampleDataClass = GetSampleDataClass(); const FString SampleDataClassStr = SampleDataClass ? SampleDataClass->GetName() : TEXT(“InvalidClass”); OutNodeAttributes.Add(TKeyValuePair<FString, FString>(TEXT(“Type”), TEXT(“CopySampleDataSetting”))); OutNodeAttributes.Add(TKeyValuePair<FString, FString>(TEXT(“Class”), GetClass()->GetName())); OutNodeAttributes.Add(TKeyValuePair<FString, FString>(TEXT(“Name”), GetName())); OutNodeAttributes.Add(TKeyValuePair<FString, FString>(TEXT(“ObjectClass”), SampleDataClassStr)); } void UK2Node_CopySampleDataSetting::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const { UClass* ActionKey = GetClass(); if (ActionRegistrar.IsOpenForRegistration(ActionKey)) { UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); check(NodeSpawner != nullptr); ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner); } } FText UK2Node_CopySampleDataSetting::GetMenuCategory() const { return LOCTEXT(“MenuCategory”, “Sample”); } void UK2Node_CopySampleDataSetting::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* TargetGraph) { Super::ExpandNode(CompilerContext, TargetGraph); static FName CopySettingA_FunctionName = GET_FUNCTION_NAME_CHECKED(USampleDataA, CopySettingA_Static); static FName CopySettingB_FunctionName = GET_FUNCTION_NAME_CHECKED(USampleDataB, CopySettingB_Static); static FString Class_ParamName = FString(TEXT(“Class”)); static FString Target_ParamName = FString(TEXT(“Target”)); static FString Setting_ParamName = FString(TEXT(“Setting”)); UK2Node_CopySampleDataSetting* CopySampleDataSettingNode = this; UEdGraphPin* NodeExec = CopySampleDataSettingNode->GetExecPin(); UEdGraphPin* ClassPin = CopySampleDataSettingNode->GetClassPin(); UEdGraphPin* TargetPin = CopySampleDataSettingNode->GetTargetPin(); UEdGraphPin* SettingPin = CopySampleDataSettingNode->GetSettingPin(); UEdGraphPin* NodeThen = CopySampleDataSettingNode->GetThenPin(); UEdGraphPin* NodeResult = CopySampleDataSettingNode->GetResultPin(); UClass* SampleDataClass = (ClassPin != NULL) ? Cast(ClassPin->DefaultObject) : NULL; if ((0 == ClassPin->LinkedTo.Num()) && (NULL == SampleDataClass)) { CompilerContext.MessageLog.Error(*LOCTEXT(“CopySampleDataSettingNodeMissingClass_Error”, “Class not set”).ToString(), CopySampleDataSettingNode); CopySampleDataSettingNode->BreakAllNodeLinks(); return; } if (SampleDataClass != USampleDataA::StaticClass() && SampleDataClass != USampleDataB::StaticClass()) { CompilerContext.MessageLog.Error(*LOCTEXT(“CopySampleDataSettingNodeMissingClass_Error”, “Invalid class”).ToString(), CopySampleDataSettingNode); CopySampleDataSettingNode->BreakAllNodeLinks(); return; } ////////////////////////////////////////////////////////////////////////// // ‘USampleData::CopySettingStatic’ を呼び出すノードを作成 UK2Node_CallFunction* CallCopyNode = CompilerContext.SpawnIntermediateNode(CopySampleDataSettingNode, TargetGraph); if (SampleDataClass == USampleDataA::StaticClass()) { CallCopyNode->FunctionReference.SetExternalMember(CopySettingA_FunctionName, SampleDataClass); } else if (SampleDataClass == USampleDataB::StaticClass()) { CallCopyNode->FunctionReference.SetExternalMember(CopySettingB_FunctionName, SampleDataClass); } CallCopyNode->AllocateDefaultPins(); UEdGraphPin* CallCopyExec = CallCopyNode->GetExecPin(); UEdGraphPin* CallCopyTargetPin = CallCopyNode->FindPinChecked(Target_ParamName); UEdGraphPin* CallCopySettingPin = CallCopyNode->FindPinChecked(Setting_ParamName); UEdGraphPin* CallCopyResult = CallCopyNode->GetReturnValuePin(); CompilerContext.MovePinLinksToIntermediate(*NodeExec, *CallCopyExec); if (TargetPin) { CompilerContext.MovePinLinksToIntermediate(*TargetPin, *CallCopyTargetPin); } if (SettingPin) { CompilerContext.MovePinLinksToIntermediate(*SettingPin, *CallCopySettingPin); } if (NodeResult) { CallCopyResult->PinType = NodeResult->PinType; CompilerContext.MovePinLinksToIntermediate(*NodeResult, *CallCopyResult); } ////////////////////////////////////////////////////////////////////////// // 出力実行ピンに、’USampleData::CopySettingStatic’ 実行結果ピンを紐付ける UEdGraphPin* LastThen = FKismetCompilerUtilities::GenerateAssignmentNodes(CompilerContext, TargetGraph, CallCopyNode, CopySampleDataSettingNode, CallCopyResult, GetSampleDataClass()); CompilerContext.MovePinLinksToIntermediate(*NodeThen, *LastThen); // 処理が終了したのでリンクを外す CopySampleDataSettingNode->BreakAllNodeLinks(); } void UK2Node_CopySampleDataSetting::CreatePinsForClass(UClass* InClass, TArray<UEdGraphPin*>* OutClassPins) { check(InClass != NULL); const UEdGraphSchema_K2* K2Schema = GetDefault(); const USampleData* ClassDefaultObject = CastChecked(InClass->GetDefaultObject(false)); // 入力クラスによって変更するピン情報はここに記述 // コピー先インスタンス UEdGraphPin* TargetPin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(“”), InClass, false, false, FK2Node_CopySampleDataSettingHelper::TargetPinName); SetPinToolTip(*TargetPin, LOCTEXT(“TargetPinDescription”, “Instance before copying”)); // 出力ピン UEdGraphPin* ResultPin = CreatePin(EGPD_Output, K2Schema->PC_Object, TEXT(“”), InClass, false, false, K2Schema->PN_ReturnValue); SetPinToolTip(*ResultPin, LOCTEXT(“ResultPinDescription”, “Instance after copying”)); if (OutClassPins) { OutClassPins->Add(TargetPin); OutClassPins->Add(ResultPin); } // コピーする設定 UClass* SettingClass = ClassDefaultObject->GetSettingClass(); if (SettingClass) { UEdGraphPin* SettingPin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(“”), ClassDefaultObject->GetSettingClass(), false, false, FK2Node_CopySampleDataSettingHelper::SettingPinName); SetPinToolTip(*SettingPin, LOCTEXT(“SettingPinDescription”, “Settings to copy”)); if (OutClassPins) { OutClassPins->Add(SettingPin); } } } UEdGraphPin* UK2Node_CopySampleDataSetting::GetClassPin(const TArray<UEdGraphPin*>* InPinsToSearch /*= NULL*/) const { const TArray<UEdGraphPin*>* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins; UEdGraphPin* Pin = NULL; for (auto PinIt = PinsToSearch->CreateConstIterator(); PinIt; ++PinIt) { UEdGraphPin* TestPin = *PinIt; if (TestPin && TestPin->PinName == FName(*FK2Node_CopySampleDataSettingHelper::ClassPinName)) { Pin = TestPin; break; } } check(Pin == NULL || Pin->Direction == EGPD_Input); return Pin; //return nullptr; } UEdGraphPin* UK2Node_CopySampleDataSetting::GetTargetPin() const { UEdGraphPin* Pin = FindPin(FK2Node_CopySampleDataSettingHelper::TargetPinName); check(Pin == NULL || Pin->Direction == EGPD_Input); return Pin; //return nullptr; } UEdGraphPin* UK2Node_CopySampleDataSetting::GetSettingPin() const { UEdGraphPin* Pin = FindPin(FK2Node_CopySampleDataSettingHelper::SettingPinName); check(Pin == NULL || Pin->Direction == EGPD_Input); return Pin; //return nullptr; } UEdGraphPin* UK2Node_CopySampleDataSetting::GetThenPin()const { const UEdGraphSchema_K2* K2Schema = GetDefault(); UEdGraphPin* Pin = FindPinChecked(K2Schema->PN_Then); check(Pin->Direction == EGPD_Output); return Pin; //return nullptr; } UEdGraphPin* UK2Node_CopySampleDataSetting::GetResultPin() const { const UEdGraphSchema_K2* K2Schema = GetDefault(); UEdGraphPin* Pin = FindPin(K2Schema->PN_ReturnValue); check(Pin == NULL || Pin->Direction == EGPD_Output); return Pin; //return nullptr; } UClass* UK2Node_CopySampleDataSetting::GetSampleDataClass(const TArray<UEdGraphPin*>* InPinsToSearch /*=NULL*/) const { UClass* SampleDataClass = NULL; const TArray<UEdGraphPin*>* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins; UEdGraphPin* ClassPin = GetClassPin(PinsToSearch); if (ClassPin && ClassPin->DefaultObject != NULL && ClassPin->LinkedTo.Num() == 0) { SampleDataClass = CastChecked(ClassPin->DefaultObject); } else if (ClassPin && ClassPin->LinkedTo.Num()) { auto ClassTarget = ClassPin->LinkedTo[0]; SampleDataClass = ClassTarget ? Cast(ClassTarget->PinType.PinSubCategoryObject.Get()) : nullptr; } return SampleDataClass; //return nullptr; } UClass* UK2Node_CopySampleDataSetting::GetClassPinBaseClass() const { return USampleData::StaticClass(); } bool UK2Node_CopySampleDataSetting::IsSpawnVarPin(UEdGraphPin* Pin) { const UEdGraphSchema_K2* K2Schema = GetDefault(); return(Pin->PinName != K2Schema->PN_Execute && Pin->PinName != K2Schema->PN_Then && Pin->PinName != FName(*FK2Node_CopySampleDataSettingHelper::ClassPinName)); //return false; } void UK2Node_CopySampleDataSetting::SetPinToolTip(UEdGraphPin& MutatablePin, const FText& PinDescription) const { MutatablePin.PinToolTip = UEdGraphSchema_K2::TypeToText(MutatablePin.PinType).ToString(); UEdGraphSchema_K2 const* const K2Schema = Cast(GetSchema()); if (K2Schema != nullptr) { MutatablePin.PinToolTip += TEXT(” “); MutatablePin.PinToolTip += K2Schema->GetPinDisplayName(&MutatablePin).ToString(); } MutatablePin.PinToolTip += FString(TEXT(“\n”)) + PinDescription.ToString(); } void UK2Node_CopySampleDataSetting::OnClassPinChanged() { const UEdGraphSchema_K2* K2Schema = GetDefault(); TArray<UEdGraphPin*> OldPins = Pins; TArray<UEdGraphPin*> OldClassPins; for (int32 i = 0; i < OldPins.Num(); i++) { UEdGraphPin* OldPin = OldPins[i]; if (IsSpawnVarPin(OldPin)) { Pins.Remove(OldPin); OldClassPins.Add(OldPin); } } CachedNodeTitle.MarkDirty(); UClass* SampleDataClass = GetSampleDataClass(); TArray<UEdGraphPin*> NewClassPins; if (SampleDataClass != NULL) { CreatePinsForClass(SampleDataClass, &NewClassPins); } RewireOldPinsToNewPins(OldClassPins, NewClassPins, nullptr); DestroyPinList(OldClassPins); UEdGraph* Graph = GetGraph(); Graph->NotifyGraphChanged(); FBlueprintEditorUtils::MarkBlueprintAsModified(GetBlueprint()); } #undef LOCTEXT_NAMESPACE |
こちらはエディタ時のみに使用するクラスになるので、プロジェクトのエディタモジュール側に定義します。
ちなみにエディタモジュールの実装に関してはコチラの記事を参考にして下さい。
また、カスタムノードを実装時には以下のように参照するモジュールを追加してあげる必要もありますのでご注意ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// Fill out your copyright notice in the Description page of Project Settings. using UnrealBuildTool; public class MyProjectEditor : ModuleRules { public MyProjectEditor(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[]{ “Core”, “CoreUObject”, “Engine”, “InputCore”, “Slate”, “EditorStyle”, “MyProject” }); PrivateDependencyModuleNames.AddRange(new string[]{ “EditorStyle”, “KismetCompiler”, “UnrealEd”, “GraphEditor”, “SlateCore”, “Kismet”, “KismetWidgets”, “PropertyEditor”, “BlueprintGraph” }); } } |
自分が個人で作成・公開している WebApi プラグイン というものがあります。
プラグインの内容は割愛しますが、こちらの実装でも今回の仕組みが活用されています。
WebApi クラスをインスタンス化する機能を提供する「Create WebApi」というノードがあります。
こちらは ConstructObjectFromClass とほぼ同機能のノードとなりますが、それに加えて以下の拡張が入っています。
また、WebApiRequestBodyBase クラスをインスタンス化し、更にプロパティコピー元のインスタンスを指定が可能な「Create WebApiRequestBody」というノードも実装されています。
こういった感じに入力ピンは自由に増やすことが可能です。