How to draw HUD in editor?

How to draw HUD in editor?
I’m trying to draw some 2D lines in 2D screen space.
I setup my HUD class and my slate widget, and when in PIE mode,
it worked well, but now I want my HUD display in editor, I searched Google
with key word “UE4 SHOW HUD IN EDITOR” but got nothing.
What should I do to enable mu HUD in editor?
Thanks!

Update: after about one week studying source code of UE4, I finally managed
draw HUD in editor. yey!

It’s simple yet ugly.



FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked<FLevelEditorModule>( TEXT("LevelEditor") );
    
    TWeakPtr<SLevelEditor> editor = LevelEditor.GetLevelEditorInstance();
    TSharedPtr<SLevelEditor> PinnedEditor( editor.Pin() );
    
    if( PinnedEditor.IsValid() )
    {
        TSharedPtr<SLevelViewport> slevelviewport = PinnedEditor->GetActiveViewport();
        FChildren* childrenSlot = slevelviewport->GetChildren();
        if(childrenSlot )
        {
            
            
            const TSharedRef<SWidget>& Child =   childrenSlot->GetChildAt(0)->GetChildren()->GetChildAt(0);
            TSharedRef<SOverlay> ViewportWidget = StaticCastSharedRef<SOverlay>(Child);
            
            ViewportWidget->AddSlot()
                                          SNew(SOverlay)
                                          +SOverlay::Slot()
                                          .VAlign(VAlign_Top)
                                          .HAlign(HAlign_Center)
                                          
                                           SNew(STextBlock)
                                           .ShadowColorAndOpacity(FLinearColor::Black)
                                           .ColorAndOpacity(FLinearColor::Red)
                                           .ShadowOffset(FIntPoint(-1, 1))
                                           .Font(FSlateFontInfo("Veranda", 16))
                                           .Text(LOCTEXT("HelloSlate", "Hello, Hooked Level Editor Slate :)))) "))
                                           ]
                                          ];
            
        }
        
        
        
    }

8ca1db7e4630d5c63a66a2aa67c903f023cc6e78.jpeg

Another Update: Since my question is drawing 2D lines, so I’ll just continue here to show how to draw 2D lines.
First of all, create your own SCompoundWidget, in my case, it’s SMyCompoundWidget.
Then override OnPaint method:



//.h:
virtual int32 OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const override;

//.cpp:
int32 SMyCompoundWidget::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
     return   SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
}


Take a look at DrawElement.h, there are plenty of functions that you can use for doodling :slight_smile:
I use FSlateDrawElement::MakeLines for drawing lines.
To draw a rectangle:



    int32 size = 80;
    const FVector2D P0( 0, 0 );
    const FVector2D P1( 0,size);
    const FVector2D P2( size,size);
    const FVector2D P3( size,0);
    
    // We want to draw this:
    //(0,0)----->
    //  |
    //  | P0---------P1
    //  V |          |
    //    |          |
    //    |          |
    //    |          |
    //    P3---------P2

    
    // Inner Box
    {
        TArray<FVector2D> InnerBox;
        
        InnerBox.Add( P0 );
        InnerBox.Add( P1 );
        InnerBox.Add( P2 );
        InnerBox.Add( P3 );
        InnerBox.Add( P0 );
        
        FSlateDrawElement::MakeLines(
                                     OutDrawElements,
                                     LayerId,
                                     AllottedGeometry.ToPaintGeometry(),
                                     InnerBox,
                                     MyClippingRect
                                     );
    }


Finally, my SCompoundWidget::OnPaint looked like this:



int32 SMyCompoundWidget::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
    
    int32 size = 80;
    const FVector2D P0( 0, 0 );
    const FVector2D P1( 0,size);
    const FVector2D P2( size,size);
    const FVector2D P3( size,0);
    
    // We want to draw this:
    //(0,0)----->
    //  |
    //  | P0---------P1
    //  V |          |
    //    |          |
    //    |          |
    //    |          |
    //    P3---------P2

    
    // Inner Box
    {
        TArray<FVector2D> InnerBox;
        
        InnerBox.Add( P0 );
        InnerBox.Add( P1 );
        InnerBox.Add( P2 );
        InnerBox.Add( P3 );
        InnerBox.Add( P0 );
        
        FSlateDrawElement::MakeLines(
                                     OutDrawElements,
                                     LayerId,
                                     AllottedGeometry.ToPaintGeometry(),
                                     InnerBox,
                                     MyClippingRect
                                     );
    }
    
     
    return   SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
}


Now we’re done with SMyCompoundWidget.

Finally:



//change 
ViewportWidget->AddSlot()
                                          SNew(SOverlay)
                                          +SOverlay::Slot()
                                          .VAlign(VAlign_Top)
                                          .HAlign(HAlign_Center)
                                          
                                           SNew(STextBlock)
                                           .ShadowColorAndOpacity(FLinearColor::Black)
                                           .ColorAndOpacity(FLinearColor::Red)
                                           .ShadowOffset(FIntPoint(-1, 1))
                                           .Font(FSlateFontInfo("Veranda", 16))
                                           .Text(LOCTEXT("HelloSlate", "Hello, Hooked Level Editor Slate :)))) "))
                                           ]
                                          ];

//to
ViewportWidget->AddSlot()[SNew(SMyCompoundWidget)];


Can you see that tiny little cute white rectangle on top left corner?
This little baby costs me almost one week. But, you know what, live & learn, and thank you Epic, for bringing such wonderful engine to us!
Thank you again!:slight_smile:

Update:
Code



 TWeakPtr<SLevelEditor> editor = LevelEditor.GetLevelEditorInstance();
    TSharedPtr<SLevelEditor> PinnedEditor( editor.Pin() );
    
    if( PinnedEditor.IsValid() )
    {
        TSharedPtr<SLevelViewport> slevelviewport = PinnedEditor->GetActiveViewport();
     ...


Sometimes won’t work on Windows, but perfectly on OS X.
When I say “sometimes”, which means I tried compile code without changing anything, sometimes it compiles, sometimes fails, but most of the time, it fails. This is really odd.:confused:
I don’t know why, it just keep failing linking even if I added “LevelEditor” dependency to Build.cs file, so if above code didn’t work, then try below:



TSharedPtr<SLevelViewport> slevelviewport = StaticCastSharedPtr<SLevelViewport>(LevelEditor.GetFirstActiveViewport());


This works.

2 Likes

Come on guys, I really need this!
I’m stuck here about one week!
Thank you!

I am no expert but you might have to extend the editors built in editor viewport classes. Most of this is done in slate, that is how all of the buttons and options show up in the viewports.
I am not 100% sure this is how you would do it.

I just looked in Engine/Source/Editor/LevelEditor

Some interesting files in there:
SLevelViewportToolbar.h and .cpp

Extending LevelEditor is a good idea, I’ll try it.
And also I managed draw MyHud (Which extends AHUD) in editor,
since AHUD is an AAtcor, so I just spawn it refer to APlayerController::ClientSetHUD_Implementation method in PlayerController.cpp,
then assigned the debug canvas using UCanvas CanvasObject = FindObject<UCanvas>(GetTransientPackage(),TEXT(“DebugCanvasObject”))*.
Overriding ShouldTickIfViewportsOnly could make MyHud tick in editor, then finally use Draw2DLine drew some lines.
Above is so far I got.
But the line I drew just keep flashing, then I tried call Super::PostRender() in Tick and move drawing method to DrawHUD, still flashing and crashes editor.
I think it might be something with threading, since as my understanding, drawing methods are processed in different thread.
I found another interesting stuff, DrawDebugHelpers.cpp. I looked into it but failed to understand it. I don’t know where did
this actually draw these stuff, the only thing it did is add stuff to array and remove stuff when expired.
I’m still looking into it, any help would be appreciated.
:arrow_down::arrow_down::arrow_down:Now I’m just like​:arrow_down::arrow_down::arrow_down:
Programmer-cat-210x157.jpg

Finally solved!
I’ll post solution to top of the thread.

Just want to say Thank You Epic!
And admins please add [SOLVED] to thread title, thanks.

thanks for sharing the code. It was very helpfull :slight_smile:
Just to add something from me to this topic. I gues it can be done (text in Editor HUD) in prittier way. Corect me if I`m wrong about this.

Code:



FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));
			TSharedPtr<ILevelViewport> LViewport = LevelEditor.GetFirstActiveViewport();
			TSharedRef<SWidget> NewWidget = SNew(SOverlay)
				+ SOverlay::Slot()
				.VAlign(VAlign_Top)
				.HAlign(HAlign_Center)
				
					SNew(STextBlock)
					.ColorAndOpacity(FLinearColor::Red)
				.Font(FSlateFontInfo("Veranda", 16))
				.Text(LOCTEXT("Hello", "esrtgnsrzengsxretgd"))
				];
			LViewport->AddOverlayWidget(NewWidget);

This is because the engine class SLevelEditor does not have the “LEVELEDITOR_API” macro, thus anything outside the LevelEditor Module cannot call functions of that class.