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
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.
//Optional UFUNCTION(BlueprintCallable)
void PrintBlueprintCallstack()
{
FString callStack = FFrame::GetScriptCallstack();
UE_LOG(LogTemp, Error, TEXT("%s"), callStack);
}
This code lets you register console commands that can be used in editor and non-shipping builds.
#if !UE_BUILD_SHIPPING
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"));
}
UFUNCTION()
void Command()
{
UE_LOG(LogTemp, Error, TEXT("Wahoo!"));
}
#endif
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.BindUFunction(this, FName(TEXT("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.BindUFunction(this, FName(TEXT("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 assets so you can modify them in Editor Utility Blueprint functions.
SCREENSHOT
When updating to a new Unreal Engine version, this log may appear indicating that changes 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").