Trending
Opinion: How will Project 2025 impact game developers?
The Heritage Foundation's manifesto for the possible next administration could do great harm to many, including large portions of the game development community.
Topic: Component Visualizers
Source for UE4.23: https://github.com/klauth86/UE4Cookery/tree/main/CPP003
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:
Read more about:
BlogsYou May Also Like