// Fill out your copyright notice in the Description page of Project Settings. #include "UtilityLibrary.h" #include "GameFramework/PlayerController.h" #include "EnhancedInputSubsystems.h" #include "Kismet/KismetSystemLibrary.h" #include "Kismet/GameplayStatics.h" #include "Blueprint/UserWidget.h" #include "Components/GridPanel.h" #include "InputMappingContext.h" #include "EnhancedInputComponent.h" #include "Animation/AnimSequenceBase.h" #define LOCTEXT_NAMESPACE "Luprex Utility" void UlxUtilityLibrary::CallFunctionByName(UObject *object, const FString &namepart1, const FString &namepart2, const FString &fallback, bool bFailIfNotFound) { FString fullname = namepart1 + namepart2; if (!IsValid(object)) { UE_LOG(LogBlueprint, Error, TEXT("In CallFunctionByName, object passed in is not valid.")); return; } UFunction* function = object->FindFunction(FName(*fullname)); if (function == nullptr) { function = object->FindFunction(FName(*fallback)); if (function == nullptr) { if (!bFailIfNotFound) { return; } UE_LOG(LogBlueprint, Error, TEXT("In CallFunctionByName, cannot find the named function or the fallback function")); return; } } if (function->ParmsSize != 0) { UE_LOG(LogBlueprint, Error, TEXT("CallFunctionByName can only call functions that have no parameters and no return values")); 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); } 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(Controller); if (pc != nullptr) { UEnhancedInputLocalPlayerSubsystem* subsys = ULocalPlayer::GetSubsystem(pc->GetLocalPlayer()); if (subsys != nullptr) { return subsys; } } return nullptr; } 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; } bool UlxUtilityLibrary::LineTraceThroughPixel(const APlayerController* PlayerController, FVector2D PixelXY, double MaxDistanceFromCamera, ETraceTypeQuery TraceChannel, bool bTraceComplex, EDrawDebugTrace::Type DrawDebugType, bool bIgnorePlayerPawn, const TArray& ActorsToIgnore, FHitResult& HitResult) { 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; if (!UGameplayStatics::DeprojectScreenToWorld(PlayerController, PixelXY, WorldStart, WorldDirection)) { 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; } 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 &Col = GridPanel->ColumnFill; TArray &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; } } 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(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 &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; } } ElxUsedOrNotUsed UlxUtilityLibrary::IsKeyUsedByMappingContext(const FKey &Key, const UInputMappingContext *MappingContext) { if (!MappingContext) { return ElxUsedOrNotUsed::NotUsed; } for (const FEnhancedActionKeyMapping& Mapping : MappingContext->GetMappings()) { if (Mapping.Key == Key) return ElxUsedOrNotUsed::Used; } return ElxUsedOrNotUsed::NotUsed; } 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(); }