Skip to content

Draw Lines in Widget C++ [UE5]

Quick tutorial for beginners how to draw lines in user widget using native paint function in C++.

Let’s start with creating NativePaint:

public:

virtual int32 NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
int32 UMyUserWidget::NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
	FPaintContext Context = FPaintContext(AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);

        // this seems to work too
        // FPaintContext Context(AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);

        //... add loop here
        // and
        // UWidgetBlueprintLibrary::DrawLine(Context, PointA, PointB, Tint, true, Thickness);

	Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);

	return LayerId;
}

This is how it looks like in UserWidget.cpp:

Click for bigger.

Extras

C++ loops are so much faster then Blueprint loops, they can run between milliseconds & picoseconds if they are done right, so it’s worth doing entire widget in C++.

To go even further on optimization create a structure array containing Point A & Point B’s and fill the array in async task.

Structure:

USTRUCT(BlueprintType)
struct FExampleLineDraw{
	GENERATED_BODY()
public:

	FExampleLineDraw()
		: PointA(FVector2D()), PointB(FVector2D()
	{}

	UPROPERTY(BlueprintReadWrite)
	FVector2D PointA

	UPROPERTY(BlueprintReadWrite)
	FVector2D PointB
};

And then:

public:

UPROPERTY(BlueprintReadOnly)
TArray<FExampleLineDraw> MyArray;

And then the async and all other stuff:

Boilerplate for AsyncTask.

AsyncTask(ENamedThreads::AnyHiPriThreadNormalTask, []()
	{
		//...
		
		// if you use [this] you don't need this:
		AsyncTask(ENamedThreads::GameThread, []()
			{
				//...
			});
	});
void UMyPlayerWidget::AsyncWorker()
{
	UWorld* world = GetWorld();

	if (!::IsValid(world)) return;

	AsyncAvailible = false;
	ScannerTimer = 0.0f;

	TArray<AActor*> FoundActors;

	// #include "Kismet/GameplayStatics.h"
	// this only works in gamethread
	UGameplayStatics::GetAllActorsOfClass(world, AMyActor::StaticClass(), FoundActors);
			
	AsyncTask(ENamedThreads::AnyHiPriThreadNormalTask, [this, FoundActors]()
		{
			TArray<FExampleLineDraw> tmparray;
			
            APlayerController* PlayerController = this->GetOwningPlayer();


			FExampleLineDraw tmpstruct;

			for(const AActor* Item : FoundActors)
			{
				// do stuff here
				
                   // #include "Blueprint/WidgetLayoutLibrary.h"
			       UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition(PlayerController, Item->GetActorLocation(), tmpstruct.PointA, true);

			       UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition(PlayerController, Item->Mesh->GetCenterOfMass(), tmpstruct.PointB, true);

				tmparray.Add(tmpstruct)
			}

			AsyncTask(ENamedThreads::GameThread, [this, tmparray]()
				{
					this->MyArray = tmparray;
					this->AsyncAvailible = true;
				});
		});
}


void UMyPlayerWidget::Scan()
{
	if (!AsyncAvailible) return;

	AsyncWorker();
}

// .h file: virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
void UMyPlayerWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
	Super::NativeTick(MyGeometry, InDeltaTime);
	Scan();
	if(AsyncAvailible) ScannerTimer += InDeltaTime; // measure process time
}

And then go to native paint:

for(const FExampleLineDraw Item : MyArray)
{
UWidgetBlueprintLibrary::DrawLine(Context, Item.PointA, Item.PointB, FLinearColor::White, true, 0.5f);
}

More about multi-threading:

Multithreading & Unreal — Guneet Sasan

Tasks Systems in Unreal Engine | Unreal Engine 5.4 Documentation | Epic Developer Community | Epic Developer Community

Subscribe
Notify of
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Level Paradox
0
Would love your thoughts, please comment.x
()
x