Sponsored By

UE4Cookery CPP003: Component Visualizers

Topic: Component Visualizers

Source for UE4.23: https://github.com/klauth86/UE4Cookery/tree/main/CPP003

Artur Kh, Blogger

March 8, 2021

4 Min Read

While designing levels or locations it is often advantageous to have some graphical infos and gizmos about actors mutual arrangement. If we think what can be expressed in this graphical infos there can be lots of possible cases: moving platform path points, dot trap fire direction and many many more. Of course, Engine have much stuff like UArrowComponent, MakeEditWidget meta specifier,  simple draw methods from DrawDebugHelpers.h and some other. But when you need more control and more separation between Game things and Editor things you can use FComponentVisualizer. Let's take a look how we can use it!

After creating some new project from template, first step to do is to add Editor Module (you can check commit history to see it all in details). After it is created lets create our own component in Game Module, like this

 

MyActorComponent.h


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Components/ActorComponent.h"
#include "MyActorComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class CPP003_API UMyActorComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UMyActorComponent();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

		
};

 

MyActorComponent.cpp


// Fill out your copyright notice in the Description page of Project Settings.


#include "MyActorComponent.h"

// Sets default values for this component's properties
UMyActorComponent::UMyActorComponent()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}


// Called when the game starts
void UMyActorComponent::BeginPlay()
{
	Super::BeginPlay();

	// ...
	
}


// Called every frame
void UMyActorComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...
}

 

To use FComponentVisualizer we need some other modules to be included in our Editor Module, so our Editor.Build.cs will be

 

Editor.Build.cs


using UnrealBuildTool;
using System.IO;

public class CPP003Editor : ModuleRules {
    
	public CPP003Editor(ReadOnlyTargetRules Target) : base(Target) {

		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

		// ... add public include paths required here ...
		PublicIncludePaths.AddRange(new string[] { Path.Combine(ModuleDirectory, "Public") });

		// ... add other private include paths required here ...
		PrivateIncludePaths.AddRange(new string[] { Path.Combine(ModuleDirectory, "Private") });

		// ... add other public dependencies that you statically link with here ...
		PublicDependencyModuleNames.AddRange(new string[] { "Core" });

		// ... add private dependencies that you statically link with here ...	
		PrivateDependencyModuleNames.AddRange(new string[] { "UnrealEd", "CPP003", "Engine" });

		// ... add any modules that your module loads dynamically here ...
		DynamicallyLoadedModuleNames.AddRange(new string[] { });
	}
}

 

So, everything is prepared to create custom visualizer for our component. Lets create it in Private folder of Editor Module

 

ComponentVisualizer_MyActorComp.h


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "ComponentVisualizer.h"

class FComponentVisualizer_MyActorComp : public FComponentVisualizer {

public:

	virtual void DrawVisualization(const UActorComponent* Component, const FSceneView* View,
		FPrimitiveDrawInterface* PDI) override;
};

 

ComponentVisualizer_MyActorComp.cpp


// Fill out your copyright notice in the Description page of Project Settings.


#include "ComponentVisualizer_MyActorComp.h"
#include "EngineUtils.h"

void FComponentVisualizer_MyActorComp::DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) {
	if (Component) {
		if (auto owner = Component->GetOwner()) {
			DrawWireSphere(PDI, owner->GetActorLocation(), FColor::Blue, 128, 16, SDPG_World, 2);
		}
	}
}

 

So, this visualizer simply draw wire sphere at Actor location. The last point - just to register it when Editor module starts up and unregister when Editor module shuts down correspondingly

 

CPP003EditorModule.h


#pragma once

#include "Modules/ModuleManager.h"

DECLARE_LOG_CATEGORY_EXTERN(LogCPP003Editor, Log, All);

class FComponentVisualizer;

class FCPP003EditorModule : public IModuleInterface {
public:

	virtual void StartupModule() override;
	virtual void ShutdownModule() override;

protected:

	void RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr<FComponentVisualizer> Visualizer);

protected:

	TArray<FName> RegisteredComponentClassNames;
};

 

CPP003EditorModule.cpp


#include "CPP003EditorModule.h"
#include "MyActorComponent.h"
#include "ComponentVisualizers/ComponentVisualizer_MyActorComp.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"

DEFINE_LOG_CATEGORY(LogCPP003Editor);

#define LOCTEXT_NAMESPACE "FCPP003EditorModule"

void FCPP003EditorModule::StartupModule() {
	RegisterComponentVisualizer(UMyActorComponent::StaticClass()->GetFName(), MakeShareable(new FComponentVisualizer_MyActorComp));
}

void FCPP003EditorModule::ShutdownModule() {
	if (GUnrealEd != NULL) {
		// Iterate over all class names we registered for
		for (FName ClassName : RegisteredComponentClassNames) {
			GUnrealEd->UnregisterComponentVisualizer(ClassName);
		}
	}
}

void FCPP003EditorModule::RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr<FComponentVisualizer> Visualizer) {
	if (GUnrealEd != NULL) {
		GUnrealEd->RegisterComponentVisualizer(ComponentClassName, Visualizer);
	}

	RegisteredComponentClassNames.Add(ComponentClassName);

	if (Visualizer.IsValid()) {
		Visualizer->OnRegister();
	}
}

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FCPP003EditorModule, CPP003Editor);

 

Now create Actor class, that contains our component, add it to some level and select it in World Outliner - and we can see how our Visualizer works:

Visualizer in Editor Viewport

Read more about:

Blogs

About the Author(s)

Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like