UE4 Component Visualizers

A brief introduction to the ComponentVisualizer in UE4 and how to get started using them, for those new to UE4 editor modifications.

Parts

  1. My ramblings
  2. Setting up the example project
  3. Creating an editor module
  4. Creating the component visualizer

Finished example project on Github



The bad way

I've wanted to write this post for a while, because I've noticed how few resources there are online on this topic, and because in my last post about the Mini City I use editor ticking actors to render visualizations of connections between various waypoints, and I don't want to be a advocate of bad habits.


Because although the editor ticking "trick" is very easy to get up and running and so is nice for prototyping, it's not a good way to approach the problem of in-editor visualizations. Mostly it's less than ideal because it blurs the line between runtime logic and editor logic, which should - for most projects - stay very clear. Now if only there was a way to do it that satisfies this criteria..


This is where Component Visualizers come in!


The good way

Component Visualizers are a special UE4 editor class especially made for editor-only visualizations, and we will solve the issue of a clear runtime / editor split by putting the visualizers in a separate module; and editor module. Modules are one of the ways of separating functionality in Unreal, but is more granular than a plugin - ie plugins can contain multiple modules.


After loading our module we register our visualizer with the component class we want visualized, and then whenever an actor with said component is selected, a draw function is called by the editor, which we will have overridden to perform whichever visualizations we desire.


Doesn't sound too bad now, does it? Now there is unfortunately going to be a bit of setup. This will be a lot easier to injest for those already familiar with editor modules so feel free to jump a head a bit, but I will go through everything so anyone can follow along. The full working example is also available on my github.


Setting up the example project

The project will be very simple. It'll follow the same idea as the waypoint project from Mini City. I'll just make a component that keeps track of other actors that are connected to it, and then visualize those connections when selecting an actor with said component.


I've created a blank C++ project named ComponentViz. I'm currently sporting 4.25, though all this code should work with the latest couple engine versions. In the editor I created a new C++ class based on the ActorComponent which I've named Connector. The only thing I've added to this class is this uproperty decleration in the header file, leaving everything else as is.


...
public:
  // Array to hold the targets
  UPROPERTY(BlueprintReadWrite, EditInstanceOnly)
  TArray<AActor*> targets;
...



Now I'll just make a blueprint actor with a static mesh on it, so we can see it, and of course I add the Connector component. You should now be able to click this actor in the editor, click the Connector component and add some actors to the targets array. They can be any type of actor. Next up we'll sort the editor module for the visualizer.


No visualizations yet, though I did apply a fashionable yellow material



Adding an editor module

Before we begin, for now during this setup, when I'm talking about adding folders and files, I mean in the system file explorer and not inside Visual Studio. Then when we're set up we can regenerate the solution and it'll mirror our file system setup in the solution.


In your projects Source directory you're going to want to add a new folder, commonly named the same as your main module but with an Editor on the end, so in my case It'll be ComponentVizEditor. This is where we'll host all our editor code. You'll also notice the xxx.Target.cs files found in the Source folder. We'll want to open the one with Editor in the name and add our new module to the string array ExtraModuleNames so that line 12 now reads like this:


ExtraModuleNames.AddRange( new string[] { "ComponentViz", "ComponentVizEditor" } );



This is to make the build system aware of what we're up to. Within our new module folder I'll add two folders, Private and Public for our code, and also a file named ComponentVizEditor.Build.cs where we can declare dependencies. This will look similar to the build.cs for the main module, but I'll include it here in its entirety.


using UnrealBuildTool;

public class ComponentVizEditor : ModuleRules
{
    public ComponentVizEditor(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

		// ComponentViz needed for the objects we're visualizing
        PublicDependencyModuleNames.AddRange(new string[] {  "Core", "Engine", "CoreUObject", "ComponentViz" });
		// Needed for our editor logic
        PrivateDependencyModuleNames.AddRange(new string[] { "UnrealEd" });
    }
}



The next thing we'll do is create two files in each of the two folders we created, putting ComponentVizEditor.h in Public and ComponentVizEditor.cpp in Private. Now the last thing you'll need to do before generating the Visual Studio projects file from the uproject file is to open it up and add our new editor module to the list of modules to load. My uproject file ends up looking like this:


{
	"FileVersion": 3,
	"EngineAssociation": "4.25",
	"Category": "",
	"Description": "",
	"Modules": [
		{
			"Name": "ComponentViz",
			"Type": "Runtime",
			"LoadingPhase": "Default",
			"AdditionalDependencies": [
				"Engine"
			]
		},
		{
			"Name": "ComponentVizEditor",
			"Type": "Editor",
			"LoadingPhase": "PostEngineInit"
		}
	]
}



If you now generate visual studio project files and open the solution you'll not only find the new module under the Source folder, but also our new empty code files. Lets fill those in as well! The header file is going to be very slim, only declaring our module class and some methods we'll override.


ComponentVizEditor.h

#pragma once
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"

class FComponentVizEditorModule : public IModuleInterface
{
public:
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
};

Notice the F prefix used, you'll see this often with editor classes



Now in the cpp file we'll implement these methods, but just with some boilerplate for now. This should be enough to get you up and running.


ComponentVizEditor.cpp

#include "ComponentVizEditor.h"
#include "UnrealEd.h"

IMPLEMENT_GAME_MODULE(FComponentVizEditorModule, ComponentVizEditor);

void FComponentVizEditorModule::StartupModule()
{

}

void FComponentVizEditorModule::ShutdownModule()
{

}



You should now be able to start the editor and it'll load our empty module!


Creating the component visualizer

Now we'll get started creating our visualizer class and verify our module loaded in the same go. Create a new C++ class in the editor choosing the None template, and when naming it change the module to ComponentVizEditor and slam that Public button.



Once created and opened in VS we'll fill out the header file like so:


ConnectorVisualizer.h

#pragma once
#include "ComponentVisualizer.h"

class COMPONENTVIZEDITOR_API FConnectorVisualizer : public FComponentVisualizer
{
private:
	// This is the only method we need to override from FComponentVisualizer
	virtual void DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) override;
};



Now the only method we'll need for what we're doing is DrawVisualization, which will be called by the editor every frame for every object we have selected that contains our component. What we're going to set up is a line that gets drawn between the actor our component is a part of and all of the targets. Additionally we'll draw a cylinder around the actor to show some other draw functions.


ConnectorVisualizer.cpp

#include "ConnectorVisualizer.h"
#include "Connector.h"

// Contains most of the stuff you need for visualization
#include "SceneManagement.h"

void FConnectorVisualizer::DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI)
{
	// Cast the incoming component to our UConnector class
	const UConnector* connector = Cast<const UConnector>(Component);

	// Draw a cylinder around the object because why not
	DrawWireCylinder(PDI,
		connector->GetOwner()->GetActorLocation(),
		FVector(1, 0, 0),		// The cylinder matrix, here it's just static
		FVector(0, 1, 0),
		FVector(0, 0, 1),
		FLinearColor(0, 0, 1),	// Blue color
		75, 75, 12,				// Various measurements for the cylinder
		SDPG_Foreground,		// Will render on top of everything
		2.f						// Line width
	);

	// Loop over all the actors in the array
	for (int i = 0; i < connector->targets.Num(); i++)
	{
		// Grab it from the array and verify that it's valid, as indicies can be empty
		AActor* target = connector->targets[i];
		if (target)
		{
			// Draw a line between the actor of the component and the target from the array
			PDI->DrawLine(
				connector->GetOwner()->GetActorLocation(),
				target->GetActorLocation(),
				FLinearColor(1.f, 0.f, 0.f),	// Red color
				SDPG_World,						// Will not render over other things
				2.0f);							// Line width
		}
	}
}


Now this is all well and good but if you tried building the editor you'll notice nothing is showing up for us. The reason for that is that there isn't anything binding our visualizer to our component at the moment. To do that we're going to have to open up the ComponentVizEditor.cpp and register our visualizer in the StartupModule method that we left empty earlier. First we'll want to include the headers to our Connector class and our ConnectorVisualizer class. Then we'll make a new instance of our visualizer, making use of UE4s TSharedPtr and pass that to the function for registering visualizers. We'll also round this off properly and unregister it at module shutdown. The final file should look something like this:


ComponentVizEditor.cpp

#include "ComponentVizEditor.h"
#include "UnrealEd.h"
#include "ConnectorVisualizer.h"
#include "Connector.h"

// Actually registers the module
IMPLEMENT_GAME_MODULE(FComponentVizEditorModule, ComponentVizEditor);

void FComponentVizEditorModule::StartupModule()
{
	if (GUnrealEd)
	{
		// Make a new instance of the visualizer
		TSharedPtr<FComponentVisualizer> visualizer = MakeShareable(new FConnectorVisualizer());

		// Register it to our specific component class
		GUnrealEd->RegisterComponentVisualizer(UConnector::StaticClass()->GetFName(), visualizer);
		visualizer->OnRegister();
	}
}

void FComponentVizEditorModule::ShutdownModule()
{
	if (GUnrealEd)
	{
		// Unregister when the module shuts down
		GUnrealEd->UnregisterComponentVisualizer(UConnector::StaticClass()->GetFName());
	}
}


And that's it! Hopefully that should get you up and running.



Again, the finished example scene is available on my Github here, should you have any issues.


Till next time!