2024-09-24 18:58:13 -04:00
|
|
|
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "UtilityLibrary.h"
|
2024-10-15 16:17:44 -04:00
|
|
|
#include "GameFramework/PlayerController.h"
|
|
|
|
|
#include "EnhancedInputSubsystems.h"
|
2025-01-07 18:46:40 -05:00
|
|
|
#include "Kismet/KismetSystemLibrary.h"
|
|
|
|
|
#include "Kismet/GameplayStatics.h"
|
2025-05-12 18:23:37 -04:00
|
|
|
#include "Blueprint/UserWidget.h"
|
|
|
|
|
#include "Components/GridPanel.h"
|
2025-05-23 15:33:18 -04:00
|
|
|
#include "InputMappingContext.h"
|
2025-05-27 16:30:49 -04:00
|
|
|
#include "EnhancedInputComponent.h"
|
2025-10-10 17:33:39 -04:00
|
|
|
#include "Animation/AnimSequenceBase.h"
|
2025-01-07 18:46:40 -05:00
|
|
|
|
2024-09-24 18:58:13 -04:00
|
|
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "Luprex Utility"
|
|
|
|
|
|
|
|
|
|
|
2024-09-24 22:13:56 -04:00
|
|
|
void UlxUtilityLibrary::CallFunctionByName(UObject *object, const FString &namepart1, const FString &namepart2, const FString &fallback, bool bFailIfNotFound) {
|
2024-09-24 18:58:13 -04:00
|
|
|
FString fullname = namepart1 + namepart2;
|
|
|
|
|
if (!IsValid(object)) {
|
2024-11-22 23:01:05 -05:00
|
|
|
UE_LOG(LogBlueprint, Error, TEXT("In CallFunctionByName, object passed in is not valid."));
|
2024-09-24 18:58:13 -04:00
|
|
|
return;
|
|
|
|
|
}
|
2024-09-30 13:59:17 -04:00
|
|
|
UFunction* function = object->FindFunction(FName(*fullname));
|
2024-09-24 18:58:13 -04:00
|
|
|
if (function == nullptr) {
|
|
|
|
|
function = object->FindFunction(FName(*fallback));
|
|
|
|
|
if (function == nullptr) {
|
2024-09-24 22:13:56 -04:00
|
|
|
if (!bFailIfNotFound) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-11-22 23:01:05 -05:00
|
|
|
UE_LOG(LogBlueprint, Error, TEXT("In CallFunctionByName, cannot find the named function or the fallback function"));
|
2024-09-24 18:58:13 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (function->ParmsSize != 0) {
|
2024-11-22 23:01:05 -05:00
|
|
|
UE_LOG(LogBlueprint, Error, TEXT("CallFunctionByName can only call functions that have no parameters and no return values"));
|
2024-09-24 18:58:13 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
object->ProcessEvent(function, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FBox UlxUtilityLibrary::GetActorBounds(const AActor *target, bool bOnlyCollidingComponents, bool bIncludeFromChildActors)
|
|
|
|
|
{
|
|
|
|
|
FVector ActorOrigin;
|
|
|
|
|
FVector BoxExtent;
|
|
|
|
|
|
|
|
|
|
// First argument is bOnlyCollidingComponents - if you want to get the bounds for components that don't have collision enabled then set to false
|
|
|
|
|
// Last argument is bIncludeFromChildActors. Usually this won't do anything but if we've child-ed an actor - like a gun child-ed to a character
|
|
|
|
|
// then we wouldn't want the gun to be part of the bounds so set to false
|
|
|
|
|
target->GetActorBounds(bOnlyCollidingComponents, ActorOrigin, BoxExtent, bIncludeFromChildActors);
|
|
|
|
|
|
|
|
|
|
return FBox::BuildAABB(ActorOrigin, BoxExtent);
|
|
|
|
|
}
|
2024-10-15 16:17:44 -04:00
|
|
|
|
|
|
|
|
void UlxUtilityLibrary::AddMovementInputRightward(APawn *target, double ScaleValue, bool Force) {
|
|
|
|
|
FRotator rotator = target->GetControlRotation();
|
|
|
|
|
rotator.Pitch = 0.0;
|
|
|
|
|
rotator.Roll = 0.0;
|
|
|
|
|
rotator.Yaw += 90.0;
|
|
|
|
|
FVector vector = rotator.Vector();
|
|
|
|
|
target->AddMovementInput(vector, ScaleValue, Force);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UlxUtilityLibrary::AddMovementInputForward(APawn *target, double ScaleValue, bool Force) {
|
|
|
|
|
FRotator rotator = target->GetControlRotation();
|
|
|
|
|
rotator.Roll = 0.0;
|
|
|
|
|
rotator.Pitch = 0.0;
|
|
|
|
|
FVector vector = rotator.Vector();
|
|
|
|
|
target->AddMovementInput(vector, ScaleValue, Force);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UEnhancedInputLocalPlayerSubsystem *UlxUtilityLibrary::GetEnhancedInputLocalPlayerSubsystem(AController *Controller) {
|
|
|
|
|
APlayerController *pc = Cast<APlayerController>(Controller);
|
|
|
|
|
if (pc != nullptr) {
|
|
|
|
|
UEnhancedInputLocalPlayerSubsystem* subsys =
|
|
|
|
|
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(pc->GetLocalPlayer());
|
|
|
|
|
if (subsys != nullptr) {
|
|
|
|
|
return subsys;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2025-01-07 18:46:40 -05:00
|
|
|
|
2025-04-02 23:22:16 -04:00
|
|
|
FVector2D UlxUtilityLibrary::PixelToViewportPosition(FVector2D Pixel)
|
|
|
|
|
{
|
|
|
|
|
FVector2D ViewportSize;
|
|
|
|
|
GEngine->GameViewport->GetViewportSize(ViewportSize);
|
|
|
|
|
return Pixel / ViewportSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FVector2D UlxUtilityLibrary::ViewportPositionToPixel(FVector2D Fraction, bool Snap)
|
|
|
|
|
{
|
|
|
|
|
FVector2D ViewportSize;
|
|
|
|
|
GEngine->GameViewport->GetViewportSize(ViewportSize);
|
|
|
|
|
FVector2D Pixel = Fraction * ViewportSize;
|
|
|
|
|
if (Snap)
|
|
|
|
|
{
|
|
|
|
|
Pixel.X = FMath::FloorToDouble(Pixel.X) + 0.5;
|
|
|
|
|
Pixel.Y = FMath::FloorToDouble(Pixel.Y) + 0.5;
|
|
|
|
|
Pixel.X = FMath::Min(ViewportSize.X - 0.5, FMath::Max(0.5, Pixel.X));
|
|
|
|
|
Pixel.Y = FMath::Min(ViewportSize.Y - 0.5, FMath::Max(0.5, Pixel.Y));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Pixel.X = FMath::Min(ViewportSize.X, FMath::Max(0.0, Pixel.X));
|
|
|
|
|
Pixel.Y = FMath::Min(ViewportSize.Y, FMath::Max(0.0, Pixel.Y));
|
|
|
|
|
}
|
|
|
|
|
return Pixel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-01-14 18:37:31 -05:00
|
|
|
bool UlxUtilityLibrary::LineTraceThroughPixel(const APlayerController* PlayerController,
|
|
|
|
|
FVector2D PixelXY, double MaxDistanceFromCamera,
|
2025-01-07 18:46:40 -05:00
|
|
|
ETraceTypeQuery TraceChannel, bool bTraceComplex, EDrawDebugTrace::Type DrawDebugType, bool bIgnorePlayerPawn,
|
2025-01-14 18:37:31 -05:00
|
|
|
const TArray<AActor*>& ActorsToIgnore, FHitResult& HitResult)
|
2025-01-07 18:46:40 -05:00
|
|
|
{
|
|
|
|
|
const FLinearColor TraceColor = FLinearColor::Red;
|
|
|
|
|
const FLinearColor TraceHitColor = FLinearColor::Green;
|
|
|
|
|
const double DrawTime = 1.0;
|
|
|
|
|
|
|
|
|
|
// Zero out the return values.
|
|
|
|
|
HitResult.Init();
|
|
|
|
|
|
|
|
|
|
// Sanity check the distance.
|
|
|
|
|
if (MaxDistanceFromCamera <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure there's a player pawn.
|
|
|
|
|
AActor *PlayerPawn = PlayerController->GetPawn();
|
|
|
|
|
if (!PlayerPawn) return false;
|
|
|
|
|
|
|
|
|
|
// Calculate the trace start and trace end positions in world space.
|
|
|
|
|
FVector WorldStart, WorldDirection, WorldEnd;
|
2025-01-14 18:37:31 -05:00
|
|
|
if (!UGameplayStatics::DeprojectScreenToWorld(PlayerController, PixelXY, WorldStart, WorldDirection))
|
2025-01-07 18:46:40 -05:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
WorldEnd = WorldStart + (WorldDirection * MaxDistanceFromCamera);
|
|
|
|
|
|
|
|
|
|
// Find the hit.
|
|
|
|
|
if (UKismetSystemLibrary::LineTraceSingle(PlayerPawn, WorldStart, WorldEnd, TraceChannel, bTraceComplex,
|
|
|
|
|
ActorsToIgnore, DrawDebugType, HitResult, bIgnorePlayerPawn, TraceColor, TraceHitColor, DrawTime))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fail.
|
|
|
|
|
HitResult.Init();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-05-12 18:23:37 -04:00
|
|
|
|
|
|
|
|
void UlxUtilityLibrary::SetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D UpperLeftXY, FVector2D LowerRightXY)
|
|
|
|
|
{
|
|
|
|
|
if ((GridPanel == nullptr) || (GridPanel->ColumnFill.Num() != 3) || (GridPanel->RowFill.Num() != 3))
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogBlueprint, Error, TEXT("SetPositionOfGridPanelMiddleCell only works on 3x3 GridPanels."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((LowerRightXY.X < UpperLeftXY.X) || (LowerRightXY.Y < UpperLeftXY.Y))
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogBlueprint, Error, TEXT("LowerRightXY must be greater than or equal to UpperLeftXY"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpperLeftXY.X = FMath::Clamp(UpperLeftXY.X, 0.0f, 1.0f);
|
|
|
|
|
UpperLeftXY.Y = FMath::Clamp(UpperLeftXY.Y, 0.0f, 1.0f);
|
|
|
|
|
LowerRightXY.X = FMath::Clamp(LowerRightXY.X, 0.0f, 1.0f);
|
|
|
|
|
LowerRightXY.Y = FMath::Clamp(LowerRightXY.Y, 0.0f, 1.0f);
|
|
|
|
|
|
|
|
|
|
GridPanel->SetRowFill(0, UpperLeftXY.Y);
|
|
|
|
|
GridPanel->SetRowFill(1, LowerRightXY.Y - UpperLeftXY.Y);
|
|
|
|
|
GridPanel->SetRowFill(2, 1.0 - LowerRightXY.Y);
|
|
|
|
|
|
|
|
|
|
GridPanel->SetColumnFill(0, UpperLeftXY.X);
|
|
|
|
|
GridPanel->SetColumnFill(1, LowerRightXY.X - UpperLeftXY.X);
|
|
|
|
|
GridPanel->SetColumnFill(2, 1.0 - LowerRightXY.X);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UlxUtilityLibrary::GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D &UpperLeftXY, FVector2D &LowerRightXY)
|
|
|
|
|
{
|
|
|
|
|
TArray<float> &Col = GridPanel->ColumnFill;
|
|
|
|
|
TArray<float> &Row = GridPanel->RowFill;
|
|
|
|
|
|
|
|
|
|
// Set default return value for error situations.
|
|
|
|
|
UpperLeftXY.X = 0.0;
|
|
|
|
|
LowerRightXY.X = 1.0;
|
|
|
|
|
UpperLeftXY.Y = 0.0;
|
|
|
|
|
LowerRightXY.Y = 1.0;
|
|
|
|
|
|
|
|
|
|
if ((GridPanel == nullptr) || (Row.Num() != 3) || (Col.Num() != 3))
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogBlueprint, Error, TEXT("SetPositionOfGridPanelMiddleCell only works on 3x3 GridPanels."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double TotalX = Col[0] + Col[1] + Col[2];
|
|
|
|
|
double TotalY = Row[0] + Row[1] + Row[2];
|
|
|
|
|
|
|
|
|
|
if (TotalX > 0)
|
|
|
|
|
{
|
|
|
|
|
UpperLeftXY.X = Col[0] / TotalX;
|
|
|
|
|
LowerRightXY.X = (Col[0] + Col[1]) / TotalX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (TotalY > 0)
|
|
|
|
|
{
|
|
|
|
|
UpperLeftXY.Y = Row[0] / TotalY;
|
|
|
|
|
LowerRightXY.Y = (Row[0] + Row[1]) / TotalY;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-23 15:33:18 -04:00
|
|
|
|
2025-10-10 17:33:39 -04:00
|
|
|
void UlxScriptedAnimations::Keep(int n)
|
|
|
|
|
{
|
|
|
|
|
if (n < 0) n = 0;
|
|
|
|
|
if (Animations.Num() > n)
|
|
|
|
|
{
|
|
|
|
|
Animations.SetNum(n);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UlxScriptedAnimations::AddAnimation(
|
|
|
|
|
UObject* WorldContextObject, UAnimSequenceBase* Sequence, double FadeInTime, double FadeOutTime)
|
|
|
|
|
{
|
|
|
|
|
check(KeepCount >= 1);
|
|
|
|
|
FlxScriptedAnimation Result;
|
|
|
|
|
|
|
|
|
|
// Get World Time
|
|
|
|
|
UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject);
|
|
|
|
|
double WorldTime = World ? World->GetTimeSeconds() : 0.0;
|
|
|
|
|
|
|
|
|
|
// Fill the static setup fields
|
|
|
|
|
Result.Sequence = Sequence;
|
|
|
|
|
Result.FadeIn = FadeInTime;
|
|
|
|
|
Result.FadeOut = FadeOutTime;
|
|
|
|
|
Result.StartTime = WorldTime;
|
|
|
|
|
Result.AdjustedLength = (Result.Sequence ? static_cast<double>(Result.Sequence->GetPlayLength()) : 0.0);
|
|
|
|
|
|
|
|
|
|
Keep(KeepCount - 1);
|
|
|
|
|
Animations.Insert(Result, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UlxScriptedAnimations::FadeOutAll(UObject *WorldContextObject)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FlxScriptedAnimationProgress UlxUtilityLibrary::ScriptedAnimationProgress(const FlxScriptedAnimation &Animation, double CurrentTime)
|
|
|
|
|
{
|
|
|
|
|
FlxScriptedAnimationProgress Progress(Animation);
|
|
|
|
|
|
|
|
|
|
// Store the world time of the last update.
|
|
|
|
|
Progress.UpdateTime = CurrentTime;
|
|
|
|
|
|
|
|
|
|
// Compute time relationships
|
|
|
|
|
Progress.EndTime = Animation.StartTime + Progress.AdjustedLength;
|
|
|
|
|
Progress.ElapsedTime = FMath::Max(0.0, Progress.UpdateTime - Progress.StartTime);
|
|
|
|
|
Progress.TimeLeft = FMath::Max(0.0, Progress.EndTime - Progress.UpdateTime);
|
|
|
|
|
|
|
|
|
|
// Determine fade-in / fade-out blend
|
|
|
|
|
Progress.FadeInAlpha = 1.0;
|
|
|
|
|
Progress.FadeOutAlpha = 1.0;
|
|
|
|
|
if (Progress.FadeIn > 0.0) Progress.FadeInAlpha = FMath::Clamp(Progress.ElapsedTime / Progress.FadeIn, 0.0, 1.0);
|
|
|
|
|
if (Progress.FadeOut > 0.0) Progress.FadeOutAlpha = FMath::Clamp(Progress.TimeLeft / Progress.FadeOut, 0.0, 1.0);
|
|
|
|
|
Progress.FadeAlpha = FMath::Min(Progress.FadeInAlpha, Progress.FadeOutAlpha);
|
|
|
|
|
|
|
|
|
|
return Progress;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UlxUtilityLibrary::ScriptedAnimationEvaluatorData(const UlxScriptedAnimations *Animations, double CurrentTime,
|
|
|
|
|
UAnimSequenceBase *&Sequence0, float &ExplicitTime0,
|
|
|
|
|
UAnimSequenceBase *&Sequence1, float &ExplicitTime1,
|
|
|
|
|
UAnimSequenceBase *&Sequence2, float &ExplicitTime2,
|
|
|
|
|
float &BaseAlpha, float &Sequence0Alpha, float &Sequence1Alpha, float &Sequence2Alpha)
|
|
|
|
|
{
|
|
|
|
|
Sequence0 = nullptr;
|
|
|
|
|
Sequence1 = nullptr;
|
|
|
|
|
Sequence2 = nullptr;
|
|
|
|
|
ExplicitTime0 = 0.0;
|
|
|
|
|
ExplicitTime1 = 0.0;
|
|
|
|
|
ExplicitTime2 = 0.0;
|
|
|
|
|
BaseAlpha = 0.0;
|
|
|
|
|
Sequence0Alpha = 0.0;
|
|
|
|
|
Sequence1Alpha = 0.0;
|
|
|
|
|
Sequence2Alpha = 0.0;
|
|
|
|
|
|
|
|
|
|
if (Animations != nullptr)
|
|
|
|
|
{
|
|
|
|
|
const TArray<FlxScriptedAnimation> &Anims = Animations->GetAnimations();
|
|
|
|
|
if (Anims.Num() > 0)
|
|
|
|
|
{
|
|
|
|
|
FlxScriptedAnimationProgress Progress = ScriptedAnimationProgress(Anims[0], CurrentTime);
|
|
|
|
|
Sequence0 = Progress.Sequence;
|
|
|
|
|
ExplicitTime0 = Progress.ElapsedTime;
|
|
|
|
|
Sequence0Alpha = Progress.FadeAlpha;
|
|
|
|
|
}
|
|
|
|
|
if (Anims.Num() > 1)
|
|
|
|
|
{
|
|
|
|
|
FlxScriptedAnimationProgress Progress = ScriptedAnimationProgress(Anims[1], CurrentTime);
|
|
|
|
|
Sequence1 = Progress.Sequence;
|
|
|
|
|
ExplicitTime1 = Progress.ElapsedTime;
|
|
|
|
|
Sequence1Alpha = Progress.FadeAlpha;
|
|
|
|
|
}
|
|
|
|
|
if (Anims.Num() > 2)
|
|
|
|
|
{
|
|
|
|
|
FlxScriptedAnimationProgress Progress = ScriptedAnimationProgress(Anims[2], CurrentTime);
|
|
|
|
|
Sequence2 = Progress.Sequence;
|
|
|
|
|
ExplicitTime2 = Progress.ElapsedTime;
|
|
|
|
|
Sequence2Alpha = Progress.FadeAlpha;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double AlphaTotal = Sequence0Alpha + Sequence1Alpha + Sequence2Alpha;
|
|
|
|
|
if (AlphaTotal > 1.0)
|
|
|
|
|
{
|
|
|
|
|
double Scale = 1.0 / AlphaTotal;
|
|
|
|
|
Sequence0Alpha *= Scale;
|
|
|
|
|
Sequence1Alpha *= Scale;
|
|
|
|
|
Sequence2Alpha *= Scale;
|
|
|
|
|
BaseAlpha = 0.0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BaseAlpha = 1.0 - AlphaTotal;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-27 17:49:18 -04:00
|
|
|
ElxUsedOrNotUsed UlxUtilityLibrary::IsKeyUsedByMappingContext(const FKey &Key, const UInputMappingContext *MappingContext)
|
2025-05-23 15:33:18 -04:00
|
|
|
{
|
2025-05-27 16:30:49 -04:00
|
|
|
if (!MappingContext)
|
|
|
|
|
{
|
|
|
|
|
return ElxUsedOrNotUsed::NotUsed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const FEnhancedActionKeyMapping& Mapping : MappingContext->GetMappings())
|
|
|
|
|
{
|
2025-05-27 17:49:18 -04:00
|
|
|
if (Mapping.Key == Key) return ElxUsedOrNotUsed::Used;
|
2025-05-27 16:30:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ElxUsedOrNotUsed::NotUsed;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-02 19:21:17 -04:00
|
|
|
FKey UlxUtilityLibrary::GetKeyByName(const FName &Name)
|
|
|
|
|
{
|
|
|
|
|
FKey Key = FKey(Name);
|
|
|
|
|
return Key.IsValid() ? Key : FKey();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FKey UlxUtilityLibrary::GetKeyByNameString(const FString &Name)
|
|
|
|
|
{
|
|
|
|
|
FKey Key = FKey(FName(*Name));
|
|
|
|
|
return Key.IsValid() ? Key : FKey();
|
|
|
|
|
}
|