Keywords: UE4, Editor Extention and Programming

Documents

Documents - Fragments

UE4 - Easy example of How to create menus in the editor
http://www.danielmayor.com/ue4-simple-menus

UE4 Level Editor

Creating an Editor Module
https://wiki.unrealengine.com/Creating_an_Editor_Module

Customizing the editor’s toolbar buttons menu via custom plugin
https://answers.unrealengine.com/questions/25609/customizing-the-editors-toolbar-buttons-menu-via-c.html

UE4 Editor Toolbar Extention
https://blog.csdn.net/hui211314ddhui/article/details/79375548

batch operations with editor utility blueprints in UE4
https://www.youtube.com/watch?v=5Ty_rQp34PQ

How to set display index / order of property in Editor

e.g.

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = "1"))
    int32 Value1 = -1;

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisplayPriority = "2"))
    int32 Value1 = -1;

Reference:
Metadata Specifiers
https://docs.unrealengine.com/en-US/Programming/UnrealArchitecture/Reference/Metadata/index.html

Editor Event

Properties changed event

#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif

Actor moved event

#if WITH_EDITOR
/** Called after an actor has been moved in the editor */
virtual void PostEditMove(bool bFinished);
#endif
How to customize Transform of Actor in Editor

Header: Add VisibleAnywhere on properties.

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = CameraEvent)
    UBoxComponent* BoxComp;
    
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = CameraEvent)
    UArrowComponent* DirectionComp;

Constructor:

BoxComp = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComp"));
BoxComp->SetupAttachment(GetDefaultAttachComponent());

DirectionComp = CreateDefaultSubobject<UArrowComponent>(TEXT("TriggerDirectionComp"));
DirectionComp->SetupAttachment(BoxComp);

In Bluerpint Editor:

In Level Editor:

In Bluerpint Editor, you can’t modify Location and Rotation of RootComponent, but you can modify them in Level Editor.

How to spawn actor in Editor (Level Editor)

example:

AActor* newActor = GEditor->AddActor(
    GEditor->GetLevelViewportClients()[0]->GetWorld()->GetCurrentLevel(), AMyActorClass, MyTransform);
// modify properties on newActor
newActor->RerunConstructionScripts();

https://answers.unrealengine.com/questions/518445/actors-spawned-in-editor-do-not-run-their-construc.html

How to limit the lenght of array in Actor Blueprint editor.

Add EditFixedSize in the property:

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Config", EditFixedSize)
TArray<int32> IdList;

Then initilize array in constructor.

AMyActor::AMyActor()
{
    IdList.Init(0, 5);
}
How to creat editor widget using C++

UE4 – C++ Editor Utility Widgets (4.22)
https://isaratech.com/ue4-c-editor-utility-widgets-4-22/

Issue:
Blueprint EditorUtilityWidget Can’t inherit custom cpp class.
Solution:
Modify MyPlugin.uplugin.
Origin:

"Modules": [
    {
        "Name": "MyExtender",
        "Type": "Runtime",
        "LoadingPhase": "Default"
    }
]

New:

"Modules": [
    {
        "Name": "MyExtender",
        "Type": "Editor",
        "LoadingPhase": "PostEngineInit"
    }
]

Then delete all intermediate files and re-generate project files.

How to open the Windows file explorer (open directory dialog) to select the file

Example:

void UMyUserWidget::OnTestBtnClicked()
{
    TSharedPtr<SWindow> ParentWindow = FSlateApplication::Get().FindWidgetWindow(TakeWidget());
    void* ParentWindowHandle = (ParentWindow.IsValid() && ParentWindow->GetNativeWindow().IsValid()) ? ParentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;
    IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
    if (DesktopPlatform)
    {
        const FString ContentDireAbsolute = FPaths::ConvertRelativePathToFull(FPaths::ProjectConfigDir());
        FString OutDire;
        DesktopPlatform->OpenDirectoryDialog(ParentWindowHandle, TEXT("Choose Folder"), ContentDireAbsolute, OutDire);
        GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Cyan, OutDire);
    }
}

Select files dialog:

/*
 * Opens a file dialog for the specified data. Leave FileTypes empty to be able to select any files.
 * Filetypes must be in the format of: <File type Description>|*.<actual extension>
 * You can combine multiple extensions by placing ";" between them
 * For example: Text Files|*.txt|Excel files|*.csv|Image Files|*.png;*.jpg;*.bmp will display 3 lines for 3 different type of files.
 */

TArray<FString> OutFileNames;

//Opening the file picker!
uint32 SelectionFlag = 0; //A value of 0 represents single file selection while a value of 1 represents multiple file selection
DesktopPlatform->OpenFileDialog(ParentWindowHandle, DialogTitle, DefaultPath, FString(""), FileTypes, SelectionFlag, OutFileNames);

Reference
https://www.orfeasel.com/creating-a-file-picker/

How to add customized tips bar in editor

Quoted from Engine\Source\Runtime\Engine\Private\ShaderCompiler\ShaderCompiler.cpp

FText StatusUpdate;
    if ( MaterialName != NULL )
    {
        FFormatNamedArguments Args;
        Args.Add( TEXT("MaterialName"), FText::FromString( MaterialName ) );
        StatusUpdate = FText::Format( NSLOCTEXT("ShaderCompilingManager", "CompilingShadersForMaterialStatus", "Compiling shaders: {MaterialName}..."), Args );
    }
    else
    {
        StatusUpdate = NSLOCTEXT("ShaderCompilingManager", "CompilingShadersStatus", "Compiling shaders...");
    }

    FScopedSlowTask SlowTask(1, StatusUpdate, GIsEditor && !IsRunningCommandlet());
    SlowTask.EnterProgressFrame(1);
How to add customized progress bar in editor

Begin:

FText Description = NSLOCTEXT("UnrealEd", "PerformingCSGOperation", "Performing CSG operation");
GWarn->BeginSlowTask( Description, true );

End:

GWarn->EndSlowTask();

References: UHCsgUtils::ComposeBrushCSG in HoudiniEngine

How to load asset data in a batch.

1st way: AssetRegistryModule

static const FName AssetRegistryName(TEXT("AssetRegistry"));
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(AssetRegistryName);

IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();

AssetRegistry.GetAssetsByClass(UMaterial::StaticClass()->GetFName(), AssetDataArray, true);
AssetRegistry.GetAssetsByClass(UMaterialInstance::StaticClass()->GetFName(), AssetDataArray, true);

Example: SMaterialAnalyzer::BuildBasicMaterialTree()

2nd way: ObjectLibrary

const auto ObjectLibrary = UObjectLibrary::CreateLibrary(UMaterialInterface::StaticClass(), false, true);
ObjectLibrary->LoadAssetDataFromPath(TEXT("/Game/TestAssets"));
TArray<FAssetData> AssetDatas;
ObjectLibrary->GetAssetDataList(AssetDatas);
for(auto& Data : AssetDatas)
{
    UObject* Obj = Data.GetAsset();
    UClass* AssetClass = Data.GetClass();
    if(const UMaterialInterface* Material = Cast<UMaterialInterface>(Obj))
    {
    }
}
How to draw debug line in Animation Editor’s viewport.

Example from FKawaiiPhysicsEditMode::Render():

void FKawaiiPhysicsEditMode::Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI)
{
    const USkeletalMeshComponent* SkelMeshComp = GetAnimPreviewScene().GetPreviewMeshComponent();

#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
    if (SkelMeshComp && SkelMeshComp->GetSkeletalMeshAsset() && SkelMeshComp->GetSkeletalMeshAsset()->GetSkeleton())
#else
    if (SkelMeshComp && SkelMeshComp->SkeletalMesh && SkelMeshComp->SkeletalMesh->GetSkeleton())
#endif
    {
        RenderSphericalLimits(PDI);
        RenderCapsuleLimit(PDI);
        RenderPlanerLimit(PDI);
        PDI->SetHitProxy(nullptr);

        if (IsValidSelectCollision())
        {
            FCollisionLimitBase* Collision = GetSelectCollisionLimitRuntime();
            if (Collision)
            {
                FTransform BoneTransform = FTransform::Identity;
                if (Collision->DrivingBone.BoneIndex >= 0)
                {
                    BoneTransform = RuntimeNode->ForwardedPose.GetComponentSpaceTransform(
                        Collision->DrivingBone.GetCompactPoseIndex(RuntimeNode->ForwardedPose.GetPose().GetBoneContainer()));
                }
                PDI->DrawPoint(BoneTransform.GetLocation(), FLinearColor::White, 10.0f, SDPG_Foreground);
                DrawDashedLine(PDI, Collision->Location, BoneTransform.GetLocation(),
                    FLinearColor::White, 1, SDPG_Foreground);
                DrawCoordinateSystem(PDI, BoneTransform.GetLocation(), Collision->Rotation.Rotator(), 20, SDPG_World + 1);

            }
        }
    }

#if ENGINE_MAJOR_VERSION == 5
    FAnimNodeEditMode::Render(View, Viewport, PDI);
#else
    FKawaiiPhysicsEditModeBase::Render(View, Viewport, PDI);
#endif
}

Testing & Optimization

Testing Automation

Automation System Overview, Overview of the automation system used for unit testing, feature testing, and content stress testing.
https://docs.unrealengine.com/4.27/en-US/TestingAndOptimization/Automation/


静水流深(Still Water Runs Deep) ---拉丁谚语