When using making C++ plugins, missing module dependencies in the plugin's Build.cs file will cause linker errors. Here are modules I've needed so far when working with certain systems.
"DeveloperSettings" //For developer settings used to add custom options to Project Settings
"AudioMixer" //For Quartz system
"MovieScene" //For level sequence systems
"LevelSequence" //For level sequence systems
"UMG" //For using UI based classes
WARNING: The module dependencies below are Editor only. This means if they are in a non-Editor type plugin module (defined in the .uplugin file) the project will fail to package. See Editor Only Module Dependencies.
"UMGEditor" //For EditorUtilityWidgetBlueprint.h
"ToolMenus" //For adding or extending toolbar menus in the editor
"Blutility" //For Editor Utility Widgets
"UnrealEd" //For access to FEditorDelegates
"EditorScriptingUtilities" //For access to Editor Utilities to get Blueprint refs in C++
This code shows how to subscribe to FEditorDelegates.
//FEditorDelegates has a lot more events that can be subscribed to than shown here
void Initialize()
{
FEditorDelegates::PrePIEEnded.AddUObject(this, &UKEditorUtilityWidget::OnEndPIE);
}
//Clean up binding on destroy
void Deinitialize()
{
FEditorDelegates::PrePIEEnded.RemoveAll(this);
}
void OnEndPIE(const bool IsSimulating)
{
//Do stuff
}
This code shows how to spawn an Actor using an Editor Utility Widget (EUW) that will only exist during PIE, otherwise spawning actors with an EUW (and any normal widget that is part of the EUW) will add actors that stay in the actual level. This is because SpawnActor() needs to be called from the PIE world but an EUW exists in a different world.
void UEditorUtilityWidgetSubclass::NativeConstruct()
{
//Pass the PIE world if the widget is already open in the editor
FWorldDelegates::OnPreWorldInitialization.AddUObject(this, &UKEditorUtilityWidget::BeginPIE);
FEditorDelegates::PrePIEEnded.AddUObject(this, &UKEditorUtilityWidget::OnEndPIE);
//Get the PIE world if the widget is opened in the editor during PIE
if (GEditor->PlayWorld)
{
BeginPIE(GEditor->PlayWorld, FWorldInitializationValues());
}
Super::NativeConstruct();
}
void UEditorUtilityWidgetSubclass::NativeDestruct()
{
FWorldDelegates::OnPreWorldInitialization.RemoveAll(this);
FEditorDelegates::PrePIEEnded.RemoveAll(this);
Super::NativeDestruct();
}
void UKEditorUtilityWidgetSubclass::BeginPIE(UWorld* world, FWorldInitializationValues worldValues)
{
//Pass the world to child widgets that will call SpawnDebugActor() using Blueprint
OnBeginPIE(world);
}
UFUNCTION(BlueprintImplementableEvent)
void UKEditorUtilityWidgetSubclass::OnBeginPIE(UWorld* world);
//CLEAN UP ALL WORLD AND PIE REFERENCES TO BE SAFE
UFUNCTION(BlueprintImplementableEvent)
void UEditorUtilityWidgetSubclass::OnEndPIE(const bool IsSimulating);
//Provide a Blueprint Function Library function that allows you to specify the world when calling SpawnActor()
UFUNCTION(BlueprintCallable)
static AActor* SpawnDebugActor(UObject* WorldContext, TSubclassOf<AActor> actorClass, FVector location, FRotator rotation)
{
return WorldContext->GetWorld()->SpawnActor(actorClass, location, rotation);
}
This code shows how to add messages to the Message Log as well as opening it automatically when the message is added. Adding an FUObjectToken allows you to add hyperlinks that can open the asset if a Blueprint called the function (hyperlinks don't always work if the WorldContextObject is not linkable).
#include "Misc/UObjectToken.h" //For FUObjectToken
//The WorldContext meta tag auto-assigns the Blueprint caller's WorldContext to the WorldContextObject parameter
UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject"))
void AddMessageLog(UObject* WorldContextObject)
{
TSharedRef<FTokenizedMessage> log = FTokenizedMessage::Create(EMessageSeverity::Warning, FText::FromString("Message"));
if (IsValid(WorldContextObject))
{
//This can be used to add a hyperlink to the Blueprint where this function was called
log->AddToken(FUObjectToken::Create(WorldContext->GetClass(), FText::FromString(TEXT("Additional Message"))));
}
FMessageLog(TEXT("PIE")).AddMessage(log);
FMessageLog(TEXT("PIE")).Open();
}
This code gets the Blueprint Callstack so you can parse and print it from C++ or Blueprints.
UFUNCTION(BlueprintCallable)
void PrintBlueprintCallstack()
{
FString callStack = FFrame::GetScriptCallstack();
UE_LOG(LogTemp, Error, TEXT("%s"), callStack);
}
This code lets you register console commands.
void RegisterConsoleComand()
{
IConsoleManager::Get().RegisterConsoleCommand(TEXT("TextYouType"), TEXT("Help tip."), FConsoleCommandDelegate::CreateUObject(this, &MyClass::Exec_Command), ECVF_Default);
}
void UnregisterConsoleCommand()
{
IConsoleManager::Get().UnregisterConsoleObject(TEXT("TextYouType"));
}
void Command()
{
UE_LOG(LogTemp, Error, TEXT("Wahoo!"));
}
This code lets you register console commands that take arguments. You can use this to pass those arguments to Blueprint and expose console command creation to designers.
void RegisterConsoleComand()
{
IConsoleManager::Get().RegisterConsoleCommand(TEXT("TextYouType"), TEXT("Help tip."), FConsoleCommandWithWorldAndArgsDelegate::CreateUObject(this, &MyClass::Exec_Command), ECVF_Default);
}
void UnregisterConsoleCommand()
{
IConsoleManager::Get().UnregisterConsoleObject(TEXT("TextYouType"));
}
void Command(const TArray<FString>& parameters, UWorld* world)
{
//Optional exposing console command creation to Blueprints
BlueprintConsoleCommand(parameters)
}
UFUNCTION(BlueprintImplementableEvent)
void BlueprintConsoleCommand(const TArray<FString>& parameters, UWorld* world);
void RegisterConsoleComand()
{
IConsoleManager::Get().RegisterConsoleCommand(TEXT("TextYouType"), TEXT("Help tip."), FConsoleCommandWithWorldAndArgsDelegate::CreateUObject(this, &MyClass::Exec_Command), ECVF_Default);
}
void UnregisterConsoleCommand()
{
IConsoleManager::Get().UnregisterConsoleObject(TEXT("TextYouType"));
}
void Command(const TArray<FString>& parameters, UWorld* world)
{
//Optional exposing console command creation to Blueprints
BlueprintConsoleCommand(parameters)
}
UFUNCTION(BlueprintImplementableEvent)
void BlueprintConsoleCommand(const TArray<FString>& parameters, UWorld* world);
This code provides a custom equals node in Blueprint for you class. The only options provided in the equals dropdown will be those that match your class. Useful for comparing UDataAsset pointers, otherwise by default you must select from all asset references.
//This function can be used to create an equals Blueprint node that only allows you to select assets that match your class.
UFUNCTION(BlueprintPure, meta = (DisplayName = "Equal (MyClass)", CompactNodeTitle = "==", Keywords = "== equal"))
static bool EqualMyClass(MyClass* A, MyClass* B)
{
if (A == B)
{
return true;
}
return false;
}
This code allows UObject based Blueprints of your class to call functions that require GetWorld() on the backend such as SpawnActor(). You can also use GetWorld() in your C++.
//Make sure this UObject is created using NewObject<MyClass>(this) where "this" will be the Outer and have a valid world such as an Actor or subsystem.
virtual UWorld* GetWorld() const override
{
if (IsTemplate() || !GetOuter())
{
return nullptr;
}
return GetOuter()->GetWorld();
}
Some older enums in the Unreal Engine source code do not use the current convention of declaring with "enum class". In order to expose UPROPERTYs of these enums for use in Blueprints use TEnumAsByte<T>.
UENUM(BlueprintType)
enum ELegacyEnum : uint8
{
SomeEngineEnumsAreLikeThis
}
UPROPERTY(EditAnywhere)
TEnumAsByte<ELegacyEnum> needToBeStoredLikeThis;
This code shows how to cancel pending command events on a running quartz clock, create a new clock or modify an existing clock, and receive quantized events from the clock.
"AudioMixer" //Module dependency for Quartz system in plugins
#include "Quartz/AudioMixerClockHandle.h"
void CreateNewQuartzClock(UQuartzClockHandle* clockHandle, int timeSignatureNumerator, float bpm)
{
//Get the Quartz subsystem
UQuartzSubsystem* quartz = GetWorld()->GetSubsystem<UQuartzSubsystem>();
if (!IsValid(quartz))
{
return;
}
//Checking if a clock has already been created that we want to reuse
if (IsValid(clockHandle))
{
//Stopping the clock allows arg 2 to clear any pending QuartzCommandEventBPs
clockHandle->StopClock(this, true, clockHandle);
}
FQuartzTimeSignature timeSignature;
timeSignature.BeatType = EQuartzTimeSignatureQuantization::QuarterNote;
timeSignature.NumBeats = timeSignatureNumerator;
FQuartzClockSettings settings;
settings.TimeSignature = timeSignature;
//If the FName in arg 2 matches an existing clock, CreateNewClock will return that clock instead of creating a new one.
//Arg 4 indicates you want to modify an existing clock's setting
clockHandle = quartz->CreateNewClock(this, FName(TEXT("MusicClock")), settings, true);
clockHandle->SetBeatsPerMinute(this, FQuartzQuantizationBoundary(), FOnQuartzCommandEventBP(), clockHandle, bpm);
//Subscribe to receive synchronized quantization events from this clock
FOnQuartzMetronomeEventBP command;
command.BindDynamic(this, &ThisClass::ReceiveClockCallback));
clockHandle->SubscribeToQuantizationEvent(this, EQuartzCommandQuantization::Beat, command, clockHandle);
clockHandle->StartClock(this, clockHandle);
}
UFUNCTION()
void ReceiveClockCallback(FName ClockName, EQuartzCommandQuantization QuantizationType, int32 NumBars, int32 Beat, float BeatFraction)
{
GEngine->AddOnScreenDebugMessage(0, 10, FColor::Green, FString::FromInt(Beat) + " : " + FString::FromInt(NumBars));
}
This code shows how to synchronizing the triggering of an playing an audio component, resetting the quartz clock, and receiving an event all at the same time. These are integral to horizontal music transition systems.
"AudioMixer" //Module dependency for Quartz system in plugins
#include "Quartz/AudioMixerClockHandle.h"
void SynchronizedEventExample(UAudioComponent* audioComponent, UQuartzClockHandle* clockHandle, float relativeSynchTime, float fadeInTime, EAudioFaderCurve fadeInCurve)
{
//FQuartzQuantizationBoundary defines the timing when your event will be fired. These settings fire based on seconds rather than on a tempo quantized value because we calculate the exact timing separately for flexibility.
FQuartzQuantizationBoundary boundary;
boundary.bFireOnClockStart = false;
boundary.Quantization = EQuartzCommandQuantization::Tick;
boundary.Multiplier = relativeSynchTime / clockHandle->GetSecondsPerTick(this);
boundary.CountingReferencePoint = EQuarztQuantizationReference::CurrentTimeRelative;
//FOnQuartzCommandEventBP will call a UFUNCTION for ALL Quartz commands. This means the receiver must evaluate which commands it wants to listen to.
FOnQuartzCommandEventBP command;
command.BindDynamic(this, &ThisClass::MyFunction));
//Reset clock if modifications not done elsewhere for the next piece of music. For how to modify, see CreateNewQuartzClock
clockHandle->ResetTransportQuantized(this, boundary, command, clockHandle);
//Schedule the audio component play timing.
audioComponent->PlayQuantized(this, clockHandle, boundary, command, 0, fadeInTime, 1, fadeInCurve);
//Use below when you need to cancel an audio component's scheduled play
audioComponent->Stop();
}
UFUNCTION()
void MyFunction(EQuartzCommandDelegateSubType EventType)
{
if (EventType != EQuartzCommandDelegateSubType::CommandOnStarted)
{
return;
}
}
This code allows you to get the CDO of your selected Blueprints so you can modify them in Editor Utility Blueprint functions.
If you are modifying an asset, you only need the code below.
This code searches the project for assets/Blueprints so you can store a string path to use later when you want to get the asset/Blueprint to modify or spawn using a widget.
This is how to grab the asset/Blueprint using the string path.
When updating to a new Unreal Engine version, this log may appear indicating that changes need to be manually made otherwise the project will not open.
To fix this, open the Visual Studio project either with the button or using the .sln file. Locate the ProjectName.Target.cs and ProjectNameEditor.Target.cs files in the Solution Explorer. In both files, change the "DefaultBuildSettings" and "IncludeOrderVersion" to the values specified in the log (boxed in red in the above image). Save and close Visual Studio. You should be able to open the project now.
NOTE: If you want to run from Visual Studio you'll need to delete the .sln, right click the .uproject file, and "Generate "Visual Studio project files" again (you may need to find it under "Show more options").
When working in a plugin, you may run into an issue where an Editor Utility Widget will not show or allow you to use a function (either it doesn't show up in the context menu or if you copy paste from somewhere else you will be unable to compile the Blueprint because it is a "runtime" Blueprint). An example is the DetailsView SetObject() function or the AssetEditorSubsystem's OpenEditorForAssets() function.
To fix this, you'll need to modify the .uplugin file in Visual Studio and change the module type from "Runtime" to "Editor". Then, delete the Binaries and Intermediate folders in the plugin folder and recompile the project.
For unknown reasons, World Subsystems call Initialize() before Game Instance Subsystems are initialized leading to null errors. FWorldDelegates also unexpectedly get run before Game Instance Subsystems are ready too, specifically OnPostWorldInitialization. To access a Game Instance Subsystem safely from a World Subsystem before BeginPlay() is called, override OnWorldBeginPlay() instead.
Console commands that enable showing visual data for audio at runtime.
au. //Shows a large number of audio related console commands
au.3dVisualize //Console commands specifically for enabling visual data. NOTE: To see attenuations the sound asset (SoundWave, MetaSoundSource, SoundCue) must have "Enable Attenuation Debug" set to true.