第一次提交
This commit is contained in:
39
Plugins/UGC/Source/AuroraDevs_UGC/AuroraDevs_UGC.Build.cs
Normal file
39
Plugins/UGC/Source/AuroraDevs_UGC/AuroraDevs_UGC.Build.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class AuroraDevs_UGC : ModuleRules
|
||||
{
|
||||
public AuroraDevs_UGC(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
OptimizeCode = CodeOptimization.Never;
|
||||
|
||||
PCHUsage = PCHUsageMode.NoPCHs;
|
||||
MinFilesUsingPrecompiledHeaderOverride = 1;
|
||||
bUseUnity = false;
|
||||
IWYUSupport = IWYUSupport.Full;
|
||||
PrecompileForTargets = PrecompileTargetsType.Any;
|
||||
|
||||
PrivateIncludePaths.Add("AuroraDevs_UGC");
|
||||
if (Target.Version.MajorVersion >= 5 && Target.Version.MinorVersion >= 5)
|
||||
{
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"EngineCameras"
|
||||
});
|
||||
}
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"EnhancedInput",
|
||||
"InputCore" ,
|
||||
"LevelSequence",
|
||||
"MovieScene",
|
||||
"MovieSceneTracks",
|
||||
"TemplateSequence"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "AnimNotifyState/UGC_CameraPropertiesRequestAnimNotifyState.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "Camera/Modifiers/UGC_CameraPropertiesAnimNotifyModifiers.h"
|
||||
#include "Camera/UGC_PlayerCameraManager.h"
|
||||
|
||||
FLinearColor UUGC_FOVRequestAnimNotifyState::GetEditorColor()
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
const uint8 Ratio = 255 - static_cast<uint8>(FMath::GetMappedRangeValueClamped(FVector2D(5.f, 170.f), FVector2D(0.f, 255.f), TargetFOV));
|
||||
NotifyColor = FColor(255, Ratio, Ratio, 255);
|
||||
return NotifyColor;
|
||||
#else
|
||||
return FLinearColor::Black;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UUGC_FOVRequestAnimNotifyState::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference)
|
||||
{
|
||||
AActor* Owner = MeshComp->GetOwner();
|
||||
if (APawn* OwnerPawn = Cast<APawn>(Owner))
|
||||
{
|
||||
if (APlayerController* PC = OwnerPawn->GetController<APlayerController>())
|
||||
{
|
||||
if (AUGC_PlayerCameraManager* CameraManager = Cast<AUGC_PlayerCameraManager>(PC->PlayerCameraManager))
|
||||
{
|
||||
if (UUGC_FOVAnimNotifyCameraModifier* Modifier = CameraManager->FindCameraModifierOfType<UUGC_FOVAnimNotifyCameraModifier>())
|
||||
{
|
||||
Modifier->PushFOVAnimNotifyRequest(RequestId, TargetFOV, TotalDuration, BlendInDuration, BlendInCurve, BlendOutDuration, BlendOutCurve);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FString UUGC_FOVRequestAnimNotifyState::GetNotifyName_Implementation() const
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
if (bShowRequestIdInName)
|
||||
{
|
||||
return FString::Printf(TEXT("[%s][UGC] FOV Request: %.2f"), *RequestId.ToString(), TargetFOV);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
return FString::Printf(TEXT("[UGC] FOV Request: %.2f"), TargetFOV);
|
||||
}
|
||||
}
|
||||
|
||||
FLinearColor UUGC_ArmOffsetRequestAnimNotifyState::GetEditorColor()
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
NotifyColor = FColor(255, 212, 105, 255);
|
||||
return NotifyColor;
|
||||
#else
|
||||
return FLinearColor::Black;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UUGC_ArmOffsetRequestAnimNotifyState::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference)
|
||||
{
|
||||
AActor* Owner = MeshComp->GetOwner();
|
||||
if (APawn* OwnerPawn = Cast<APawn>(Owner))
|
||||
{
|
||||
if (APlayerController* PC = OwnerPawn->GetController<APlayerController>())
|
||||
{
|
||||
if (AUGC_PlayerCameraManager* CameraManager = Cast<AUGC_PlayerCameraManager>(PC->PlayerCameraManager))
|
||||
{
|
||||
if (UUGC_ArmOffsetAnimNotifyCameraModifier* Modifier = CameraManager->FindCameraModifierOfType<UUGC_ArmOffsetAnimNotifyCameraModifier>())
|
||||
{
|
||||
if (bModifySocketOffset)
|
||||
{
|
||||
Modifier->PushArmSocketOffsetAnimNotifyRequest(RequestId, TargetSocketOffset, TotalDuration, SocketOffsetBlendInDuration, SocketOffsetBlendInCurve, SocketOffsetBlendOutDuration, SocketOffsetBlendOutCurve);
|
||||
}
|
||||
|
||||
if (bModifyTargetOffset)
|
||||
{
|
||||
Modifier->PushArmTargetOffsetAnimNotifyRequest(RequestId, TargetTargetOffset, TotalDuration, TargetOffsetBlendInDuration, TargetOffsetBlendInCurve, TargetOffsetBlendOutDuration, TargetOffsetBlendOutCurve);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FString UUGC_ArmOffsetRequestAnimNotifyState::GetNotifyName_Implementation() const
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
if (bShowRequestIdInName)
|
||||
{
|
||||
FString Text = FString::Printf(TEXT("[%s][UGC]"), *RequestId.ToString());
|
||||
if (bModifySocketOffset)
|
||||
{
|
||||
Text.Append(FString::Printf(TEXT(" Socket: %s"), *TargetSocketOffset.ToCompactString()));
|
||||
}
|
||||
if (bModifyTargetOffset)
|
||||
{
|
||||
Text.Append(FString::Printf(TEXT(" Target: %s"), *TargetTargetOffset.ToCompactString()));
|
||||
}
|
||||
return Text;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
FString Text = TEXT("[UGC]");
|
||||
if (bModifySocketOffset)
|
||||
{
|
||||
Text.Append(FString::Printf(TEXT(" Socket: %s"), *TargetSocketOffset.ToCompactString()));
|
||||
}
|
||||
if (bModifyTargetOffset)
|
||||
{
|
||||
Text.Append(FString::Printf(TEXT(" Target: %s"), *TargetTargetOffset.ToCompactString()));
|
||||
}
|
||||
return Text;
|
||||
}
|
||||
}
|
||||
|
||||
FLinearColor UUGC_ArmLengthRequestAnimNotifyState::GetEditorColor()
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
NotifyColor = FColor(55, 219, 33, 255);
|
||||
return NotifyColor;
|
||||
#else
|
||||
return FLinearColor::Black;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UUGC_ArmLengthRequestAnimNotifyState::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference)
|
||||
{
|
||||
AActor* Owner = MeshComp->GetOwner();
|
||||
if (APawn* OwnerPawn = Cast<APawn>(Owner))
|
||||
{
|
||||
if (APlayerController* PC = OwnerPawn->GetController<APlayerController>())
|
||||
{
|
||||
if (AUGC_PlayerCameraManager* CameraManager = Cast<AUGC_PlayerCameraManager>(PC->PlayerCameraManager))
|
||||
{
|
||||
if (UUGC_ArmLengthAnimNotifyCameraModifier* Modifier = CameraManager->FindCameraModifierOfType<UUGC_ArmLengthAnimNotifyCameraModifier>())
|
||||
{
|
||||
Modifier->PushArmLengthAnimNotifyRequest(RequestId, TargetArmLength, TotalDuration, BlendInDuration, BlendInCurve, BlendOutDuration, BlendOutCurve);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FString UUGC_ArmLengthRequestAnimNotifyState::GetNotifyName_Implementation() const
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
if (bShowRequestIdInName)
|
||||
{
|
||||
return FString::Printf(TEXT("[%s][UGC] Arm Length: %.2f"), *RequestId.ToString(), TargetArmLength);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
return FString::Printf(TEXT("[UGC] Arm Length: %.2f"), TargetArmLength);
|
||||
}
|
||||
}
|
||||
20
Plugins/UGC/Source/AuroraDevs_UGC/Private/AuroraDevs_UGC.cpp
Normal file
20
Plugins/UGC/Source/AuroraDevs_UGC/Private/AuroraDevs_UGC.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "AuroraDevs_UGC.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(AuroraUGC);
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FAuroraDevs_UGCModule"
|
||||
|
||||
void FAuroraDevs_UGCModule::StartupModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FAuroraDevs_UGCModule::ShutdownModule()
|
||||
{
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FAuroraDevs_UGCModule, AuroraDevs_UGC)
|
||||
@@ -0,0 +1,331 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Camera/Components/UGC_SpringArmComponentBase.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "Kismet/KismetMathLibrary.h"
|
||||
#include "Math/RotationMatrix.h"
|
||||
#include "PhysicsEngine/PhysicsSettings.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "Camera/UGC_PlayerCameraManager.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
void UUGC_SpringArmComponentBase::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
}
|
||||
|
||||
void UUGC_SpringArmComponentBase::UpdateDesiredArmLocation(bool bDoTrace, bool bDoLocationLag, bool bDoRotationLag, float DeltaTime)
|
||||
{
|
||||
FRotator DesiredRot = GetTargetRotation();
|
||||
|
||||
// If our viewtarget is simulating using physics, we may need to clamp deltatime
|
||||
if (bClampToMaxPhysicsDeltaTime)
|
||||
{
|
||||
// Use the same max timestep cap as the physics system to avoid camera jitter when the viewtarget simulates less time than the camera
|
||||
DeltaTime = FMath::Min(DeltaTime, UPhysicsSettings::Get()->MaxPhysicsDeltaTime);
|
||||
}
|
||||
|
||||
// Apply 'lag' to rotation if desired
|
||||
if (bDoRotationLag)
|
||||
{
|
||||
if (bUseCameraLagSubstepping && DeltaTime > CameraLagMaxTimeStep && CameraRotationLagSpeed > 0.f)
|
||||
{
|
||||
const FRotator ArmRotStep = (DesiredRot - PreviousDesiredRot).GetNormalized() * (1.f / DeltaTime);
|
||||
FRotator LerpTarget = PreviousDesiredRot;
|
||||
float RemainingTime = DeltaTime;
|
||||
while (RemainingTime > UE_KINDA_SMALL_NUMBER)
|
||||
{
|
||||
const float LerpAmount = FMath::Min(CameraLagMaxTimeStep, RemainingTime);
|
||||
LerpTarget += ArmRotStep * LerpAmount;
|
||||
RemainingTime -= LerpAmount;
|
||||
|
||||
DesiredRot = FRotator(FMath::QInterpTo(FQuat(PreviousDesiredRot), FQuat(LerpTarget), LerpAmount, CameraRotationLagSpeed));
|
||||
PreviousDesiredRot = DesiredRot;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DesiredRot = FRotator(FMath::QInterpTo(FQuat(PreviousDesiredRot), FQuat(DesiredRot), DeltaTime, CameraRotationLagSpeed));
|
||||
}
|
||||
}
|
||||
PreviousDesiredRot = DesiredRot;
|
||||
|
||||
// Get the spring arm 'origin', the target we want to look at
|
||||
FVector const OriginalArmOrigin = GetComponentLocation() + TargetOffset;
|
||||
FVector const ArmOriginWithSocketOffset = OriginalArmOrigin + FRotationMatrix(DesiredRot).TransformVector(SocketOffset);
|
||||
|
||||
// Get the spring arm 'origin', the target we want to look at
|
||||
FVector ArmOrigin = OriginalArmOrigin;
|
||||
// We lag the target, not the actual camera position, so rotating the camera around does not have lag
|
||||
FVector DesiredLoc = ArmOrigin;
|
||||
if (bDoLocationLag)
|
||||
{
|
||||
if (bUseCameraLagSubstepping && DeltaTime > CameraLagMaxTimeStep && CameraLagSpeed > 0.f)
|
||||
{
|
||||
const FVector ArmMovementStep = (DesiredLoc - PreviousDesiredLoc) * (1.f / DeltaTime);
|
||||
FVector LerpTarget = PreviousDesiredLoc;
|
||||
|
||||
float RemainingTime = DeltaTime;
|
||||
while (RemainingTime > UE_KINDA_SMALL_NUMBER)
|
||||
{
|
||||
const float LerpAmount = FMath::Min(CameraLagMaxTimeStep, RemainingTime);
|
||||
LerpTarget += ArmMovementStep * LerpAmount;
|
||||
RemainingTime -= LerpAmount;
|
||||
|
||||
DesiredLoc = FMath::VInterpTo(PreviousDesiredLoc, LerpTarget, LerpAmount, CameraLagSpeed);
|
||||
PreviousDesiredLoc = DesiredLoc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DesiredLoc = FMath::VInterpTo(PreviousDesiredLoc, DesiredLoc, DeltaTime, CameraLagSpeed);
|
||||
}
|
||||
|
||||
// Clamp distance if requested
|
||||
bool bClampedDist = false;
|
||||
if (CameraLagMaxDistance > 0.f)
|
||||
{
|
||||
const FVector FromOrigin = DesiredLoc - ArmOrigin;
|
||||
if (FromOrigin.SizeSquared() > FMath::Square(CameraLagMaxDistance))
|
||||
{
|
||||
DesiredLoc = ArmOrigin + FromOrigin.GetClampedToMaxSize(CameraLagMaxDistance);
|
||||
bClampedDist = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
if (bDrawDebugLagMarkers)
|
||||
{
|
||||
DrawDebugSphere(GetWorld(), ArmOrigin, 5.f, 8, FColor::Green);
|
||||
DrawDebugSphere(GetWorld(), DesiredLoc, 5.f, 8, FColor::Yellow);
|
||||
|
||||
const FVector ToOrigin = ArmOrigin - DesiredLoc;
|
||||
DrawDebugDirectionalArrow(GetWorld(), DesiredLoc, DesiredLoc + ToOrigin * 0.5f, 7.5f, bClampedDist ? FColor::Red : FColor::Green);
|
||||
DrawDebugDirectionalArrow(GetWorld(), DesiredLoc + ToOrigin * 0.5f, ArmOrigin, 7.5f, bClampedDist ? FColor::Red : FColor::Green);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
PreviousArmOrigin = ArmOrigin;
|
||||
PreviousDesiredLoc = DesiredLoc;
|
||||
|
||||
// Now offset camera position back along our rotation
|
||||
DesiredLoc -= DesiredRot.Vector() * TargetArmLength;
|
||||
// Add socket offset in local space
|
||||
DesiredLoc += FRotationMatrix(DesiredRot).TransformVector(SocketOffset);
|
||||
|
||||
// Do a sweep to ensure we are not penetrating the world
|
||||
FVector ResultLoc;
|
||||
if (bDoTrace && (TargetArmLength != 0.0f))
|
||||
{
|
||||
bIsCameraFixed = true;
|
||||
FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(SpringArm), false, GetOwner());
|
||||
|
||||
FHitResult Result;
|
||||
// It's important to use the OriginalArmOrigin for the traces otherwise jumping over obstacles will trigger collisions (because ArmOrigin stays low).
|
||||
GetWorld()->SweepSingleByChannel(Result, OriginalArmOrigin, DesiredLoc, FQuat::Identity, ProbeChannel, FCollisionShape::MakeSphere(ProbeSize), QueryParams);
|
||||
|
||||
UnfixedCameraPosition = DesiredLoc;
|
||||
|
||||
ResultLoc = BlendLocations(DesiredLoc, Result.Location, Result.bBlockingHit, DeltaTime);
|
||||
|
||||
if (ResultLoc == DesiredLoc)
|
||||
{
|
||||
bIsCameraFixed = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ResultLoc = DesiredLoc;
|
||||
bIsCameraFixed = false;
|
||||
UnfixedCameraPosition = ResultLoc;
|
||||
}
|
||||
|
||||
// Form a transform for new world transform for camera
|
||||
FTransform WorldCamTM(DesiredRot, ResultLoc);
|
||||
// Convert to relative to component
|
||||
FTransform RelCamTM = WorldCamTM.GetRelativeTransform(GetComponentTransform());
|
||||
|
||||
// Update socket location/rotation
|
||||
RelativeSocketLocation = RelCamTM.GetLocation();
|
||||
RelativeSocketRotation = RelCamTM.GetRotation();
|
||||
|
||||
UpdateChildTransforms();
|
||||
}
|
||||
|
||||
FVector UUGC_SpringArmComponentBase::BlendLocations(const FVector& DesiredArmLocation, const FVector& TraceHitLocation, bool bHitSomething, float DeltaTime)
|
||||
{
|
||||
if (!bDoCollisionTest || !CameraCollisionSettings.bPreventPenetration)
|
||||
{
|
||||
return DesiredArmLocation;
|
||||
}
|
||||
|
||||
const bool bShouldComplexTrace = CameraCollisionSettings.bDoPredictiveAvoidance && CameraCollisionSettings.PenetrationAvoidanceFeelers.Num() > 0 && IsPlayerControlled();
|
||||
if (!bShouldComplexTrace)
|
||||
{
|
||||
return Super::BlendLocations(DesiredArmLocation, TraceHitLocation, bHitSomething, DeltaTime);
|
||||
}
|
||||
|
||||
FVector SafeLoc = FVector::ZeroVector;
|
||||
if (bMaintainFramingDuringCollisions)
|
||||
{
|
||||
FRotator DesiredRot = GetTargetRotation();
|
||||
const FVector OriginalArmOrigin = GetComponentLocation() + TargetOffset;
|
||||
SafeLoc = OriginalArmOrigin + FRotationMatrix(DesiredRot).TransformVector(SocketOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
SafeLoc = PreviousArmOrigin;
|
||||
}
|
||||
|
||||
const FVector CameraLoc = DesiredArmLocation;
|
||||
const FVector BaseRay = CameraLoc - SafeLoc;
|
||||
|
||||
if (BaseRay.IsNearlyZero())
|
||||
{
|
||||
return DesiredArmLocation;
|
||||
}
|
||||
|
||||
const FRotationMatrix BaseMatrix(BaseRay.Rotation());
|
||||
const FVector Right = BaseMatrix.GetUnitAxis(EAxis::Y);
|
||||
const FVector Up = BaseMatrix.GetUnitAxis(EAxis::Z);
|
||||
|
||||
float HardBlockedPct = DistBlockedPct;
|
||||
float SoftBlockedPct = DistBlockedPct;
|
||||
float BlockedThisFrame = 1.f;
|
||||
|
||||
UWorld* World = GetWorld();
|
||||
int32 NbrHits = 0;
|
||||
const AActor* OwningActor = GetOwner();
|
||||
|
||||
FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(CameraPen), false, OwningActor);
|
||||
QueryParams.AddIgnoredActor(OwningActor);
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
const int32 NewCapacity = CameraCollisionSettings.PenetrationAvoidanceFeelers.Num();
|
||||
const int32 OldCapacity = HitActors.Max();
|
||||
HitActors.Reset(FMath::Max(NewCapacity, OldCapacity)); // Reset array elements but keep allocation to max capacity.
|
||||
#endif
|
||||
|
||||
for (int32 i = 0; i < CameraCollisionSettings.PenetrationAvoidanceFeelers.Num(); ++i)
|
||||
{
|
||||
const FPenetrationAvoidanceFeeler& Feeler = CameraCollisionSettings.PenetrationAvoidanceFeelers[i];
|
||||
FRotator OffsetRot = Feeler.AdjustmentRot;
|
||||
|
||||
if (i == 0 && Feeler.AdjustmentRot != FRotator::ZeroRotator)
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (GEngine != nullptr)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, 0.f, FColor::Red, TEXT("DSA_SpringArmComponent: First Penetration Avoidance Feeler should always have an adjustment roation equal to 0,0,0!."));
|
||||
}
|
||||
#endif
|
||||
OffsetRot = FRotator::ZeroRotator;
|
||||
}
|
||||
|
||||
FVector RayTarget = BaseRay.RotateAngleAxis(OffsetRot.Yaw, Up).RotateAngleAxis(OffsetRot.Pitch, Right) + SafeLoc;
|
||||
|
||||
FHitResult Hit;
|
||||
FCollisionShape Shape = FCollisionShape::MakeSphere(Feeler.ProbeRadius);
|
||||
bool bHit = World->SweepSingleByChannel(Hit, SafeLoc, RayTarget, FQuat::Identity, ProbeChannel, Shape, QueryParams);
|
||||
|
||||
if (bHit && Hit.GetActor())
|
||||
{
|
||||
if (Hit.GetActor()->ActorHasTag(CameraCollisionSettings.IgnoreCameraCollisionTag))
|
||||
{
|
||||
QueryParams.AddIgnoredActor(Hit.GetActor());
|
||||
continue;
|
||||
}
|
||||
|
||||
++NbrHits;
|
||||
const float Weight = Hit.GetActor()->IsA<APawn>() ? Feeler.PawnWeight : Feeler.WorldWeight;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
HitActors.AddUnique(Hit.GetActor());
|
||||
#endif
|
||||
|
||||
float NewBlockPct = Hit.Time + (1.f - Hit.Time) * (1.f - Weight);
|
||||
NewBlockPct = (Hit.Location - SafeLoc).Size() / (RayTarget - SafeLoc).Size();
|
||||
|
||||
BlockedThisFrame = FMath::Min(NewBlockPct, BlockedThisFrame);
|
||||
if (i == 0)
|
||||
HardBlockedPct = BlockedThisFrame;
|
||||
else
|
||||
SoftBlockedPct = BlockedThisFrame;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (NbrHits > 0)
|
||||
{
|
||||
if (bPrintCollisionDebug && GEngine != nullptr)
|
||||
{
|
||||
if (bPrintHitActors)
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
for (int i = HitActors.Num() - 1; i >= 0; --i)
|
||||
{
|
||||
if (AActor* HitActor = HitActors[i])
|
||||
{
|
||||
FString const DebugText = FString::Printf(TEXT("DSA_SpringArmComponent: Colliding with %s."), *GetNameSafe(HitActor));
|
||||
GEngine->AddOnScreenDebugMessage(-1, 0.f, FColor(150, 150, 200), DebugText);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 6
|
||||
FString const DebugText = FString::Printf(TEXT("DSA_SpringArmComponent: %d feeler%hs colliding."), NbrHits, NbrHits > 1 ? "s" : "");
|
||||
#else
|
||||
FString const DebugText = FString::Printf(TEXT("DSA_SpringArmComponent: %d feeler%s colliding."), NbrHits, NbrHits > 1 ? "s" : "");
|
||||
#endif
|
||||
GEngine->AddOnScreenDebugMessage(-1, 0.f, FColor(150, 150, 200), DebugText);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (DistBlockedPct < BlockedThisFrame)
|
||||
{
|
||||
float BlendOutTime = FMath::Max(CameraCollisionSettings.PenetrationBlendOutTime, UE_KINDA_SMALL_NUMBER);
|
||||
DistBlockedPct += DeltaTime / BlendOutTime * (BlockedThisFrame - DistBlockedPct);
|
||||
}
|
||||
else if (DistBlockedPct > HardBlockedPct)
|
||||
{
|
||||
DistBlockedPct = HardBlockedPct;
|
||||
}
|
||||
else if (DistBlockedPct > SoftBlockedPct)
|
||||
{
|
||||
float BlendInTime = FMath::Max(CameraCollisionSettings.PenetrationBlendInTime, UE_KINDA_SMALL_NUMBER);
|
||||
DistBlockedPct -= DeltaTime / BlendInTime * (DistBlockedPct - SoftBlockedPct);
|
||||
}
|
||||
|
||||
DistBlockedPct = FMath::Clamp(DistBlockedPct, 0.f, 1.f);
|
||||
return SafeLoc + (CameraLoc - SafeLoc) * DistBlockedPct;
|
||||
}
|
||||
|
||||
bool UUGC_SpringArmComponentBase::IsPlayerControlled() const
|
||||
{
|
||||
APawn* PawnOwner = Cast<APawn>(GetOwner());
|
||||
AController* Controller = PawnOwner ? PawnOwner->GetController() : nullptr;
|
||||
|
||||
bool bPlayerControlled = (PawnOwner && Controller && PawnOwner->IsPlayerControlled());
|
||||
if (!bPlayerControlled)
|
||||
{
|
||||
if (GEngine)
|
||||
{
|
||||
if (APlayerController* PC = GEngine->GetFirstLocalPlayerController(GetWorld()))
|
||||
{
|
||||
if (APlayerCameraManager* PCM = PC->PlayerCameraManager)
|
||||
{
|
||||
bPlayerControlled = PCM->PendingViewTarget.Target == GetOwner() || (Controller != nullptr && PCM->PendingViewTarget.Target == Controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bPlayerControlled;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
|
||||
#include "Camera/Data/UGC_CameraData.h"
|
||||
|
||||
FPenetrationAvoidanceFeeler::FPenetrationAvoidanceFeeler()
|
||||
: AdjustmentRot(ForceInit)
|
||||
, WorldWeight(0.f)
|
||||
, PawnWeight(0.f)
|
||||
, ProbeRadius(0)
|
||||
{
|
||||
}
|
||||
|
||||
FPenetrationAvoidanceFeeler::FPenetrationAvoidanceFeeler(const FRotator& InAdjustmentRot, const float& InWorldWeight, const float& InPawnWeight, const float& InExtent)
|
||||
: AdjustmentRot(InAdjustmentRot)
|
||||
, WorldWeight(InWorldWeight)
|
||||
, PawnWeight(InPawnWeight)
|
||||
, ProbeRadius(InExtent)
|
||||
{
|
||||
}
|
||||
|
||||
FCameraCollisionSettings::FCameraCollisionSettings()
|
||||
{
|
||||
// AdjusmentRotation, WorldWeight, PawnWeight, Extent
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+0.00f, +00.00f, 0.00f), 0.50f, 1.00f, 15.00f));
|
||||
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+0.00f, +5.00f, 0.00f), 0.20f, 0.75f, 15.00f));
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+0.00f, +10.0f, 0.00f), 0.20f, 0.75f, 15.00f));
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+0.00f, -5.00f, 0.00f), 0.20f, 0.75f, 15.00f));
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+0.00f, -10.0f, 0.00f), 0.20f, 0.75f, 15.00f));
|
||||
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+0.00f, +5.00f, 0.00f), 0.15f, 0.50f, 15.00f));
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+0.00f, +10.0f, 0.00f), 0.15f, 0.50f, 15.00f));
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+0.00f, -5.00f, 0.00f), 0.15f, 0.50f, 15.00f));
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+0.00f, -10.0f, 0.00f), 0.15f, 0.50f, 15.00f));
|
||||
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(+15.0f, +0.00f, 0.00f), 0.50f, 1.00f, 10.00f));
|
||||
PenetrationAvoidanceFeelers.Add(FPenetrationAvoidanceFeeler(FRotator(-10.0f, +0.00f, 0.00f), 0.50f, 0.50f, 10.00f));
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Camera/Methods/UGC_IFocusTargetMethod.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
AActor* UUGC_IFocusTargetMethod::GetTargetLocation_Implementation(AActor* InOwner, FVector OwnerLocation, FVector ViewPointLocation, FRotator ViewPointRotation, FVector& OutTargetLocation)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UWorld* UUGC_IFocusTargetMethod::GetWorld() const
|
||||
{
|
||||
if (GWorld && GWorld->IsGameWorld() && GWorld->HasBegunPlay())
|
||||
{
|
||||
return GWorld;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Camera/Methods/UGC_IGetActorsMethod.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
void UUGC_IGetActorsMethod::GetActors_Implementation(AActor* InOwner, FVector OwnerLocation, FVector ViewPointLocation, FRotator ViewPointRotation, TArray<AActor*>& OutActors)
|
||||
{
|
||||
}
|
||||
|
||||
UWorld* UUGC_IGetActorsMethod::GetWorld() const
|
||||
{
|
||||
if (GWorld && GWorld->IsGameWorld() && GWorld->HasBegunPlay())
|
||||
{
|
||||
return GWorld;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Camera/Modifiers/UGC_CameraAnimationModifier.h"
|
||||
#include "Camera/CameraAnimationHelper.h"
|
||||
#include "Camera/Modifiers/UGC_CameraCollisionModifier.h"
|
||||
#include "Camera/PlayerCameraManager.h"
|
||||
#include "CameraAnimationSequence.h"
|
||||
#include "CameraAnimationSequencePlayer.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "DisplayDebugHelpers.h"
|
||||
#include "Engine/Canvas.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "EntitySystem/MovieSceneEntitySystemLinker.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "MovieSceneFwd.h"
|
||||
#include "Camera/UGC_PlayerCameraManager.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Kismet/KismetMathLibrary.h"
|
||||
#include "ProfilingDebugging/CountersTrace.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
|
||||
namespace UGCCameraAnimationHelper
|
||||
{
|
||||
FCameraAnimationHandle const UGCInvalid(MAX_int16, 0);
|
||||
FPenetrationAvoidanceFeeler const CollisionProbe = FPenetrationAvoidanceFeeler(FRotator(+0.00f, +00.00f, 0.00f), 0.50f, 1.00f, 15.00f);
|
||||
float constexpr CollisionBlendInTime = 0.05f;
|
||||
float constexpr CollisionBlendOutTime = 0.5f;
|
||||
FName const CollisionIgnoreActorWithTag = FName("UGC_AnimIgnoreCollision");
|
||||
}
|
||||
|
||||
FCameraAnimationHandle UUGC_CameraAnimationModifier::PlaySingleCameraAnimation(UCameraAnimationSequence* Sequence, FCameraAnimationParams Params, ECameraAnimationResetType ResetType, bool bInterruptOthers, bool bDoCollisionChecks)
|
||||
{
|
||||
if (!ensure(Sequence))
|
||||
{
|
||||
return UGCCameraAnimationHelper::UGCInvalid;
|
||||
}
|
||||
|
||||
TArray<UCameraAnimationSequence*> InterruptedSequences;
|
||||
if (IsAnyCameraAnimationSequence())
|
||||
{
|
||||
if (bInterruptOthers)
|
||||
{
|
||||
InterruptedSequences.Reserve(ActiveAnimations.Num());
|
||||
for (int i = 0; i < ActiveAnimations.Num(); ++i)
|
||||
{
|
||||
if (ActiveAnimations[i].IsValid() && ActiveAnimations[i].Player != nullptr && ActiveAnimations[i].Player->GetPlaybackStatus() == EMovieScenePlayerStatus::Playing)
|
||||
{
|
||||
InterruptedSequences.Add(ActiveAnimations[i].Sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always play one animation only
|
||||
int32 const NewIndex = FindInactiveCameraAnimation();
|
||||
check(NewIndex < MAX_uint16);
|
||||
|
||||
const uint16 InstanceSerial = NextInstanceSerialNumber++;
|
||||
FCameraAnimationHandle InstanceHandle{ (uint16)NewIndex, InstanceSerial };
|
||||
|
||||
FActiveCameraAnimationInfo& NewCameraAnimation = ActiveAnimations[NewIndex];
|
||||
|
||||
// Update UGCAnimInfo size
|
||||
if (ActiveAnimations.Num() > UGCAnimInfo.Num())
|
||||
{
|
||||
int const InfoIndex = UGCAnimInfo.Emplace();
|
||||
ensure(InfoIndex == NewIndex);
|
||||
}
|
||||
|
||||
UGCAnimInfo[NewIndex].ResetType = ResetType;
|
||||
UGCAnimInfo[NewIndex].bWasEasingOut = false;
|
||||
UGCAnimInfo[NewIndex].bDoCollisionChecks = bDoCollisionChecks;
|
||||
UGCAnimInfo[NewIndex].DistBlockedPct = 1.f;
|
||||
|
||||
NewCameraAnimation.Sequence = Sequence;
|
||||
NewCameraAnimation.Params = Params;
|
||||
NewCameraAnimation.Handle = InstanceHandle;
|
||||
|
||||
const FName PlayerName = MakeUniqueObjectName(this, UCameraAnimationSequencePlayer::StaticClass(), TEXT("CameraAnimationPlayer"));
|
||||
NewCameraAnimation.Player = NewObject<UCameraAnimationSequencePlayer>(this, PlayerName);
|
||||
const FName CameraStandInName = MakeUniqueObjectName(this, UCameraAnimationSequenceCameraStandIn::StaticClass(), TEXT("CameraStandIn"));
|
||||
NewCameraAnimation.CameraStandIn = NewObject<UCameraAnimationSequenceCameraStandIn>(this, CameraStandInName);
|
||||
|
||||
// Start easing in immediately if there's any defined.
|
||||
NewCameraAnimation.bIsEasingIn = (Params.EaseInDuration > 0.f);
|
||||
NewCameraAnimation.EaseInCurrentTime = 0.f;
|
||||
NewCameraAnimation.bIsEasingOut = false;
|
||||
NewCameraAnimation.EaseOutCurrentTime = 0.f;
|
||||
|
||||
// Initialize our stand-in object.
|
||||
NewCameraAnimation.CameraStandIn->Initialize(Sequence);
|
||||
|
||||
// Make the player always use our stand-in object whenever a sequence wants to spawn or possess an object.
|
||||
NewCameraAnimation.Player->SetBoundObjectOverride(NewCameraAnimation.CameraStandIn);
|
||||
|
||||
// Initialize it and start playing.
|
||||
NewCameraAnimation.Player->Initialize(Sequence);
|
||||
NewCameraAnimation.Player->Play(Params.bLoop, Params.bRandomStartTime);
|
||||
LastIndex = NewIndex;
|
||||
|
||||
if (bInterruptOthers && InterruptedSequences.Num() > 0)
|
||||
{
|
||||
static bool constexpr bInterrupt = true;
|
||||
for (int i = 0; i < InterruptedSequences.Num(); ++i)
|
||||
{
|
||||
OnAnimationEnded.ExecuteIfBound(InterruptedSequences[i], bInterrupt);
|
||||
}
|
||||
}
|
||||
|
||||
return InstanceHandle;
|
||||
}
|
||||
|
||||
void UUGC_CameraAnimationModifier::StopCameraAnimationSequence(UCameraAnimationSequence* Sequence, bool bImmediate)
|
||||
{
|
||||
if (!ActiveAnimations.IsEmpty())
|
||||
{
|
||||
for (int i = 0; i < ActiveAnimations.Num(); ++i)
|
||||
{
|
||||
if (ActiveAnimations[i].IsValid() && (Sequence == nullptr || ActiveAnimations[i].Sequence == Sequence))
|
||||
{
|
||||
if (UCameraAnimationSequence* InterruptedSequence = ActiveAnimations[i].Sequence)
|
||||
{
|
||||
StopCameraAnimation(ActiveAnimations[i].Handle, bImmediate);
|
||||
static bool constexpr bInterrupt = true;
|
||||
OnAnimationEnded.ExecuteIfBound(InterruptedSequence, bInterrupt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UUGC_CameraAnimationModifier::GetCurrentCameraAnimations(TArray<UCameraAnimationSequence*>& OutAnimations) const
|
||||
{
|
||||
if (!ActiveAnimations.IsEmpty())
|
||||
{
|
||||
for (int i = 0; i < ActiveAnimations.Num(); ++i)
|
||||
{
|
||||
if (ActiveAnimations[i].IsValid() && ActiveAnimations[i].Player && ActiveAnimations[i].Player->GetPlaybackStatus() == EMovieScenePlayerStatus::Playing)
|
||||
{
|
||||
OutAnimations.Add(ActiveAnimations[i].Sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UUGC_CameraAnimationModifier::IsCameraAnimationSequenceActive(UCameraAnimationSequence* Sequence) const
|
||||
{
|
||||
if (!ActiveAnimations.IsEmpty())
|
||||
{
|
||||
for (int i = 0; i < ActiveAnimations.Num(); ++i)
|
||||
{
|
||||
if (ActiveAnimations[i].IsValid() && ActiveAnimations[i].Sequence == Sequence && ActiveAnimations[i].Player->GetPlaybackStatus() == EMovieScenePlayerStatus::Playing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UUGC_CameraAnimationModifier::IsAnyCameraAnimationSequence() const
|
||||
{
|
||||
if (!ActiveAnimations.IsEmpty())
|
||||
{
|
||||
for (int i = 0; i < ActiveAnimations.Num(); ++i)
|
||||
{
|
||||
if (ActiveAnimations[i].IsValid() && ActiveAnimations[i].Player && ActiveAnimations[i].Player->GetPlaybackStatus() == EMovieScenePlayerStatus::Playing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UUGC_CameraAnimationModifier::ModifyCamera(float DeltaTime, FMinimalViewInfo& InOutPOV)
|
||||
{
|
||||
UCameraModifier::ModifyCamera(DeltaTime, InOutPOV);
|
||||
if (UGCCameraManager)
|
||||
{
|
||||
bool const bAnyActive = IsAnyCameraAnimationSequence();
|
||||
if (!CollisionModifier)
|
||||
{
|
||||
CollisionModifier = UGCCameraManager->FindCameraModifierOfType<UUGC_CameraCollisionModifier>();
|
||||
}
|
||||
|
||||
if (CollisionModifier)
|
||||
{
|
||||
bAnyActive ? CollisionModifier->AddSingleRayOverrider(this) : CollisionModifier->RemoveSingleRayOverrider(this);
|
||||
}
|
||||
}
|
||||
UGCTickActiveAnimation(DeltaTime, InOutPOV);
|
||||
return false;
|
||||
}
|
||||
|
||||
void UUGC_CameraAnimationModifier::CameraAnimation_SetEasingOutDelegate(FOnCameraAnimationEaseOutStarted& InOnAnimationEaseOutStarted, FCameraAnimationHandle AnimationHandle)
|
||||
{
|
||||
if (AnimationHandle.IsValid() && IsCameraAnimationActive(AnimationHandle))
|
||||
{
|
||||
OnAnimationEaseOutStarted = InOnAnimationEaseOutStarted;
|
||||
}
|
||||
}
|
||||
|
||||
void UUGC_CameraAnimationModifier::CameraAnimation_SetEndedDelegate(FOnCameraAnimationEnded& InOnAnimationEnded, FCameraAnimationHandle AnimationHandle)
|
||||
{
|
||||
if (AnimationHandle.IsValid() && IsCameraAnimationActive(AnimationHandle))
|
||||
{
|
||||
OnAnimationEnded = InOnAnimationEnded;
|
||||
}
|
||||
}
|
||||
|
||||
void UUGC_CameraAnimationModifier::UGCDeactivateCameraAnimation(FActiveCameraAnimationInfo& ActiveAnimation)
|
||||
{
|
||||
for (auto& a : ActiveAnimations)
|
||||
{
|
||||
if (a.Handle == ActiveAnimation.Handle)
|
||||
{
|
||||
if (a.Player && !ensure(a.Player->GetPlaybackStatus() == EMovieScenePlayerStatus::Stopped))
|
||||
{
|
||||
a.Player->Stop();
|
||||
}
|
||||
|
||||
a = FActiveCameraAnimationInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UUGC_CameraAnimationModifier::UGCTickActiveAnimation(float DeltaTime, FMinimalViewInfo& InOutPOV)
|
||||
{
|
||||
UGCCameraManager = Cast<AUGC_PlayerCameraManager>(CameraOwner);
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
if (!UGCCameraManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActiveAnimations.Num() >= 1)
|
||||
{
|
||||
for (int i = 0; i < ActiveAnimations.Num(); ++i)
|
||||
{
|
||||
|
||||
FActiveCameraAnimationInfo& ActiveAnimation = ActiveAnimations[i];
|
||||
if (ActiveAnimation.IsValid())
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
UGCDebugAnimation(ActiveAnimation, DeltaTime);
|
||||
#endif
|
||||
// float const Dilation = UGameplayStatics::GetGlobalTimeDilation(GetWorld());
|
||||
// float const UndilatedDeltaTime = FMath::IsNearlyZero(Dilation) ? 0.f : DeltaTime / Dilation;
|
||||
UGCTickAnimation(ActiveAnimation, DeltaTime, InOutPOV, i);
|
||||
UGCTickAnimCollision(ActiveAnimation, DeltaTime, InOutPOV, i);
|
||||
|
||||
if (ActiveAnimation.Player->GetPlaybackStatus() == EMovieScenePlayerStatus::Stopped)
|
||||
{
|
||||
// Here animation has just finished (ease out has completed as well)
|
||||
UGCDeactivateCameraAnimation(ActiveAnimation);
|
||||
static bool constexpr bInterrupt = false;
|
||||
OnAnimationEnded.ExecuteIfBound(ActiveAnimation.Sequence, bInterrupt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UUGC_CameraAnimationModifier::UGCTickAnimation(FActiveCameraAnimationInfo& CameraAnimation, float DeltaTime, FMinimalViewInfo& InOutPOV, int Index)
|
||||
{
|
||||
check(CameraAnimation.Player);
|
||||
check(CameraAnimation.CameraStandIn);
|
||||
|
||||
const FCameraAnimationParams Params = CameraAnimation.Params;
|
||||
UCameraAnimationSequencePlayer* Player = CameraAnimation.Player;
|
||||
UCameraAnimationSequenceCameraStandIn* CameraStandIn = CameraAnimation.CameraStandIn;
|
||||
|
||||
const FFrameRate InputRate = Player->GetInputRate();
|
||||
const FFrameTime CurrentPosition = Player->GetCurrentPosition();
|
||||
const float CurrentTime = InputRate.AsSeconds(CurrentPosition);
|
||||
const float DurationTime = InputRate.AsSeconds(Player->GetDuration()) * Params.PlayRate;
|
||||
|
||||
const float ScaledDeltaTime = DeltaTime * Params.PlayRate;
|
||||
|
||||
const float NewTime = CurrentTime + ScaledDeltaTime;
|
||||
const FFrameTime NewPosition = CurrentPosition + DeltaTime * Params.PlayRate * InputRate;
|
||||
|
||||
// Advance any easing times.
|
||||
if (CameraAnimation.bIsEasingIn)
|
||||
{
|
||||
CameraAnimation.EaseInCurrentTime += DeltaTime;
|
||||
}
|
||||
if (CameraAnimation.bIsEasingOut)
|
||||
{
|
||||
CameraAnimation.EaseOutCurrentTime += DeltaTime;
|
||||
}
|
||||
|
||||
ECameraAnimationResetType ResetType = UGCAnimInfo[Index].ResetType;
|
||||
bool const bWasEasingOut = UGCAnimInfo[Index].bWasEasingOut;
|
||||
|
||||
// Start easing out if we're nearing the end.
|
||||
// CameraAnimation may already be easing out if StopCameraAnimation has been called.
|
||||
if (!Player->GetIsLooping() && !CameraAnimation.bIsEasingOut)
|
||||
{
|
||||
const float BlendOutStartTime = DurationTime - Params.EaseOutDuration;
|
||||
if (NewTime > BlendOutStartTime)
|
||||
{
|
||||
CameraAnimation.bIsEasingOut = true;
|
||||
CameraAnimation.EaseOutCurrentTime = NewTime - BlendOutStartTime;
|
||||
|
||||
if (!bWasEasingOut)
|
||||
{
|
||||
// Here animation has just started easing out but hasn't finished yet
|
||||
OnAnimationEaseOutStarted.ExecuteIfBound(CameraAnimation.Sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're done easing in or out.
|
||||
bool bIsDoneEasingOut = false;
|
||||
if (CameraAnimation.bIsEasingIn)
|
||||
{
|
||||
if (CameraAnimation.EaseInCurrentTime > Params.EaseInDuration || Params.EaseInDuration == 0.f)
|
||||
{
|
||||
CameraAnimation.bIsEasingIn = false;
|
||||
}
|
||||
}
|
||||
if (CameraAnimation.bIsEasingOut)
|
||||
{
|
||||
if (CameraAnimation.EaseOutCurrentTime > Params.EaseOutDuration)
|
||||
{
|
||||
bIsDoneEasingOut = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out the final easing weight.
|
||||
const float EasingInT = FMath::Clamp((CameraAnimation.EaseInCurrentTime / Params.EaseInDuration), 0.f, 1.f);
|
||||
const float EasingInWeight = CameraAnimation.bIsEasingIn ?
|
||||
EvaluateEasing(Params.EaseInType, EasingInT) : 1.f;
|
||||
|
||||
const float EasingOutT = FMath::Clamp((1.f - CameraAnimation.EaseOutCurrentTime / Params.EaseOutDuration), 0.f, 1.f);
|
||||
const float EasingOutWeight = CameraAnimation.bIsEasingOut ?
|
||||
EvaluateEasing(Params.EaseOutType, EasingOutT) : 1.f;
|
||||
|
||||
const float TotalEasingWeight = FMath::Min(EasingInWeight, EasingOutWeight);
|
||||
|
||||
// We might be done playing. Normally the player will stop on its own, but there are other situation in which
|
||||
// the responsibility falls to this code:
|
||||
// - If the animation is looping and waiting for an explicit Stop() call on us.
|
||||
// - If there was a Stop() call with bImmediate=false to let an animation blend out.
|
||||
if (bIsDoneEasingOut || TotalEasingWeight <= 0.f)
|
||||
{
|
||||
Player->Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
UMovieSceneEntitySystemLinker* Linker = Player->GetEvaluationTemplate().GetEntitySystemLinker();
|
||||
CameraStandIn->Reset(InOutPOV, Linker);
|
||||
|
||||
// Get the "unanimated" properties that need to be treated additively.
|
||||
const float OriginalFieldOfView = CameraStandIn->FieldOfView;
|
||||
|
||||
// Update the sequence.
|
||||
Player->Update(NewPosition);
|
||||
|
||||
// Recalculate properties that might be invalidated by other properties having been animated.
|
||||
CameraStandIn->RecalcDerivedData();
|
||||
|
||||
// Grab the final animated (animated) values, figure out the delta, apply scale, and feed that into the result.
|
||||
// Transform is always treated as a local, additive value. The data better be good.
|
||||
const float Scale = Params.Scale * TotalEasingWeight;
|
||||
const FTransform AnimatedTransform = CameraStandIn->GetTransform();
|
||||
FVector AnimatedLocation = AnimatedTransform.GetLocation() * Scale;
|
||||
FRotator AnimatedRotation = AnimatedTransform.GetRotation().Rotator() * Scale;
|
||||
const FCameraAnimationHelperOffset CameraOffset{ AnimatedLocation, AnimatedRotation };
|
||||
|
||||
FVector OwnerLocation = GetViewTarget()->GetActorLocation();
|
||||
|
||||
// If using a character, camera should start from the pivot location of the mesh.
|
||||
{
|
||||
ACharacter* OwnerCharacter = GetViewTargetAs<ACharacter>();
|
||||
if (OwnerCharacter && OwnerCharacter->GetMesh())
|
||||
{
|
||||
OwnerLocation = OwnerCharacter->GetMesh()->GetComponentLocation();
|
||||
}
|
||||
}
|
||||
|
||||
// TO DO #GravityCompatibility
|
||||
FRotator const OwnerRot = FRotator(0.f, GetViewTarget()->GetActorRotation().Yaw, 0.f);
|
||||
const FMatrix OwnerRotationMatrix = FRotationMatrix(OwnerRot);
|
||||
|
||||
FMinimalViewInfo InPOV = InOutPOV;
|
||||
|
||||
// Blend from current camera location to actor location
|
||||
InPOV.Location = FMath::Lerp(InOutPOV.Location, OwnerLocation, Scale);
|
||||
InPOV.Rotation = FMath::Lerp(InOutPOV.Rotation, OwnerRot, Scale);
|
||||
|
||||
FCameraAnimationHelper::ApplyOffset(OwnerRotationMatrix, InPOV, CameraOffset, AnimatedLocation, AnimatedRotation);
|
||||
|
||||
InOutPOV.Location = AnimatedLocation;
|
||||
InOutPOV.Rotation = AnimatedRotation;
|
||||
|
||||
// Blend back depending on reset type
|
||||
if (ResetType != ECameraAnimationResetType::BackToStart
|
||||
&& CameraOwner && CameraOwner->GetOwningPlayerController() && CameraAnimation.bIsEasingOut && !bWasEasingOut)
|
||||
{
|
||||
FRotator TargetRot = OwnerRot;
|
||||
switch (ResetType)
|
||||
{
|
||||
case ECameraAnimationResetType::ContinueFromEnd:
|
||||
{
|
||||
bool const bIsStrafing = UGCCameraManager->IsOwnerStrafing();
|
||||
if (!bIsStrafing)
|
||||
{
|
||||
// TO DO #GravityCompatibility
|
||||
TargetRot = FRotator(InOutPOV.Rotation.Pitch, InOutPOV.Rotation.Yaw, 0.f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
CameraOwner->GetOwningPlayerController()->SetControlRotation(TargetRot);
|
||||
}
|
||||
|
||||
// FieldOfView follows the current camera's value every frame, so we can compute how much the animation is
|
||||
// changing it.
|
||||
const float AnimatedFieldOfView = CameraStandIn->FieldOfView;
|
||||
const float DeltaFieldOfView = AnimatedFieldOfView - OriginalFieldOfView;
|
||||
InOutPOV.FOV = OriginalFieldOfView + DeltaFieldOfView * Scale;
|
||||
|
||||
// Add the post-process settings.
|
||||
if (CameraOwner != nullptr && CameraStandIn->PostProcessBlendWeight > 0.f)
|
||||
{
|
||||
CameraOwner->AddCachedPPBlend(CameraStandIn->PostProcessSettings, CameraStandIn->PostProcessBlendWeight);
|
||||
}
|
||||
|
||||
UGCAnimInfo[Index].bWasEasingOut = CameraAnimation.bIsEasingOut;
|
||||
}
|
||||
|
||||
FVector UUGC_CameraAnimationModifier::GetTraceSafeLocation(FMinimalViewInfo const& InPOV)
|
||||
{
|
||||
AActor* TargetActor = GetViewTarget();
|
||||
FVector SafeLocation = TargetActor ? TargetActor->GetActorLocation() : FVector::Zero();
|
||||
if (TargetActor && UGCCameraManager)
|
||||
{
|
||||
if (USpringArmComponent* SpringArm = UGCCameraManager->GetOwnerSpringArmComponent())
|
||||
{
|
||||
SafeLocation = SpringArm->GetComponentLocation() + SpringArm->TargetOffset;
|
||||
}
|
||||
else if (UPrimitiveComponent const* PPActorRootComponent = Cast<UPrimitiveComponent>(TargetActor->GetRootComponent()))
|
||||
{
|
||||
// Attempt at picking SafeLocation automatically, so we reduce camera translation when aiming.
|
||||
// Our camera is our reticle, so we want to preserve our aim and keep that as steady and smooth as possible.
|
||||
// Pick closest point on capsule to our aim line.
|
||||
FVector ClosestPointOnLineToCapsuleCenter;
|
||||
FMath::PointDistToLine(SafeLocation, InPOV.Rotation.Vector(), InPOV.Location, ClosestPointOnLineToCapsuleCenter);
|
||||
|
||||
// Adjust Safe distance height to be same as aim line, but within capsule.
|
||||
float const PushInDistance = UGCCameraAnimationHelper::CollisionProbe.ProbeRadius;
|
||||
float const MaxHalfHeight = TargetActor->GetSimpleCollisionHalfHeight() - PushInDistance;
|
||||
SafeLocation.Z = FMath::Clamp(ClosestPointOnLineToCapsuleCenter.Z, SafeLocation.Z - MaxHalfHeight, SafeLocation.Z + MaxHalfHeight);
|
||||
|
||||
float DistanceSqr = 0.f;
|
||||
PPActorRootComponent->GetSquaredDistanceToCollision(ClosestPointOnLineToCapsuleCenter, DistanceSqr, SafeLocation);
|
||||
// Push back inside capsule to avoid initial penetration when doing line checks.
|
||||
SafeLocation += (SafeLocation - ClosestPointOnLineToCapsuleCenter).GetSafeNormal() * PushInDistance;
|
||||
}
|
||||
}
|
||||
return SafeLocation;
|
||||
}
|
||||
|
||||
void UUGC_CameraAnimationModifier::UGCTickAnimCollision(FActiveCameraAnimationInfo& CameraAnimation, float DeltaTime, FMinimalViewInfo& InOutPOV, int Index)
|
||||
{
|
||||
if (!UGCAnimInfo[Index].bDoCollisionChecks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
check(CameraAnimation.Player);
|
||||
check(CameraAnimation.CameraStandIn);
|
||||
const FCameraAnimationParams& Params = CameraAnimation.Params;
|
||||
const float BlendInTime = FMath::Max(UGCCameraAnimationHelper::CollisionBlendInTime, UE_KINDA_SMALL_NUMBER);
|
||||
float BlendOutTime = FMath::Max(UGCCameraAnimationHelper::CollisionBlendOutTime, UE_KINDA_SMALL_NUMBER);
|
||||
if (CameraAnimation.bIsEasingOut)
|
||||
{
|
||||
BlendOutTime = FMath::Min(Params.EaseOutDuration - CameraAnimation.EaseOutCurrentTime, UGCCameraAnimationHelper::CollisionBlendOutTime);
|
||||
BlendOutTime = FMath::Max(BlendOutTime, UE_KINDA_SMALL_NUMBER);
|
||||
}
|
||||
|
||||
FVector SafeLoc = GetTraceSafeLocation(InOutPOV);
|
||||
|
||||
const FVector CameraLoc = InOutPOV.Location;
|
||||
const FVector BaseRay = CameraLoc - SafeLoc;
|
||||
|
||||
if (BaseRay.IsNearlyZero())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const FRotationMatrix BaseMatrix(BaseRay.Rotation());
|
||||
const FVector Right = BaseMatrix.GetUnitAxis(EAxis::Y);
|
||||
const FVector Up = BaseMatrix.GetUnitAxis(EAxis::Z);
|
||||
|
||||
float HardBlockedPct = UGCAnimInfo[Index].DistBlockedPct;
|
||||
float SoftBlockedPct = UGCAnimInfo[Index].DistBlockedPct;
|
||||
float BlockedThisFrame = 1.f;
|
||||
|
||||
UWorld* World = GetWorld();
|
||||
int32 NbrHits = 0;
|
||||
const AActor* OwningActor = GetViewTarget();
|
||||
|
||||
FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(CameraPen), false, OwningActor);
|
||||
QueryParams.AddIgnoredActor(OwningActor);
|
||||
|
||||
{
|
||||
const FPenetrationAvoidanceFeeler& Feeler = UGCCameraAnimationHelper::CollisionProbe;
|
||||
const FRotator OffsetRot = Feeler.AdjustmentRot;
|
||||
|
||||
FVector RayTarget = BaseRay.RotateAngleAxis(OffsetRot.Yaw, Up).RotateAngleAxis(OffsetRot.Pitch, Right) + SafeLoc;
|
||||
|
||||
FHitResult Hit;
|
||||
FCollisionShape Shape = FCollisionShape::MakeSphere(Feeler.ProbeRadius);
|
||||
bool bHit = World->SweepSingleByChannel(Hit, SafeLoc, RayTarget, FQuat::Identity, ECollisionChannel::ECC_Camera, Shape, QueryParams);
|
||||
|
||||
if (bHit && Hit.GetActor())
|
||||
{
|
||||
if (Hit.GetActor()->ActorHasTag(UGCCameraAnimationHelper::CollisionIgnoreActorWithTag))
|
||||
{
|
||||
QueryParams.AddIgnoredActor(Hit.GetActor());
|
||||
}
|
||||
else
|
||||
{
|
||||
++NbrHits;
|
||||
const float Weight = Hit.GetActor()->IsA<APawn>() ? Feeler.PawnWeight : Feeler.WorldWeight;
|
||||
float NewBlockPct = Hit.Time + (1.f - Hit.Time) * (1.f - Weight);
|
||||
NewBlockPct = (Hit.Location - SafeLoc).Size() / (RayTarget - SafeLoc).Size();
|
||||
|
||||
BlockedThisFrame = FMath::Min(NewBlockPct, BlockedThisFrame);
|
||||
HardBlockedPct = BlockedThisFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (NbrHits > 0)
|
||||
{
|
||||
if (bDebug && GEngine != nullptr)
|
||||
{
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 6
|
||||
FString const& DebugText = FString::Printf(TEXT("UGC_CameraAnimationModifier: %d feeler%hs colliding."), NbrHits, NbrHits > 1 ? "s" : "");
|
||||
#else
|
||||
FString const& DebugText = FString::Printf(TEXT("UGC_CameraAnimationModifier: %d feeler%s colliding."), NbrHits, NbrHits > 1 ? "s" : "");
|
||||
#endif
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor(150, 150, 200), DebugText);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (UGCAnimInfo[Index].DistBlockedPct < BlockedThisFrame)
|
||||
{
|
||||
UGCAnimInfo[Index].DistBlockedPct += DeltaTime / BlendOutTime * (BlockedThisFrame - UGCAnimInfo[Index].DistBlockedPct);
|
||||
}
|
||||
else if (UGCAnimInfo[Index].DistBlockedPct > HardBlockedPct)
|
||||
{
|
||||
UGCAnimInfo[Index].DistBlockedPct = HardBlockedPct;
|
||||
}
|
||||
else if (UGCAnimInfo[Index].DistBlockedPct > SoftBlockedPct)
|
||||
{
|
||||
UGCAnimInfo[Index].DistBlockedPct -= DeltaTime / BlendInTime * (UGCAnimInfo[Index].DistBlockedPct - SoftBlockedPct);
|
||||
}
|
||||
|
||||
UGCAnimInfo[Index].DistBlockedPct = FMath::Clamp(UGCAnimInfo[Index].DistBlockedPct, 0.f, 1.f);
|
||||
InOutPOV.Location = SafeLoc + (CameraLoc - SafeLoc) * UGCAnimInfo[Index].DistBlockedPct;
|
||||
}
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
void UUGC_CameraAnimationModifier::UGCDebugAnimation(FActiveCameraAnimationInfo& ActiveAnimation, float DeltaTime)
|
||||
{
|
||||
if (bDebug && ActiveAnimation.IsValid() && GEngine)
|
||||
{
|
||||
const FFrameRate InputRate = ActiveAnimation.Player->GetInputRate();
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3
|
||||
const FFrameTime DurationFrames = ActiveAnimation.Player->GetDuration();
|
||||
#elif ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION < 3
|
||||
const FFrameNumber DurationFrames = ActiveAnimation.Player->GetDuration();
|
||||
#endif
|
||||
const FFrameTime CurrentPosition = ActiveAnimation.Player->GetCurrentPosition();
|
||||
|
||||
const float CurrentTime = InputRate.AsSeconds(CurrentPosition);
|
||||
const float DurationSeconds = InputRate.AsSeconds(DurationFrames);
|
||||
|
||||
const FString LoopString = ActiveAnimation.Params.bLoop ? TEXT(" - Looping") : TEXT("");
|
||||
const FString EaseInString = ActiveAnimation.bIsEasingIn ? FString::Printf(TEXT(" - Easing In: %f / %f"), ActiveAnimation.EaseInCurrentTime, ActiveAnimation.Params.EaseInDuration) : TEXT("");
|
||||
const FString EaseOutString = ActiveAnimation.bIsEasingOut ? FString::Printf(TEXT(" - Easing Out: %f / %f"), ActiveAnimation.EaseOutCurrentTime, ActiveAnimation.Params.EaseOutDuration) : TEXT("");
|
||||
const FString DebugText = FString::Printf(TEXT("UGC_CameraAnimationModifier: %s - PlayRate: %f%s - Duration: %f - Elapsed: %f%s%s"), *GetNameSafe(ActiveAnimation.Sequence), ActiveAnimation.Params.PlayRate, *LoopString, DurationSeconds, CurrentTime, *EaseInString, *EaseOutString);
|
||||
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor(49, 61, 255), DebugText);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,332 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Camera/Modifiers/UGC_CameraCollisionModifier.h"
|
||||
|
||||
#include "Camera/PlayerCameraManager.h"
|
||||
#include "Camera/UGC_PlayerCameraManager.h"
|
||||
#include "CineCameraActor.h"
|
||||
#include "CollisionQueryParams.h"
|
||||
#include "CollisionShape.h"
|
||||
#include "Components/PrimitiveComponent.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/CameraBlockingVolume.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
|
||||
/*
|
||||
* DEPRECATED. USE UGC_SpringArmComponent INSTEAD.
|
||||
*/
|
||||
|
||||
namespace CollisionHelpers
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
FColor const DebugColor = FColor(150, 150, 200);
|
||||
#endif
|
||||
}
|
||||
|
||||
UUGC_CameraCollisionModifier::UUGC_CameraCollisionModifier()
|
||||
{
|
||||
Priority = 134;
|
||||
bExclusive = true;
|
||||
}
|
||||
|
||||
bool UUGC_CameraCollisionModifier::ModifyCamera(float DeltaTime, FMinimalViewInfo& InOutPOV)
|
||||
{
|
||||
Super::ModifyCamera(DeltaTime, InOutPOV);
|
||||
|
||||
if (!bPlayDuringCameraAnimations)
|
||||
{
|
||||
if (UGCCameraManager && UGCCameraManager->IsPlayingAnyCameraAnimation())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (SpringArm)
|
||||
{
|
||||
// Don't do collision tests is spring arm is taking care of them.
|
||||
if (SpringArm->bDoCollisionTest && CollisionSettings.bPreventPenetration)
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (GEngine != nullptr)
|
||||
{
|
||||
FString const CameraManagerName = GetNameSafe(CameraOwner);
|
||||
FString const ActorName = GetNameSafe(GetViewTarget());
|
||||
FString const SpringName = GetNameSafe(SpringArm);
|
||||
FString const DebugText = FString::Printf(TEXT("Actor %s has Collision Checks enabled on SpringArm %s but Camera Manager %s has Camera Modifier UGC_CameraCollisionModifier. This is not allowed so the modifier will be aborted.")
|
||||
, *ActorName, *SpringName, *CameraManagerName);
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor::Red, DebugText);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Do collision checks
|
||||
UpdatePreventPenetration(DeltaTime, InOutPOV);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UUGC_CameraCollisionModifier::UpdatePreventPenetration(float DeltaTime, FMinimalViewInfo& InOutPOV)
|
||||
{
|
||||
if (!CollisionSettings.bPreventPenetration)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FVector const SafeLocation = GetTraceSafeLocation(InOutPOV);
|
||||
|
||||
// Then aim line to desired camera position
|
||||
bool const bSingleRayPenetrationCheck = !CollisionSettings.bDoPredictiveAvoidance || !SingleRayOverriders.IsEmpty();
|
||||
|
||||
if (bSingleRayPenetrationCheck)
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (bDebug && GEngine != nullptr)
|
||||
{
|
||||
FString const& DebugText = FString::Printf(TEXT("UGC_CameraCollisionModifier: Single Ray mode enabled."));
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, CollisionHelpers::DebugColor, DebugText);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (AActor* TargetActor = GetViewTarget())
|
||||
{
|
||||
PreventCameraPenetration(*TargetActor, SafeLocation, InOutPOV.Location, DeltaTime, AimLineToDesiredPosBlockedPct, bSingleRayPenetrationCheck);
|
||||
}
|
||||
}
|
||||
|
||||
FVector UUGC_CameraCollisionModifier::GetTraceSafeLocation(FMinimalViewInfo const& InPOV)
|
||||
{
|
||||
AActor* TargetActor = GetViewTarget();
|
||||
FVector SafeLocation = TargetActor ? TargetActor->GetActorLocation() : FVector::Zero();
|
||||
if (TargetActor)
|
||||
{
|
||||
if (SpringArm)
|
||||
{
|
||||
SafeLocation = SpringArm->GetComponentLocation() + SpringArm->TargetOffset;
|
||||
}
|
||||
else if (UPrimitiveComponent const* PPActorRootComponent = Cast<UPrimitiveComponent>(TargetActor->GetRootComponent()))
|
||||
{
|
||||
// Attempt at picking SafeLocation automatically, so we reduce camera translation when aiming.
|
||||
// Our camera is our reticle, so we want to preserve our aim and keep that as steady and smooth as possible.
|
||||
// Pick closest point on capsule to our aim line.
|
||||
FVector ClosestPointOnLineToCapsuleCenter;
|
||||
FMath::PointDistToLine(SafeLocation, InPOV.Rotation.Vector(), InPOV.Location, ClosestPointOnLineToCapsuleCenter);
|
||||
|
||||
// Adjust Safe distance height to be same as aim line, but within capsule.
|
||||
float const PushInDistance = CollisionSettings.PenetrationAvoidanceFeelers[0].ProbeRadius;
|
||||
float const MaxHalfHeight = TargetActor->GetSimpleCollisionHalfHeight() - PushInDistance;
|
||||
SafeLocation.Z = FMath::Clamp(ClosestPointOnLineToCapsuleCenter.Z, SafeLocation.Z - MaxHalfHeight, SafeLocation.Z + MaxHalfHeight);
|
||||
|
||||
float DistanceSqr = 0.f;
|
||||
PPActorRootComponent->GetSquaredDistanceToCollision(ClosestPointOnLineToCapsuleCenter, DistanceSqr, SafeLocation);
|
||||
// Push back inside capsule to avoid initial penetration when doing line checks.
|
||||
if (CollisionSettings.PenetrationAvoidanceFeelers.Num() > 0)
|
||||
{
|
||||
SafeLocation += (SafeLocation - ClosestPointOnLineToCapsuleCenter).GetSafeNormal() * PushInDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SafeLocation;
|
||||
}
|
||||
|
||||
void UUGC_CameraCollisionModifier::PreventCameraPenetration(AActor const& ViewTarget, FVector const& SafeLoc, FVector& OutCameraLoc, float const& DeltaTime, float& OutDistBlockedPct, bool bSingleRayOnly)
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
DebugActorsHitDuringCameraPenetration.Reset();
|
||||
#endif
|
||||
|
||||
FVector const& CameraLoc = OutCameraLoc;
|
||||
float HardBlockedPct = OutDistBlockedPct;
|
||||
float SoftBlockedPct = OutDistBlockedPct;
|
||||
|
||||
FVector BaseRay = CameraLoc - SafeLoc;
|
||||
FRotationMatrix BaseRayMatrix(BaseRay.Rotation());
|
||||
FVector BaseRayLocalUp, BaseRayLocalFwd, BaseRayLocalRight;
|
||||
|
||||
BaseRayMatrix.GetScaledAxes(BaseRayLocalFwd, BaseRayLocalRight, BaseRayLocalUp);
|
||||
|
||||
float DistBlockedPctThisFrame = 1.f;
|
||||
|
||||
int32 const NumRaysToShoot = bSingleRayOnly ? FMath::Min(1, CollisionSettings.PenetrationAvoidanceFeelers.Num()) : CollisionSettings.PenetrationAvoidanceFeelers.Num();
|
||||
|
||||
CollidingFeelers = TStaticBitArray<128U>();
|
||||
|
||||
FCollisionQueryParams SphereParams(SCENE_QUERY_STAT(CameraPen), false, nullptr/*PlayerCamera*/);
|
||||
|
||||
SphereParams.AddIgnoredActor(&ViewTarget);
|
||||
|
||||
FCollisionShape SphereShape = FCollisionShape::MakeSphere(0.f);
|
||||
UWorld* World = GetWorld();
|
||||
|
||||
int32 NbrHits = 0;
|
||||
|
||||
for (int32 RayIdx = 0; RayIdx < NumRaysToShoot; ++RayIdx)
|
||||
{
|
||||
FPenetrationAvoidanceFeeler& Feeler = CollisionSettings.PenetrationAvoidanceFeelers[RayIdx];
|
||||
FRotator AdjustmentRot = Feeler.AdjustmentRot;
|
||||
|
||||
if (RayIdx == 0 && Feeler.AdjustmentRot != FRotator::ZeroRotator)
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (GEngine != nullptr)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor::Red, TEXT("UGC_CameraCollisionModifier: First Penetration Avoidance Feeler should always have an adjustment roation equal to 0,0,0!."));
|
||||
}
|
||||
#endif
|
||||
AdjustmentRot = FRotator::ZeroRotator;
|
||||
}
|
||||
|
||||
// calc ray target
|
||||
FVector RayTarget;
|
||||
{
|
||||
// TO DO #GravityCompatibility
|
||||
FVector RotatedRay = BaseRay.RotateAngleAxis(AdjustmentRot.Yaw, BaseRayLocalUp);
|
||||
RotatedRay = RotatedRay.RotateAngleAxis(AdjustmentRot.Pitch, BaseRayLocalRight);
|
||||
RayTarget = SafeLoc + RotatedRay;
|
||||
}
|
||||
|
||||
// Cast for world and pawn hits separately. this is so we can safely ignore the camera's target pawn.
|
||||
bool const bForceSmallShape = bSingleRayOnly && !SingleRayOverriders.IsEmpty();
|
||||
SphereShape.Sphere.Radius = bForceSmallShape ? 2.f : Feeler.ProbeRadius;
|
||||
ECollisionChannel TraceChannel = ECC_Camera;
|
||||
|
||||
// Do multi-line check to make sure the hits we throw out aren't masking real hits behind (these are important rays).
|
||||
// Passing camera as actor so that camerablockingvolumes know when it's the camera doing traces.
|
||||
FHitResult Hit;
|
||||
const bool bHit = World->SweepSingleByChannel(Hit, SafeLoc, RayTarget, FQuat::Identity, TraceChannel, SphereShape, SphereParams);
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (World->TimeSince(LastDrawDebugTime) < 1.f)
|
||||
{
|
||||
DrawDebugSphere(World, SafeLoc, SphereShape.Sphere.Radius, 8, FColor::Red);
|
||||
DrawDebugSphere(World, bHit ? Hit.Location : RayTarget, SphereShape.Sphere.Radius, 8, FColor::Red);
|
||||
DrawDebugLine(World, SafeLoc, bHit ? Hit.Location : RayTarget, FColor::Red);
|
||||
}
|
||||
#endif // ENABLE_DRAW_DEBUG
|
||||
|
||||
const AActor* HitActor = Hit.GetActor();
|
||||
|
||||
if (bHit && HitActor)
|
||||
{
|
||||
bool bIgnoreHit = false;
|
||||
|
||||
if (HitActor->ActorHasTag(CollisionSettings.IgnoreCameraCollisionTag))
|
||||
{
|
||||
bIgnoreHit = true;
|
||||
SphereParams.AddIgnoredActor(HitActor);
|
||||
}
|
||||
|
||||
// Ignore CameraBlockingVolume hits that occur in front of the ViewTarget.
|
||||
if (!bIgnoreHit && HitActor->IsA<ACameraBlockingVolume>())
|
||||
{
|
||||
const FVector ViewTargetForwardXY = ViewTarget.GetActorForwardVector().GetSafeNormal2D();
|
||||
const FVector ViewTargetLocation = ViewTarget.GetActorLocation();
|
||||
const FVector HitOffset = Hit.Location - ViewTargetLocation;
|
||||
const FVector HitDirectionXY = HitOffset.GetSafeNormal2D();
|
||||
const float DotHitDirection = FVector::DotProduct(ViewTargetForwardXY, HitDirectionXY);
|
||||
if (DotHitDirection > 0.0f)
|
||||
{
|
||||
bIgnoreHit = true;
|
||||
// Ignore this CameraBlockingVolume on the remaining sweeps.
|
||||
SphereParams.AddIgnoredActor(HitActor);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
DebugActorsHitDuringCameraPenetration.AddUnique(TObjectPtr<const AActor>(HitActor));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (!bIgnoreHit)
|
||||
{
|
||||
CollidingFeelers[RayIdx] = true;
|
||||
++NbrHits;
|
||||
|
||||
float const Weight = Cast<APawn>(Hit.GetActor()) ? Feeler.PawnWeight : Feeler.WorldWeight;
|
||||
float NewBlockPct = Hit.Time;
|
||||
NewBlockPct += (1.f - NewBlockPct) * (1.f - Weight);
|
||||
|
||||
// Recompute blocked pct taking into account pushout distance.
|
||||
NewBlockPct = (Hit.Location - SafeLoc).Size() / (RayTarget - SafeLoc).Size();
|
||||
DistBlockedPctThisFrame = FMath::Min(NewBlockPct, DistBlockedPctThisFrame);
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
DebugActorsHitDuringCameraPenetration.AddUnique(TObjectPtr<const AActor>(HitActor));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (RayIdx == 0)
|
||||
{
|
||||
// Don't interpolate toward this one, snap to it. THIS ASSUMES RAY 0 IS THE CENTER/MAIN RAY.
|
||||
HardBlockedPct = DistBlockedPctThisFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
SoftBlockedPct = DistBlockedPctThisFrame;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (NbrHits > 0)
|
||||
{
|
||||
if (bDebug && GEngine != nullptr)
|
||||
{
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 6
|
||||
FString const& DebugText = FString::Printf(TEXT("UGC_CameraCollisionModifier: %d feeler%hs colliding."), NbrHits, NbrHits > 1 ? "s" : "");
|
||||
#else
|
||||
FString const& DebugText = FString::Printf(TEXT("UGC_CameraCollisionModifier: %d feeler%s colliding."), NbrHits, NbrHits > 1 ? "s" : "");
|
||||
#endif
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, CollisionHelpers::DebugColor, DebugText);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (OutDistBlockedPct < DistBlockedPctThisFrame)
|
||||
{
|
||||
// interpolate smoothly out
|
||||
if (CollisionSettings.PenetrationBlendOutTime > DeltaTime)
|
||||
{
|
||||
OutDistBlockedPct = OutDistBlockedPct + DeltaTime / CollisionSettings.PenetrationBlendOutTime * (DistBlockedPctThisFrame - OutDistBlockedPct);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutDistBlockedPct = DistBlockedPctThisFrame;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OutDistBlockedPct > HardBlockedPct)
|
||||
{
|
||||
OutDistBlockedPct = HardBlockedPct;
|
||||
}
|
||||
else if (OutDistBlockedPct > SoftBlockedPct)
|
||||
{
|
||||
// interpolate smoothly in
|
||||
if (CollisionSettings.PenetrationBlendInTime > DeltaTime)
|
||||
{
|
||||
OutDistBlockedPct = OutDistBlockedPct - DeltaTime / CollisionSettings.PenetrationBlendInTime * (OutDistBlockedPct - SoftBlockedPct);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutDistBlockedPct = SoftBlockedPct;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutDistBlockedPct = FMath::Clamp<float>(OutDistBlockedPct, 0.f, 1.f);
|
||||
if (OutDistBlockedPct < (1.f - ZERO_ANIMWEIGHT_THRESH))
|
||||
{
|
||||
OutCameraLoc = SafeLoc + (CameraLoc - SafeLoc) * OutDistBlockedPct;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
|
||||
#include "Camera/Modifiers/UGC_CameraDitheringModifier.h"
|
||||
|
||||
#include "CollisionQueryParams.h"
|
||||
#include "CollisionShape.h"
|
||||
#include "Camera/PlayerCameraManager.h"
|
||||
#include "Camera/UGC_PlayerCameraManager.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4
|
||||
#include "Engine/OverlapResult.h"
|
||||
#endif
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "Components/PrimitiveComponent.h"
|
||||
#include "Components/MeshComponent.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Materials/MaterialLayersFunctions.h"
|
||||
#include "Materials/MaterialParameterCollection.h"
|
||||
#include "Materials/MaterialParameterCollectionInstance.h"
|
||||
#include "Misc/Guid.h"
|
||||
|
||||
namespace DitherHelpers
|
||||
{
|
||||
bool DoAnyComponentsOverlap(AActor* inActor, ECollisionChannel overlapChannel)
|
||||
{
|
||||
if (inActor)
|
||||
{
|
||||
for (UActorComponent* ActorComponent : inActor->GetComponents())
|
||||
{
|
||||
UPrimitiveComponent* Primitive = Cast<UPrimitiveComponent>(ActorComponent);
|
||||
if (Primitive && Primitive->IsCollisionEnabled())
|
||||
{
|
||||
if (Primitive->GetCollisionResponseToChannel(overlapChannel) == ECollisionResponse::ECR_Overlap)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 FindInactiveDitherState(TArray<FDitheredActorState>& DitherStates)
|
||||
{
|
||||
for (int32 Index = 0; Index < DitherStates.Num(); ++Index)
|
||||
{
|
||||
const FDitheredActorState& DitherState(DitherStates[Index]);
|
||||
if (!DitherState.IsValid())
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return DitherStates.Emplace();
|
||||
}
|
||||
|
||||
bool DitherStatesContain(TArray<FDitheredActorState>& DitherStates, AActor* InActor)
|
||||
{
|
||||
if (InActor)
|
||||
{
|
||||
for (auto const& State : DitherStates)
|
||||
{
|
||||
if (State.Actor == InActor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FVector GetTraceSafeLocation(AActor* TargetActor, USpringArmComponent* SpringArm, FMinimalViewInfo const& InPOV)
|
||||
{
|
||||
FVector SafeLocation = TargetActor ? TargetActor->GetActorLocation() : FVector::Zero();
|
||||
if (TargetActor)
|
||||
{
|
||||
// Try to get spring arm component
|
||||
if (SpringArm)
|
||||
{
|
||||
SafeLocation = SpringArm->GetComponentLocation() + SpringArm->TargetOffset;
|
||||
}
|
||||
else if (UPrimitiveComponent const* PPActorRootComponent = Cast<UPrimitiveComponent>(TargetActor->GetRootComponent()))
|
||||
{
|
||||
// Attempt at picking SafeLocation automatically, so we reduce camera translation when aiming.
|
||||
// Our camera is our reticle, so we want to preserve our aim and keep that as steady and smooth as possible.
|
||||
// Pick closest point on capsule to our aim line.
|
||||
FVector ClosestPointOnLineToCapsuleCenter;
|
||||
FMath::PointDistToLine(SafeLocation, InPOV.Rotation.Vector(), InPOV.Location, ClosestPointOnLineToCapsuleCenter);
|
||||
|
||||
// Adjust Safe distance height to be same as aim line, but within capsule.
|
||||
float const PushInDistance = 0.f;
|
||||
float const MaxHalfHeight = TargetActor->GetSimpleCollisionHalfHeight() - PushInDistance;
|
||||
SafeLocation.Z = FMath::Clamp(ClosestPointOnLineToCapsuleCenter.Z, SafeLocation.Z - MaxHalfHeight, SafeLocation.Z + MaxHalfHeight);
|
||||
|
||||
float DistanceSqr = 0.f;
|
||||
PPActorRootComponent->GetSquaredDistanceToCollision(ClosestPointOnLineToCapsuleCenter, DistanceSqr, SafeLocation);
|
||||
// Push back inside capsule to avoid initial penetration when doing line checks.
|
||||
SafeLocation += (SafeLocation - ClosestPointOnLineToCapsuleCenter).GetSafeNormal() * PushInDistance;
|
||||
}
|
||||
}
|
||||
return SafeLocation;
|
||||
}
|
||||
}
|
||||
|
||||
void FDitheredActorState::StartDithering(AActor* InActor, EDitherType InDitherType)
|
||||
{
|
||||
if (InActor)
|
||||
{
|
||||
Actor = InActor;
|
||||
CollisionTime = 0.f;
|
||||
bIsDitheringIn = false;
|
||||
bIsDitheringOut = false;
|
||||
DitherType = InDitherType;
|
||||
}
|
||||
}
|
||||
|
||||
void FDitheredActorState::Invalidate()
|
||||
{
|
||||
Actor = nullptr;
|
||||
CurrentOpacity = 1.f;
|
||||
CollisionTime = 0.f;
|
||||
bIsDitheringIn = false;
|
||||
bIsDitheringOut = false;
|
||||
DitherType = EDitherType::None;
|
||||
}
|
||||
|
||||
void FDitheredActorState::ComputeOpacity(float DeltaTime, float DitherInSpeed, float DitherOutSpeed, float DitherMin)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool const bCanDither = !bIsDitheringOut && bIsDitheringIn;
|
||||
CurrentOpacity = FMath::FInterpTo(CurrentOpacity, bCanDither ? DitherMin : 1.f, DeltaTime, bIsDitheringOut ? DitherOutSpeed : DitherInSpeed);
|
||||
}
|
||||
|
||||
UUGC_CameraDitheringModifier::UUGC_CameraDitheringModifier()
|
||||
{
|
||||
Priority = 255; // You want this modifier to be the last one to be executed so that it always knows the final camera position.
|
||||
}
|
||||
|
||||
void UUGC_CameraDitheringModifier::ResetDitheredActors_Implementation()
|
||||
{
|
||||
for (auto& DitherState : DitheredActorStates)
|
||||
{
|
||||
DitherState.bIsDitheringIn = false;
|
||||
DitherState.bIsDitheringOut = true;
|
||||
DitherState.CurrentOpacity = 1.f;
|
||||
static float constexpr DeltaTime = 1 / 60.f;
|
||||
ApplyDithering(DeltaTime, DitherState);
|
||||
DitherState.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
bool UUGC_CameraDitheringModifier::ModifyCamera(float DeltaTime, FMinimalViewInfo& InOutPOV)
|
||||
{
|
||||
Super::ModifyCamera(DeltaTime, InOutPOV);
|
||||
if (CameraOwner && CameraOwner->PCOwner && CameraOwner->PCOwner->GetPawn())
|
||||
{
|
||||
FCollisionQueryParams QueryParams(TEXT("CameraDithering"));
|
||||
|
||||
// Get All overlapped actors and LOS-blocking actors
|
||||
TArray<AActor*> ActorsToDither;
|
||||
|
||||
if (DitheringSettings.bDitherOverlaps)
|
||||
{
|
||||
if (!DitheringSettings.bDitherOwner)
|
||||
{
|
||||
QueryParams.AddIgnoredActor(CameraOwner->PCOwner->GetPawn());
|
||||
if (AActor* PendingTarget = CameraOwner->PendingViewTarget.Target)
|
||||
{
|
||||
QueryParams.AddIgnoredActor(PendingTarget);
|
||||
}
|
||||
}
|
||||
|
||||
TArray<FOverlapResult> OutOverlaps;
|
||||
GetWorld()->OverlapMultiByChannel(OutOverlaps, InOutPOV.Location, FQuat::Identity, DitheringSettings.DitherOverlapChannel, FCollisionShape::MakeSphere(DitheringSettings.SphereCollisionRadius), QueryParams);
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (GetWorld()->DebugDrawTraceTag == TEXT("CameraDithering"))
|
||||
{
|
||||
DrawDebugSphere(GetWorld(), InOutPOV.Location, DitheringSettings.SphereCollisionRadius, 32, FColor::Red);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (auto const& Overlap : OutOverlaps)
|
||||
{
|
||||
// Ignore null actors
|
||||
if (!Overlap.GetActor())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TArray<AActor*> OverlapActors;
|
||||
OverlapActors.Add(Overlap.GetActor());
|
||||
|
||||
if (DitheringSettings.bDitherAttachedComponents)
|
||||
{
|
||||
Overlap.GetActor()->GetAttachedActors(OverlapActors, false, true);
|
||||
}
|
||||
|
||||
for (auto& Actor : OverlapActors)
|
||||
{
|
||||
// Ignore null actors or actors with IgnoreDitheringTag
|
||||
if (!Actor || Actor->ActorHasTag(DitheringSettings.IgnoreDitheringTag))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool const bIsCollisionResponseCorrect = DitherHelpers::DoAnyComponentsOverlap(Actor, DitheringSettings.DitherOverlapChannel);
|
||||
if (!bIsCollisionResponseCorrect)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ActorsToDither.Add(Actor);
|
||||
|
||||
// Start dithering actor if not done already
|
||||
if (!DitherHelpers::DitherStatesContain(DitheredActorStates, Actor))
|
||||
{
|
||||
QueryParams.AddIgnoredActor(Actor);
|
||||
int32 Index = DitherHelpers::FindInactiveDitherState(DitheredActorStates);
|
||||
DitheredActorStates[Index].StartDithering(Actor, EDitherType::OverlappingCamera);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DitheringSettings.bDitherLineOfSight)
|
||||
{
|
||||
QueryParams.AddIgnoredActor(CameraOwner->PCOwner->GetPawn());
|
||||
if (AActor* PendingTarget = CameraOwner->PendingViewTarget.Target)
|
||||
{
|
||||
QueryParams.AddIgnoredActor(PendingTarget);
|
||||
}
|
||||
|
||||
TArray<FOverlapResult> OutOverlaps;
|
||||
FVector const EndLocation = DitherHelpers::GetTraceSafeLocation(GetViewTarget(), SpringArm, InOutPOV);
|
||||
FVector const ToTarget = (EndLocation - InOutPOV.Location).GetSafeNormal();
|
||||
FVector const StartLocation = InOutPOV.Location + ToTarget * DitheringSettings.SphereCollisionRadius;
|
||||
FVector const Delta = EndLocation - StartLocation;
|
||||
FQuat const Rotation = FRotationMatrix::MakeFromX(Delta.GetSafeNormal()).ToQuat();
|
||||
FVector const BoxOrigin = Delta * 0.5f + StartLocation;
|
||||
|
||||
FVector const BoxExtent = FVector(Delta.Size() * 0.495f, DitheringSettings.LOSProbeSize, DitheringSettings.LOSProbeSize);
|
||||
FCollisionShape const Box = FCollisionShape::MakeBox(BoxExtent);
|
||||
|
||||
GetWorld()->OverlapMultiByChannel(OutOverlaps, BoxOrigin, Rotation, DitheringSettings.DitherLOSChannel, Box, QueryParams);
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (GetWorld()->DebugDrawTraceTag == TEXT("CameraDithering"))
|
||||
{
|
||||
DrawDebugBox(GetWorld(), BoxOrigin, BoxExtent, Rotation, FColor::Red);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (auto const& Overlap : OutOverlaps)
|
||||
{
|
||||
// Ignore null actors
|
||||
if (!Overlap.GetActor())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TArray<AActor*> OverlapActors;
|
||||
OverlapActors.Add(Overlap.GetActor());
|
||||
|
||||
if (DitheringSettings.bDitherAttachedComponents)
|
||||
{
|
||||
Overlap.GetActor()->GetAttachedActors(OverlapActors, false, true);
|
||||
}
|
||||
|
||||
for (auto& Actor : OverlapActors)
|
||||
{
|
||||
// Ignore null actors or actors with IgnoreDitheringTag
|
||||
if (!Actor || Actor->ActorHasTag(DitheringSettings.IgnoreDitheringTag))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ActorsToDither.Add(Actor);
|
||||
|
||||
// Start dithering actor if not done already
|
||||
if (!DitherHelpers::DitherStatesContain(DitheredActorStates, Actor))
|
||||
{
|
||||
int32 Index = DitherHelpers::FindInactiveDitherState(DitheredActorStates);
|
||||
DitheredActorStates[Index].StartDithering(Actor, EDitherType::BlockingLOS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update dithered state of actors
|
||||
for (auto& DitherState : DitheredActorStates)
|
||||
{
|
||||
if (!DitherState.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the opacity
|
||||
DitherState.ComputeOpacity(DeltaTime, DitheringSettings.DitherInSpeed, DitheringSettings.DitherOutSpeed, DitheringSettings.MaterialDitherMinimum);
|
||||
|
||||
ApplyDithering(DeltaTime, DitherState);
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
UGCDebugDithering(DitherState, DeltaTime, DitheringSettings.MaterialDitherMinimum);
|
||||
#endif
|
||||
|
||||
// If the dithered actor isn't overlapped
|
||||
if (!ActorsToDither.Contains(DitherState.Actor))
|
||||
{
|
||||
// Stop and do not dither in
|
||||
DitherState.bIsDitheringIn = false;
|
||||
DitherState.CollisionTime = 0.f;
|
||||
DitherState.bIsDitheringOut = true;
|
||||
|
||||
// If finished dithering out
|
||||
if (DitherState.CurrentOpacity >= 1.f)
|
||||
{
|
||||
DitherState.Invalidate();
|
||||
}
|
||||
}
|
||||
// Otherwise if the dithered actor is still overlapped
|
||||
else
|
||||
{
|
||||
// Do not dither out
|
||||
DitherState.bIsDitheringOut = false;
|
||||
if (DitherState.DitherType == EDitherType::OverlappingCamera || (DitherState.DitherType == EDitherType::BlockingLOS && DitherState.CollisionTime >= DitheringSettings.CollisionTimeThreshold))
|
||||
{
|
||||
DitherState.bIsDitheringIn = true;
|
||||
}
|
||||
DitherState.CollisionTime += DeltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UUGC_CameraDitheringModifier::ApplyDithering(float DeltaTime, FDitheredActorState& DitherState)
|
||||
{
|
||||
if (!DitherState.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (DitheringSettings.bUpdateMaterialPlayerPosition && CameraOwner && CameraOwner->PCOwner && CameraOwner->PCOwner->GetPawn() && DitheringMPC && GetWorld())
|
||||
{
|
||||
if (!DitheringMPCInstance)
|
||||
{
|
||||
DitheringMPCInstance = GetWorld()->GetParameterCollectionInstance(DitheringMPC.Get());
|
||||
}
|
||||
if (DitheringMPCInstance)
|
||||
{
|
||||
DitheringMPCInstance->SetVectorParameterValue(DitheringSettings.MaterialPlayerPositionParameterName, CameraOwner->PCOwner->GetPawn()->GetActorLocation());
|
||||
}
|
||||
}
|
||||
|
||||
// Get all mesh components
|
||||
TArray<UMeshComponent*> Meshes;
|
||||
DitherState.Actor->GetComponents(Meshes, DitheringSettings.bDitherChildActors);
|
||||
|
||||
for (auto& Mesh : Meshes)
|
||||
{
|
||||
if (!Mesh)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all materials
|
||||
const TArray<UMaterialInterface*> Materials = Mesh->GetMaterials();
|
||||
for (auto& Material : Materials)
|
||||
{
|
||||
if (!Material)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all float parameters
|
||||
TArray<FMaterialParameterInfo> ScalarParams;
|
||||
TArray<FGuid> ScalarParamIds;
|
||||
Material->GetAllScalarParameterInfo(ScalarParams, ScalarParamIds);
|
||||
|
||||
// Set the params that have the same name as MaterialOpacityParameterName
|
||||
for (auto& ScalarParam : ScalarParams)
|
||||
{
|
||||
if (ScalarParam.Name != DitheringSettings.MaterialOpacityParameterName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Mesh->SetScalarParameterValueOnMaterials(DitheringSettings.MaterialOpacityParameterName, DitherState.CurrentOpacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
void UUGC_CameraDitheringModifier::UGCDebugDithering(FDitheredActorState& DitherState, float DeltaTime, float DitherMin)
|
||||
{
|
||||
if (bDebug && DitherState.IsValid() && GEngine)
|
||||
{
|
||||
const FString EaseInString = DitherState.bIsDitheringIn ? FString::Printf(TEXT(" - Dithering In")) : TEXT("");
|
||||
const FString EaseOutString = DitherState.bIsDitheringOut ? FString::Printf(TEXT(" - Dithering Out")) : TEXT("");
|
||||
const FString DebugText = FString::Printf(TEXT("UGC_CameraDitheringModifier: %s - Type: %s - Opacity: %f - Elapsed: %f%s%s")
|
||||
, *GetNameSafe(DitherState.Actor), *UEnum::GetDisplayValueAsText(DitherState.DitherType).ToString(), DitherState.CurrentOpacity, DitherState.CollisionTime, *EaseInString, *EaseOutString);
|
||||
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor::Silver, DebugText);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,250 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Camera/Modifiers/UGC_CameraModifier.h"
|
||||
|
||||
#include "AuroraDevs_UGC.h"
|
||||
#include "Camera/Modifiers/UGC_CameraAnimationModifier.h"
|
||||
#include "Camera/PlayerCameraManager.h"
|
||||
#include "Camera/UGC_PlayerCameraManager.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
|
||||
void UUGC_CameraModifier::EnableModifier()
|
||||
{
|
||||
Super::EnableModifier();
|
||||
OnModifierEnabled(CameraOwner->GetCameraCacheView());
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::DisableModifier(bool bImmediate)
|
||||
{
|
||||
Super::DisableModifier(bImmediate);
|
||||
|
||||
if (bImmediate && bDisabled && !bPendingDisable)
|
||||
{
|
||||
Alpha = 0.f;
|
||||
}
|
||||
OnModifierDisabled(CameraOwner->GetCameraCacheView(), bImmediate);
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::OnModifierEnabled_Implementation(FMinimalViewInfo const& LastPOV)
|
||||
{
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::OnModifierDisabled_Implementation(FMinimalViewInfo const& LastPOV, bool bImmediate)
|
||||
{
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::ProcessBoomLengthAndFOV_Implementation(float DeltaTime, float InFOV, float InArmLength, FVector ViewLocation, FRotator ViewRotation, float& OutFOV, float& OutArmLength)
|
||||
{
|
||||
OutFOV = InFOV;
|
||||
OutArmLength = InArmLength;
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::ProcessBoomOffsets_Implementation(float DeltaTime, FVector InSocketOffset, FVector InTargetOffset, FVector ViewLocation, FRotator ViewRotation, FVector& OutSocketOffset, FVector& OutTargetOffset)
|
||||
{
|
||||
OutSocketOffset = InSocketOffset;
|
||||
OutTargetOffset = InTargetOffset;
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::OnAnyLevelSequenceStarted_Implementation()
|
||||
{
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::OnAnyLevelSequenceEnded_Implementation()
|
||||
{
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::OnSetViewTarget_Implementation(bool bImmediate, bool bNewTargetIsOwner)
|
||||
{
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::PostUpdate_Implementation(float DeltaTime, FVector ViewLocation, FRotator ViewRotation)
|
||||
{
|
||||
}
|
||||
|
||||
bool UUGC_CameraModifier::IsDebugEnabled() const
|
||||
{
|
||||
return bDebug;
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::ToggleDebug(bool const bEnabled)
|
||||
{
|
||||
bDebug = bEnabled;
|
||||
}
|
||||
|
||||
bool UUGC_CameraModifier::ModifyCamera(float DeltaTime, FMinimalViewInfo& InOutPOV)
|
||||
{
|
||||
return Super::ModifyCamera(DeltaTime, InOutPOV);
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::ModifyCamera(float DeltaTime, FVector ViewLocation, FRotator ViewRotation, float FOV, FVector& OutViewLocation, FRotator& OutViewRotation, float& OutFOV)
|
||||
{
|
||||
Super::ModifyCamera(DeltaTime, ViewLocation, ViewRotation, FOV, OutViewLocation, OutViewRotation, OutFOV);
|
||||
|
||||
OutViewLocation = ViewLocation;
|
||||
OutViewRotation = ViewRotation;
|
||||
OutFOV = FOV;
|
||||
|
||||
UpdateOwnerReferences();
|
||||
|
||||
if (!UGCCameraManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bPlayDuringCameraAnimations)
|
||||
{
|
||||
if (UGCCameraManager->IsPlayingAnyCameraAnimation())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (OwnerController && OwnerPawn)
|
||||
{
|
||||
UpdateInternalVariables(DeltaTime);
|
||||
if (SpringArm)
|
||||
{
|
||||
ProcessBoomLengthAndFOV(DeltaTime, FOV, CurrentArmLength, ViewLocation, ViewRotation, OutFOV, SpringArm->TargetArmLength);
|
||||
ProcessBoomOffsets(DeltaTime, CurrentSocketOffset, CurrentTargetOffset, ViewLocation, ViewRotation, SpringArm->SocketOffset, SpringArm->TargetOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
UGC_LOG_ONCE(InvalidSpringArm, Error, TEXT("%s uses UGC but doesn't have a valid Spring Arm Component."), *GetNameSafe(OwnerPawn));
|
||||
}
|
||||
PostUpdate(DeltaTime, ViewLocation, ViewRotation);
|
||||
}
|
||||
}
|
||||
|
||||
bool UUGC_CameraModifier::ProcessViewRotation(AActor* ViewTarget, float DeltaTime, FRotator& OutViewRotation, FRotator& OutDeltaRot)
|
||||
{
|
||||
bool bResult = Super::ProcessViewRotation(ViewTarget, DeltaTime, OutViewRotation, OutDeltaRot);
|
||||
|
||||
if (!bPlayDuringCameraAnimations)
|
||||
{
|
||||
if (UGCCameraManager && UGCCameraManager->IsPlayingAnyCameraAnimation())
|
||||
{
|
||||
return bResult;
|
||||
}
|
||||
}
|
||||
|
||||
if (ViewTarget && CameraOwner && CameraOwner->GetOwningPlayerController())
|
||||
{
|
||||
// TO DO #GravityCompatibility
|
||||
FVector const CameraLocation = CameraOwner->GetCameraLocation();
|
||||
FRotator const ControlRotation = CameraOwner->GetOwningPlayerController()->GetControlRotation();
|
||||
FRotator const OwnerRotation = ViewTarget ? ViewTarget->GetActorRotation() : FRotator::ZeroRotator;
|
||||
FRotator InLocalControlRotation = ControlRotation - OwnerRotation;
|
||||
InLocalControlRotation.Normalize();
|
||||
bResult = ProcessControlRotation(ViewTarget, DeltaTime, CameraLocation, OutViewRotation, InLocalControlRotation, OutDeltaRot, OutDeltaRot);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::UpdateOwnerReferences()
|
||||
{
|
||||
UGCCameraManager = Cast<AUGC_PlayerCameraManager>(CameraOwner);
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
if (!UGCCameraManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OwnerController = UGCCameraManager->GetOwningPlayerController();
|
||||
OwnerCharacter = UGCCameraManager->OwnerCharacter;
|
||||
OwnerPawn = UGCCameraManager->OwnerPawn;
|
||||
if (OwnerPawn && OwnerPawn->IsLocallyControlled())
|
||||
{
|
||||
SpringArm = UGCCameraManager->CameraArm;
|
||||
MovementComponent = UGCCameraManager->MovementComponent;
|
||||
}
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::UpdateInternalVariables(float DeltaTime)
|
||||
{
|
||||
if (!UGCCameraManager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bHasMovementInput = UGCCameraManager->bHasMovementInput;
|
||||
PreviousMovementInput = MovementInput;
|
||||
MovementInput = UGCCameraManager->MovementInput;
|
||||
TimeSinceMovementInput = UGCCameraManager->TimeSinceMovementInput;
|
||||
bHasRotationInput = UGCCameraManager->bHasRotationInput;
|
||||
RotationInput = UGCCameraManager->RotationInput;
|
||||
TimeSinceRotationInput = UGCCameraManager->TimeSinceRotationInput;
|
||||
|
||||
if (SpringArm)
|
||||
{
|
||||
CurrentSocketOffset = SpringArm->SocketOffset;
|
||||
CurrentTargetOffset = SpringArm->TargetOffset;
|
||||
CurrentArmLength = SpringArm->TargetArmLength;
|
||||
}
|
||||
}
|
||||
|
||||
bool UUGC_CameraModifier::ProcessTurnRate_Implementation(float DeltaTime, FRotator InLocalControlRotation, float InPitchTurnRate, float InYawTurnRate, float& OutPitchTurnRate, float& OutYawTurnRate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UUGC_CameraModifier::ProcessControlRotation_Implementation(AActor* ViewTarget, float DeltaTime, FVector InViewLocation, FRotator InViewRotation, FRotator InLocalControlRotation, FRotator InDeltaRot, FRotator& OutDeltaRot)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FVector UUGC_CameraModifier::GetOwnerVelocity() const
|
||||
{
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
return UGCCameraManager->GetOwnerVelocity();
|
||||
}
|
||||
|
||||
bool UUGC_CameraModifier::IsOwnerFalling() const
|
||||
{
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
return UGCCameraManager->IsOwnerFalling();
|
||||
}
|
||||
|
||||
bool UUGC_CameraModifier::IsOwnerStrafing() const
|
||||
{
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
return UGCCameraManager->IsOwnerStrafing();
|
||||
}
|
||||
|
||||
bool UUGC_CameraModifier::IsOwnerMovingOnGround() const
|
||||
{
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
return UGCCameraManager->IsOwnerMovingOnGround();
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::ComputeOwnerFloorDistance(float SweepDistance, float CapsuleRadius, bool& bOutFloorExists, float& OutFloorDistance) const
|
||||
{
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
return UGCCameraManager->ComputeOwnerFloorDist(SweepDistance, CapsuleRadius, bOutFloorExists, OutFloorDistance);
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::ComputeOwnerFloorNormal(float SweepDistance, float CapsuleRadius, bool& bOutFloorExists, FVector& OutFloorNormal) const
|
||||
{
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
return UGCCameraManager->ComputeOwnerFloorNormal(SweepDistance, CapsuleRadius, bOutFloorExists, OutFloorNormal);
|
||||
}
|
||||
|
||||
void UUGC_CameraModifier::ComputeOwnerSlopeAngle(float& OutSlopePitchDegrees, float& OutSlopeRollDegrees)
|
||||
{
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
return UGCCameraManager->ComputeOwnerSlopeAngle(OutSlopePitchDegrees, OutSlopeRollDegrees);
|
||||
}
|
||||
|
||||
float UUGC_CameraModifier::ComputeOwnerLookAndMovementDot()
|
||||
{
|
||||
ensureMsgf(UGCCameraManager, TEXT("Please use UGC Camera Modifiers only with a player camera manager inheriting from UGC_PlayerCameraManager."));
|
||||
return UGCCameraManager->ComputeOwnerLookAndMovementDot();
|
||||
}
|
||||
|
||||
void UUGC_CameraAddOnModifier::SetSettings_Implementation(class UUGC_CameraAddOnModifierSettings* InSettings)
|
||||
{
|
||||
Settings = InSettings;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Camera/Modifiers/UGC_CameraPropertiesAnimNotifyModifiers.h"
|
||||
|
||||
#include "Curves/CurveFloat.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
UUGC_FOVAnimNotifyCameraModifier::UUGC_FOVAnimNotifyCameraModifier()
|
||||
: Super()
|
||||
{
|
||||
bPlayDuringCameraAnimations = true;
|
||||
RequestHelper.Init(TEXT("FOV"), [this]() -> bool { return bDebug; });
|
||||
}
|
||||
|
||||
void UUGC_FOVAnimNotifyCameraModifier::ProcessBoomLengthAndFOV_Implementation(float DeltaTime, float InFOV, float InArmLength, FVector ViewLocation, FRotator ViewRotation, float& OutFOV, float& OutArmLength)
|
||||
{
|
||||
Super::ProcessBoomLengthAndFOV_Implementation(DeltaTime, InFOV, InArmLength, ViewLocation, ViewRotation, OutFOV, OutArmLength);
|
||||
RequestHelper.ProcessValue(DeltaTime, InFOV, ViewLocation, ViewRotation, OutFOV);
|
||||
}
|
||||
|
||||
void UUGC_FOVAnimNotifyCameraModifier::OnModifierDisabled_Implementation(FMinimalViewInfo const& LastPOV, bool bWasImmediate)
|
||||
{
|
||||
Super::OnModifierDisabled_Implementation(LastPOV, bWasImmediate);
|
||||
RequestHelper.OnModifierDisabled(LastPOV, bWasImmediate);
|
||||
}
|
||||
|
||||
void UUGC_FOVAnimNotifyCameraModifier::PushFOVAnimNotifyRequest(FGuid RequestId, float TargetFOV, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve)
|
||||
{
|
||||
RequestHelper.PushValueRequest(RequestId, TargetFOV, TotalDuration, BlendInDuration, BlendInCurve, BlendOutDuration, BlendOutCurve);
|
||||
}
|
||||
|
||||
void UUGC_FOVAnimNotifyCameraModifier::PopFOVAnimNotifyRequest(FGuid RequestId)
|
||||
{
|
||||
RequestHelper.PopValueRequest(RequestId);
|
||||
}
|
||||
|
||||
UUGC_ArmOffsetAnimNotifyCameraModifier::UUGC_ArmOffsetAnimNotifyCameraModifier()
|
||||
: Super()
|
||||
{
|
||||
bPlayDuringCameraAnimations = false;
|
||||
SocketOffsetRequestHelper.Init(TEXT("SocketOffset"), [this]() -> bool { return bDebug; });
|
||||
TargetOffsetRequestHelper.Init(TEXT("TargetOffset"), [this]() -> bool { return bDebug; });
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
SocketOffsetRequestHelper.PropertyColor = FColor(252, 195, 53, 255);
|
||||
TargetOffsetRequestHelper.PropertyColor = FColor(255, 212, 105, 255);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UUGC_ArmOffsetAnimNotifyCameraModifier::ProcessBoomOffsets_Implementation(float DeltaTime, FVector InSocketOffset, FVector InTargetOffset, FVector ViewLocation, FRotator ViewRotation, FVector& OutSocketOffset, FVector& OutTargetOffset)
|
||||
{
|
||||
Super::ProcessBoomOffsets_Implementation(DeltaTime, InSocketOffset, InTargetOffset, ViewLocation, ViewRotation, OutSocketOffset, OutTargetOffset);
|
||||
SocketOffsetRequestHelper.ProcessValue(DeltaTime, InSocketOffset, ViewLocation, ViewRotation, OutSocketOffset);
|
||||
TargetOffsetRequestHelper.ProcessValue(DeltaTime, InTargetOffset, ViewLocation, ViewRotation, OutTargetOffset);
|
||||
}
|
||||
|
||||
void UUGC_ArmOffsetAnimNotifyCameraModifier::OnModifierDisabled_Implementation(FMinimalViewInfo const& LastPOV, bool bWasImmediate)
|
||||
{
|
||||
Super::OnModifierDisabled_Implementation(LastPOV, bWasImmediate);
|
||||
SocketOffsetRequestHelper.OnModifierDisabled(LastPOV, bWasImmediate);
|
||||
TargetOffsetRequestHelper.OnModifierDisabled(LastPOV, bWasImmediate);
|
||||
}
|
||||
|
||||
void UUGC_ArmOffsetAnimNotifyCameraModifier::PushArmSocketOffsetAnimNotifyRequest(FGuid RequestId, FVector TargetOffset, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve)
|
||||
{
|
||||
SocketOffsetRequestHelper.PushValueRequest(RequestId, TargetOffset, TotalDuration, BlendInDuration, BlendInCurve, BlendOutDuration, BlendOutCurve);
|
||||
}
|
||||
|
||||
void UUGC_ArmOffsetAnimNotifyCameraModifier::PopArmSocketOffsetAnimNotifyRequest(FGuid RequestId)
|
||||
{
|
||||
SocketOffsetRequestHelper.PopValueRequest(RequestId);
|
||||
}
|
||||
|
||||
void UUGC_ArmOffsetAnimNotifyCameraModifier::PushArmTargetOffsetAnimNotifyRequest(FGuid RequestId, FVector TargetOffset, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve)
|
||||
{
|
||||
TargetOffsetRequestHelper.PushValueRequest(RequestId, TargetOffset, TotalDuration, BlendInDuration, BlendInCurve, BlendOutDuration, BlendOutCurve);
|
||||
}
|
||||
|
||||
void UUGC_ArmOffsetAnimNotifyCameraModifier::PopArmTargetOffsetAnimNotifyRequest(FGuid RequestId)
|
||||
{
|
||||
TargetOffsetRequestHelper.PopValueRequest(RequestId);
|
||||
}
|
||||
|
||||
UUGC_ArmLengthAnimNotifyCameraModifier::UUGC_ArmLengthAnimNotifyCameraModifier()
|
||||
: Super()
|
||||
{
|
||||
bPlayDuringCameraAnimations = false;
|
||||
RequestHelper.Init(TEXT("ArmLength"), [this]() -> bool { return bDebug; });
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
RequestHelper.PropertyColor = FColor(55, 219, 33, 255);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UUGC_ArmLengthAnimNotifyCameraModifier::ProcessBoomLengthAndFOV_Implementation(float DeltaTime, float InFOV, float InArmLength, FVector ViewLocation, FRotator ViewRotation, float& OutFOV, float& OutArmLength)
|
||||
{
|
||||
Super::ProcessBoomLengthAndFOV_Implementation(DeltaTime, InFOV, InArmLength, ViewLocation, ViewRotation, OutFOV, OutArmLength);
|
||||
RequestHelper.ProcessValue(DeltaTime, InArmLength, ViewLocation, ViewRotation, OutArmLength);
|
||||
}
|
||||
|
||||
void UUGC_ArmLengthAnimNotifyCameraModifier::OnModifierDisabled_Implementation(FMinimalViewInfo const& LastPOV, bool bWasImmediate)
|
||||
{
|
||||
Super::OnModifierDisabled_Implementation(LastPOV, bWasImmediate);
|
||||
RequestHelper.OnModifierDisabled(LastPOV, bWasImmediate);
|
||||
}
|
||||
|
||||
void UUGC_ArmLengthAnimNotifyCameraModifier::PushArmLengthAnimNotifyRequest(FGuid RequestId, float TargetLength, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve)
|
||||
{
|
||||
RequestHelper.PushValueRequest(RequestId, TargetLength, TotalDuration, BlendInDuration, BlendInCurve, BlendOutDuration, BlendOutCurve);
|
||||
}
|
||||
|
||||
void UUGC_ArmLengthAnimNotifyCameraModifier::PopArmLengthAnimNotifyRequest(FGuid RequestId)
|
||||
{
|
||||
RequestHelper.PopValueRequest(RequestId);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Camera/Modifiers/UGC_PlayCameraAnimCallbackProxy.h"
|
||||
#include "Camera/PlayerCameraManager.h"
|
||||
|
||||
namespace PlayCameraAnimCallbackProxyHelper
|
||||
{
|
||||
FCameraAnimationHandle const UGCInvalid(MAX_int16, 0);
|
||||
}
|
||||
|
||||
UUGC_PlayCameraAnimCallbackProxy::UUGC_PlayCameraAnimCallbackProxy(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, bInterruptedCalledBeforeBlendingOut(false)
|
||||
{
|
||||
}
|
||||
|
||||
FUGCCameraAnimationParams::operator FCameraAnimationParams() const
|
||||
{
|
||||
FCameraAnimationParams Params;
|
||||
Params.PlayRate = PlayRate;
|
||||
Params.EaseInDuration = EaseInDuration;
|
||||
Params.EaseOutDuration = EaseOutDuration;
|
||||
Params.EaseInType = EaseInType;
|
||||
Params.EaseOutType = EaseOutType;
|
||||
Params.bLoop = false;
|
||||
|
||||
return Params;
|
||||
}
|
||||
|
||||
UUGC_PlayCameraAnimCallbackProxy* UUGC_PlayCameraAnimCallbackProxy::CreateProxyObjectForPlayCameraAnim(APlayerCameraManager* InPlayerCameraManager, TSubclassOf<UUGC_CameraAnimationModifier> ModifierClass, UCameraAnimationSequence* CameraSequence, FUGCCameraAnimationParams Params, FCameraAnimationHandle& Handle, bool bInterruptOthers, bool bDoCollisionChecks)
|
||||
{
|
||||
UUGC_PlayCameraAnimCallbackProxy* Proxy = NewObject<UUGC_PlayCameraAnimCallbackProxy>();
|
||||
Proxy->SetFlags(RF_StrongRefOnFrame);
|
||||
Proxy->PlayCameraAnimation(InPlayerCameraManager, ModifierClass, CameraSequence, Params, Handle, bInterruptOthers, bDoCollisionChecks);
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
UUGC_PlayCameraAnimCallbackProxy* UUGC_PlayCameraAnimCallbackProxy::CreateProxyObjectForPlayCameraAnimForModifier(UUGC_CameraAnimationModifier* CameraAnimationModifier, UCameraAnimationSequence* CameraSequence, FUGCCameraAnimationParams Params, FCameraAnimationHandle& Handle, bool bInterruptOthers, bool bDoCollisionChecks)
|
||||
{
|
||||
UUGC_PlayCameraAnimCallbackProxy* Proxy = NewObject<UUGC_PlayCameraAnimCallbackProxy>();
|
||||
Proxy->SetFlags(RF_StrongRefOnFrame);
|
||||
Proxy->PlayCameraAnimation(CameraAnimationModifier, CameraSequence, Params, Handle, bInterruptOthers, bDoCollisionChecks);
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
void UUGC_PlayCameraAnimCallbackProxy::PlayCameraAnimation(UUGC_CameraAnimationModifier* CameraAnimModifier, UCameraAnimationSequence* CameraSequence, FUGCCameraAnimationParams Params, FCameraAnimationHandle& Handle, bool bInterruptOthers, bool bDoCollisionChecks)
|
||||
{
|
||||
Handle = PlayCameraAnimCallbackProxyHelper::UGCInvalid;
|
||||
bool bPlayedSuccessfully = false;
|
||||
|
||||
if (CameraAnimModifier)
|
||||
{
|
||||
Handle = CameraAnimModifier->PlaySingleCameraAnimation(CameraSequence, static_cast<FCameraAnimationParams>(Params), Params.ResetType, bInterruptOthers, bDoCollisionChecks);
|
||||
bPlayedSuccessfully = Handle.IsValid();
|
||||
|
||||
if (bPlayedSuccessfully)
|
||||
{
|
||||
CameraAnimationModifierPtr = CameraAnimModifier;
|
||||
|
||||
CameraAnimationEasingOutDelegate.BindUObject(this, &UUGC_PlayCameraAnimCallbackProxy::OnCameraAnimationEasingOut);
|
||||
CameraAnimationModifierPtr->CameraAnimation_SetEasingOutDelegate(CameraAnimationEasingOutDelegate, Handle);
|
||||
|
||||
CameraAnimationEndedDelegate.BindUObject(this, &UUGC_PlayCameraAnimCallbackProxy::OnCameraAnimationEnded);
|
||||
CameraAnimationModifierPtr->CameraAnimation_SetEndedDelegate(CameraAnimationEndedDelegate, Handle);
|
||||
}
|
||||
}
|
||||
|
||||
if (!bPlayedSuccessfully)
|
||||
{
|
||||
OnInterrupted.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void UUGC_PlayCameraAnimCallbackProxy::PlayCameraAnimation(APlayerCameraManager* InPlayerCameraManager, TSubclassOf<UUGC_CameraAnimationModifier> ModifierClass, UCameraAnimationSequence* CameraSequence, FUGCCameraAnimationParams Params, FCameraAnimationHandle& Handle, bool bInterruptOthers, bool bDoCollisionChecks)
|
||||
{
|
||||
if (InPlayerCameraManager)
|
||||
{
|
||||
if (UUGC_CameraAnimationModifier* CameraAnimModifier = Cast<UUGC_CameraAnimationModifier>(InPlayerCameraManager->FindCameraModifierByClass(ModifierClass)))
|
||||
{
|
||||
PlayCameraAnimation(CameraAnimModifier, CameraSequence, Params, Handle, bInterruptOthers, bDoCollisionChecks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UUGC_PlayCameraAnimCallbackProxy::OnCameraAnimationEasingOut(UCameraAnimationSequence* CameraAnimation)
|
||||
{
|
||||
OnEaseOut.Broadcast();
|
||||
}
|
||||
|
||||
void UUGC_PlayCameraAnimCallbackProxy::OnCameraAnimationEnded(UCameraAnimationSequence* CameraAnimation, bool bInterrupted)
|
||||
{
|
||||
if (!bInterrupted)
|
||||
{
|
||||
OnCompleted.Broadcast();
|
||||
}
|
||||
else if (!bInterruptedCalledBeforeBlendingOut)
|
||||
{
|
||||
OnInterrupted.Broadcast();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,791 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Camera/UGC_PlayerCameraManager.h"
|
||||
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "Camera/Data/UGC_CameraData.h"
|
||||
#include "Camera/Modifiers/UGC_CameraAnimationModifier.h"
|
||||
#include "Camera/Modifiers/UGC_CameraModifier.h"
|
||||
#include "Camera/Modifiers/UGC_PlayCameraAnimCallbackProxy.h"
|
||||
#include "CameraAnimationSequence.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "Engine/Canvas.h"
|
||||
#include "Engine/World.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Kismet/KismetMathLibrary.h"
|
||||
#include "LevelSequenceActor.h"
|
||||
#include "LevelSequencePlayer.h"
|
||||
#include "Pawn/UGC_PawnInterface.h"
|
||||
|
||||
TAutoConsoleVariable<bool> GShowCameraManagerModifiersCVar(
|
||||
TEXT("ShowCameraModifiersDebug"),
|
||||
false,
|
||||
TEXT("Show information about the currently active camera modifiers and their priorities."));
|
||||
|
||||
namespace PlayerCameraHelpers
|
||||
{
|
||||
UCameraComponent* GetSpringArmChildCamera(USpringArmComponent* SpringArm)
|
||||
{
|
||||
UCameraComponent* Camera = nullptr;
|
||||
if (SpringArm)
|
||||
{
|
||||
for (int32 i = 0; i < SpringArm->GetNumChildrenComponents(); ++i)
|
||||
{
|
||||
USceneComponent* Child = SpringArm->GetChildComponent(i);
|
||||
|
||||
if (UCameraComponent* PotentialCamera = Cast<UCameraComponent>(Child))
|
||||
{
|
||||
Camera = PotentialCamera;
|
||||
break; // found it
|
||||
}
|
||||
}
|
||||
}
|
||||
return Camera;
|
||||
}
|
||||
}
|
||||
|
||||
AUGC_PlayerCameraManager::AUGC_PlayerCameraManager()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::InitializeFor(APlayerController* PC)
|
||||
{
|
||||
Super::InitializeFor(PC);
|
||||
RefreshLevelSequences(); // TO DO, this might not work for open worlds if level sequences are loaded in runtime.
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::PrePossess(APawn* NewPawn, UUGC_CameraDataAssetBase* NewCameraDA, bool bBlendSpringArmProperties, bool bMatchCameraRotation)
|
||||
{
|
||||
if (!NewPawn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
USpringArmComponent* NewSpringArm = NewPawn->FindComponentByClass<USpringArmComponent>();
|
||||
if (!NewSpringArm || !CameraArm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bBlendSpringArmProperties)
|
||||
{
|
||||
const bool bSkipChecks = !NewCameraDA || !GetCurrentCameraDataAsset();
|
||||
if (bSkipChecks || ((NewCameraDA->ArmLengthSettings.MinArmLength != GetCurrentCameraDataAsset()->ArmLengthSettings.MinArmLength
|
||||
|| NewCameraDA->ArmLengthSettings.MaxArmLength != GetCurrentCameraDataAsset()->ArmLengthSettings.MaxArmLength)
|
||||
&& NewCameraDA->ArmLengthSettings.ArmRangeBlendTime > 0.f))
|
||||
{
|
||||
NewSpringArm->TargetArmLength = CameraArm->TargetArmLength;
|
||||
}
|
||||
|
||||
if (bSkipChecks || (NewCameraDA->ArmOffsetSettings.ArmSocketOffset != GetCurrentCameraDataAsset()->ArmOffsetSettings.ArmSocketOffset
|
||||
&& NewCameraDA->ArmOffsetSettings.ArmSocketOffsetBlendTime > 0.f))
|
||||
{
|
||||
NewSpringArm->SocketOffset = CameraArm->SocketOffset;
|
||||
}
|
||||
|
||||
if (bSkipChecks || (NewCameraDA->ArmOffsetSettings.ArmTargetOffset != GetCurrentCameraDataAsset()->ArmOffsetSettings.ArmTargetOffset
|
||||
&& NewCameraDA->ArmOffsetSettings.ArmTargetOffsetBlendTime > 0.f))
|
||||
{
|
||||
NewSpringArm->TargetOffset = CameraArm->TargetOffset;
|
||||
}
|
||||
|
||||
if (bSkipChecks || ((NewCameraDA->FOVSettings.MinFOV != GetCurrentCameraDataAsset()->FOVSettings.MinFOV
|
||||
|| NewCameraDA->FOVSettings.MaxFOV != GetCurrentCameraDataAsset()->FOVSettings.MaxFOV)
|
||||
&& NewCameraDA->FOVSettings.FOVRangeBlendTime > 0.f))
|
||||
{
|
||||
if (UCameraComponent* NewCamera = PlayerCameraHelpers::GetSpringArmChildCamera(NewSpringArm))
|
||||
{
|
||||
NewCamera->SetFieldOfView(ViewTarget.POV.FOV);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (NewCameraDA)
|
||||
{
|
||||
const float PitchRatio = FMath::GetMappedRangeValueClamped(
|
||||
FVector2D(ViewPitchMin, ViewPitchMax), FVector2D(-1.f, 1.f),
|
||||
bMatchCameraRotation ? static_cast<float>(ViewTarget.POV.Rotation.Pitch) : 0.f);
|
||||
NewSpringArm->TargetArmLength = NewCameraDA->PitchToArmAndFOVCurveSettings.PitchToArmLengthCurve ?
|
||||
FMath::GetMappedRangeValueClamped(FVector2D(-1.f, 1.f), FVector2D(NewCameraDA->ArmLengthSettings.MinArmLength, NewCameraDA->ArmLengthSettings.MaxArmLength), NewCameraDA->PitchToArmAndFOVCurveSettings.PitchToArmLengthCurve->GetFloatValue(PitchRatio))
|
||||
: NewCameraDA->ArmLengthSettings.MinArmLength;
|
||||
NewSpringArm->SocketOffset = NewCameraDA->ArmOffsetSettings.ArmSocketOffset;
|
||||
NewSpringArm->TargetOffset = NewCameraDA->ArmOffsetSettings.ArmTargetOffset;
|
||||
if (UCameraComponent* NewCamera = PlayerCameraHelpers::GetSpringArmChildCamera(NewSpringArm))
|
||||
{
|
||||
NewCamera->SetFieldOfView(NewCameraDA->PitchToArmAndFOVCurveSettings.PitchToFOVCurve ?
|
||||
FMath::GetMappedRangeValueClamped(FVector2D(-1.f, 1.f), FVector2D(NewCameraDA->FOVSettings.MinFOV, NewCameraDA->FOVSettings.MaxFOV), NewCameraDA->PitchToArmAndFOVCurveSettings.PitchToFOVCurve->GetFloatValue(PitchRatio))
|
||||
: NewCameraDA->FOVSettings.MinFOV);
|
||||
}
|
||||
}
|
||||
|
||||
if (bMatchCameraRotation)
|
||||
{
|
||||
PendingPossessPayload.bMatchCameraRotation = true;
|
||||
PendingPossessPayload.PendingControlRotation = GetOwningPlayerController()->GetControlRotation();
|
||||
}
|
||||
PendingPossessPayload.PendingCameraDA = NewCameraDA;
|
||||
PendingPossessPayload.bBlendCameraProperties = bBlendSpringArmProperties;
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::PostPossess(bool bReplaceCurrentCameraDA)
|
||||
{
|
||||
if (PendingPossessPayload.PendingCameraDA)
|
||||
{
|
||||
UUGC_CameraDataAssetBase* CurrentHead = GetCurrentCameraDataAsset();
|
||||
PushCameraData_Internal(PendingPossessPayload.PendingCameraDA, PendingPossessPayload.bBlendCameraProperties);
|
||||
if (bReplaceCurrentCameraDA && CurrentHead)
|
||||
{
|
||||
PopCameraData(CurrentHead);
|
||||
}
|
||||
}
|
||||
if (PendingPossessPayload.bMatchCameraRotation)
|
||||
{
|
||||
GetOwningPlayerController()->SetControlRotation(PendingPossessPayload.PendingControlRotation);
|
||||
}
|
||||
PendingPossessPayload = AUGC_PlayerCameraManager::PossessPayload();
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::RefreshLevelSequences()
|
||||
{
|
||||
// This resets the array and gets all actors of class.
|
||||
QUICK_SCOPE_CYCLE_COUNTER(AUGC_PlayerCameraManager_RefreshLevelSequences);
|
||||
LevelSequences.Reset();
|
||||
|
||||
for (TActorIterator<ALevelSequenceActor> It(GetWorld()); It; ++It)
|
||||
{
|
||||
ALevelSequenceActor* LevelSequence = *It;
|
||||
LevelSequences.Add(LevelSequence);
|
||||
|
||||
LevelSequence->GetSequencePlayer()->OnPlay.AddDynamic(this, &AUGC_PlayerCameraManager::OnLevelSequenceStarted);
|
||||
LevelSequence->GetSequencePlayer()->OnPlayReverse.AddDynamic(this, &AUGC_PlayerCameraManager::OnLevelSequenceStarted);
|
||||
LevelSequence->GetSequencePlayer()->OnStop.AddDynamic(this, &AUGC_PlayerCameraManager::OnLevelSequenceEnded);
|
||||
LevelSequence->GetSequencePlayer()->OnPause.AddDynamic(this, &AUGC_PlayerCameraManager::OnLevelSequencePaused);
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::OnLevelSequenceStarted()
|
||||
{
|
||||
if (NbrActivePausedLevelSequences > 0) --NbrActivePausedLevelSequences;
|
||||
++NbrActiveLevelSequences;
|
||||
DoForEachUGCModifier(&UUGC_CameraModifier::OnAnyLevelSequenceStarted);
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::OnLevelSequencePaused()
|
||||
{
|
||||
++NbrActivePausedLevelSequences;
|
||||
--NbrActiveLevelSequences;
|
||||
ensure(NbrActiveLevelSequences >= 0);
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::OnLevelSequenceEnded()
|
||||
{
|
||||
--NbrActiveLevelSequences;
|
||||
ensure(NbrActiveLevelSequences >= 0);
|
||||
DoForEachUGCModifier(&UUGC_CameraModifier::OnAnyLevelSequenceEnded);
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
UpdateInternalVariables(DeltaTime);
|
||||
}
|
||||
|
||||
bool AUGC_PlayerCameraManager::IsPlayingAnyCameraAnimation() const
|
||||
{
|
||||
if (UUGC_CameraAnimationModifier const* CameraAnimModifier = FindCameraModifierOfType<UUGC_CameraAnimationModifier>())
|
||||
{
|
||||
return CameraAnimModifier && CameraAnimModifier->IsAnyCameraAnimationSequence();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::PlayCameraAnimation(UCameraAnimationSequence* CameraSequence, FUGCCameraAnimationParams const& Params, bool bInterruptOthers, bool bDoCollisionChecks)
|
||||
{
|
||||
if (UUGC_CameraAnimationModifier* CameraAnimModifier = FindCameraModifierOfType<UUGC_CameraAnimationModifier>())
|
||||
{
|
||||
CameraAnimModifier->PlaySingleCameraAnimation(CameraSequence, static_cast<FCameraAnimationParams>(Params), Params.ResetType, bInterruptOthers, bDoCollisionChecks);
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::SetViewTarget(AActor* NewViewTarget, FViewTargetTransitionParams TransitionParams)
|
||||
{
|
||||
auto OldPendingTarget = PendingViewTarget.Target;
|
||||
auto OldTarget = ViewTarget.Target;
|
||||
|
||||
Super::SetViewTarget(NewViewTarget, TransitionParams);
|
||||
|
||||
if (!OwnerPawn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool const bAssignedNewTarget = ViewTarget.Target != OldTarget;
|
||||
bool const bBlendingToNewTarget = PendingViewTarget.Target != OldPendingTarget;
|
||||
if (bAssignedNewTarget || bBlendingToNewTarget)
|
||||
{
|
||||
bool const bWasImmediate = bAssignedNewTarget && !bBlendingToNewTarget;
|
||||
bool bNewTargetIsOwner = false;
|
||||
|
||||
// If old character has been unpossessed, then our new target is the owner!
|
||||
// Or, if the view target is the controller, we are doing seamless travel which does not unpossess the pawn.
|
||||
if (bWasImmediate && (OwnerPawn->GetController() == nullptr || OwnerPawn->GetController() == ViewTarget.Target))
|
||||
{
|
||||
bNewTargetIsOwner = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bNewTargetIsOwner = bWasImmediate ? ViewTarget.Target == OwnerPawn : PendingViewTarget.Target == OwnerPawn;
|
||||
}
|
||||
|
||||
DoForEachUGCModifier([bNewTargetIsOwner, bWasImmediate](UUGC_CameraModifier* UGCModifier)
|
||||
{
|
||||
UGCModifier->OnSetViewTarget(bWasImmediate, bNewTargetIsOwner);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
UCameraModifier* AUGC_PlayerCameraManager::FindCameraModifierOfClass(TSubclassOf<UCameraModifier> ModifierClass, bool bIncludeInherited)
|
||||
{
|
||||
for (UCameraModifier* Mod : ModifierList)
|
||||
{
|
||||
if (bIncludeInherited)
|
||||
{
|
||||
if (Mod->GetClass()->IsChildOf(ModifierClass))
|
||||
{
|
||||
return Mod;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Mod->GetClass() == ModifierClass)
|
||||
{
|
||||
return Mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UCameraModifier const* AUGC_PlayerCameraManager::FindCameraModifierOfClass(TSubclassOf<UCameraModifier> ModifierClass, bool bIncludeInherited) const
|
||||
{
|
||||
for (UCameraModifier* Mod : ModifierList)
|
||||
{
|
||||
if (bIncludeInherited)
|
||||
{
|
||||
if (Mod->GetClass()->IsChildOf(ModifierClass))
|
||||
{
|
||||
return Mod;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Mod->GetClass() == ModifierClass)
|
||||
{
|
||||
return Mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::ToggleUGCCameraModifiers(bool const bEnabled, bool const bImmediate)
|
||||
{
|
||||
DoForEachUGCModifier([bEnabled, bImmediate](UUGC_CameraModifier* UGCModifier)
|
||||
{
|
||||
if (bEnabled)
|
||||
{
|
||||
UGCModifier->EnableModifier();
|
||||
}
|
||||
else
|
||||
{
|
||||
UGCModifier->DisableModifier(bImmediate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::ToggleCameraModifiers(bool const bEnabled, bool const bImmediate)
|
||||
{
|
||||
for (int32 ModifierIdx = 0; ModifierIdx < ModifierList.Num(); ModifierIdx++)
|
||||
{
|
||||
if (ModifierList[ModifierIdx] != nullptr)
|
||||
{
|
||||
if (bEnabled)
|
||||
{
|
||||
ModifierList[ModifierIdx]->EnableModifier();
|
||||
}
|
||||
else
|
||||
{
|
||||
ModifierList[ModifierIdx]->DisableModifier(bImmediate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::ToggleAllUGCModifiersDebug(bool const bEnabled)
|
||||
{
|
||||
DoForEachUGCModifier([bEnabled](UUGC_CameraModifier* UGCModifier)
|
||||
{
|
||||
if (!UGCModifier->IsDisabled())
|
||||
{
|
||||
UGCModifier->bDebug = bEnabled;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::ToggleAllModifiersDebug(bool const bEnabled)
|
||||
{
|
||||
for (int32 ModifierIdx = 0; ModifierIdx < ModifierList.Num(); ModifierIdx++)
|
||||
{
|
||||
if (ModifierList[ModifierIdx] != nullptr && !ModifierList[ModifierIdx]->IsDisabled())
|
||||
{
|
||||
ModifierList[ModifierIdx]->bDebug = bEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::PushCameraData_Internal(UUGC_CameraDataAssetBase* CameraDA, bool bBlendCameraProperties)
|
||||
{
|
||||
CameraDataStack.Push(CameraDA);
|
||||
OnCameraDataStackChanged(CameraDA, bBlendCameraProperties);
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::PushCameraData(UUGC_CameraDataAssetBase* CameraDA)
|
||||
{
|
||||
static constexpr bool bBlendCameraProperties = true;
|
||||
PushCameraData_Internal(CameraDA, bBlendCameraProperties);
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::PopCameraDataHead()
|
||||
{
|
||||
CameraDataStack.Pop();
|
||||
OnCameraDataStackChanged(CameraDataStack.IsEmpty() ? nullptr : CameraDataStack[CameraDataStack.Num() - 1]);
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::PopCameraData(UUGC_CameraDataAssetBase* CameraDA)
|
||||
{
|
||||
if (CameraDataStack.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetCurrentCameraDataAsset() == CameraDA)
|
||||
{
|
||||
PopCameraDataHead();
|
||||
}
|
||||
|
||||
CameraDataStack.Remove(CameraDA);
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::OnCameraDataStackChanged_Implementation(UUGC_CameraDataAssetBase* CameraDA, bool bBlendSpringArmProperties)
|
||||
{
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::ProcessViewRotation(float DeltaTime, FRotator& OutViewRotation, FRotator& OutDeltaRot)
|
||||
{
|
||||
Super::ProcessViewRotation(DeltaTime, OutViewRotation, OutDeltaRot);
|
||||
if (PCOwner && ViewTarget.Target)
|
||||
{
|
||||
FRotator const ControlRotation = PCOwner->GetControlRotation();
|
||||
FRotator const OwnerRotation = ViewTarget.Target->GetActorRotation();
|
||||
FRotator InLocalControlRotation = ControlRotation - OwnerRotation;
|
||||
InLocalControlRotation.Normalize();
|
||||
|
||||
float OutPitchTurnRate = PitchTurnRate;
|
||||
float OutYawTurnRate = YawTurnRate;
|
||||
|
||||
// TO DO #GravityCompatibility
|
||||
ProcessTurnRate(DeltaTime, InLocalControlRotation, OutPitchTurnRate, OutYawTurnRate);
|
||||
|
||||
PitchTurnRate = FMath::Clamp(OutPitchTurnRate, 0.f, 1.f);
|
||||
YawTurnRate = FMath::Clamp(OutYawTurnRate, 0.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::ProcessTurnRate(float DeltaTime, FRotator InLocalControlRotation, float& OutPitchTurnRate, float& OutYawTurnRate)
|
||||
{
|
||||
DoForEachUGCModifierWithBreak([&](UUGC_CameraModifier* UGCModifier) -> bool
|
||||
{
|
||||
if (!UGCModifier->IsDisabled())
|
||||
{
|
||||
if (!UGCModifier->CanPlayDuringCameraAnimation())
|
||||
{
|
||||
if (IsPlayingAnyCameraAnimation())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TO DO #GravityCompatibility
|
||||
return UGCModifier->ProcessTurnRate(DeltaTime, InLocalControlRotation, PitchTurnRate, YawTurnRate, OutPitchTurnRate, OutYawTurnRate);
|
||||
}
|
||||
return false; // Don't break
|
||||
});
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::UpdateInternalVariables_Implementation(float DeltaTime)
|
||||
{
|
||||
AspectRatio = GetCameraCacheView().AspectRatio;
|
||||
HorizontalFOV = GetFOVAngle();
|
||||
ensureAlways(!FMath::IsNearlyZero(AspectRatio));
|
||||
VerticalFOV = FMath::RadiansToDegrees(2.f * FMath::Atan(FMath::Tan(FMath::DegreesToRadians(HorizontalFOV) * 0.5f) / AspectRatio));
|
||||
|
||||
if (PCOwner && PCOwner->GetPawn())
|
||||
{
|
||||
APawn* NewOwnerPawn = PCOwner->GetPawn();
|
||||
if (!OwnerPawn || NewOwnerPawn != OwnerPawn)
|
||||
{
|
||||
// Either initialising reference, or we have possessed a new character.
|
||||
OwnerPawn = NewOwnerPawn;
|
||||
if (OwnerCharacter = PCOwner->GetPawn<ACharacter>(),
|
||||
OwnerCharacter != nullptr)
|
||||
{
|
||||
MovementComponent = OwnerCharacter->GetCharacterMovement();
|
||||
}
|
||||
CameraArm = OwnerPawn->FindComponentByClass<USpringArmComponent>();
|
||||
OriginalArmLength = CameraArm ? CameraArm->TargetArmLength : 0.f;
|
||||
|
||||
}
|
||||
|
||||
if (OwnerPawn)
|
||||
{
|
||||
MovementInput = GetMovementControlInput();
|
||||
bHasMovementInput = !MovementInput.IsZero();
|
||||
TimeSinceMovementInput = bHasMovementInput ? 0.f : TimeSinceMovementInput + DeltaTime;
|
||||
|
||||
RotationInput = GetRotationInput();
|
||||
bHasRotationInput = !RotationInput.IsZero();
|
||||
TimeSinceRotationInput = bHasRotationInput ? 0.f : TimeSinceRotationInput + DeltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FRotator AUGC_PlayerCameraManager::GetRotationInput_Implementation() const
|
||||
{
|
||||
FRotator RotInput = FRotator::ZeroRotator;
|
||||
|
||||
if (OwnerPawn && OwnerPawn->GetClass()->ImplementsInterface(UUGC_PawnInterface::StaticClass()))
|
||||
{
|
||||
RotInput = IUGC_PawnInterface::Execute_GetRotationInput(OwnerPawn);
|
||||
}
|
||||
return RotInput;
|
||||
}
|
||||
|
||||
FVector AUGC_PlayerCameraManager::GetMovementControlInput_Implementation() const
|
||||
{
|
||||
FVector MovInput = FVector::ZeroVector;
|
||||
|
||||
if (OwnerPawn && OwnerPawn->GetClass()->ImplementsInterface(UUGC_PawnInterface::StaticClass()))
|
||||
{
|
||||
MovInput = IUGC_PawnInterface::Execute_GetMovementInput(OwnerPawn);
|
||||
}
|
||||
return MovInput;
|
||||
}
|
||||
|
||||
// Limit the view yaw in local space instead of world space.
|
||||
void AUGC_PlayerCameraManager::LimitViewYaw(FRotator& ViewRotation, float InViewYawMin, float InViewYawMax)
|
||||
{
|
||||
// TO DO #GravityCompatibility
|
||||
if (PCOwner && PCOwner->GetPawn())
|
||||
{
|
||||
FRotator ActorRotation = PCOwner->GetPawn()->GetActorRotation();
|
||||
ViewRotation.Yaw = FMath::ClampAngle(ViewRotation.Yaw, ActorRotation.Yaw + InViewYawMin, ActorRotation.Yaw + InViewYawMax);
|
||||
ViewRotation.Yaw = FRotator::ClampAxis(ViewRotation.Yaw);
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::DrawRealDebugCamera(float Duration, FLinearColor CameraColor, float Thickness) const
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
::DrawDebugCamera(GetWorld(), ViewTarget.POV.Location, ViewTarget.POV.Rotation, ViewTarget.POV.FOV, 1.0f, CameraColor.ToFColor(true), false, Duration);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Draw a debug camera shape. */
|
||||
void AUGC_PlayerCameraManager::DrawGameDebugCamera(float Duration, bool bDrawCamera, FLinearColor CameraColor, bool bDrawSpringArm, FLinearColor SpringArmColor, float Thickness) const
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
|
||||
if (bDrawCamera && CameraArm)
|
||||
{
|
||||
int32 const NbrComponents = CameraArm->GetNumChildrenComponents();
|
||||
for (int32 i = 0; i < NbrComponents; ++i)
|
||||
{
|
||||
if (USceneComponent* ChildComp = CameraArm->GetChildComponent(i))
|
||||
{
|
||||
if (UCameraComponent* CameraComp = Cast<UCameraComponent>(ChildComp))
|
||||
{
|
||||
::DrawDebugCamera(GetWorld(), CameraComp->GetComponentLocation(), CameraComp->GetComponentRotation(), ViewTarget.POV.FOV, 1.0f, CameraColor.ToFColor(true), false, Duration);
|
||||
|
||||
if (bDrawSpringArm)
|
||||
{
|
||||
DrawDebugSpringArm(CameraComp->GetComponentLocation(), Duration, SpringArmColor, Thickness);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::DrawDebugSpringArm(FVector const& CameraLocation, float Duration, FLinearColor SpringArmColor, float Thickness) const
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (CameraArm)
|
||||
{
|
||||
FVector const SafeLocation = CameraArm->GetComponentLocation() + CameraArm->TargetOffset;
|
||||
::DrawDebugLine(GetWorld(), CameraLocation, SafeLocation, SpringArmColor.ToFColor(true), false, Duration, 0, Thickness);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::DoForEachUGCModifier(TFunction<void(UUGC_CameraModifier*)> const& Function)
|
||||
{
|
||||
if (Function)
|
||||
{
|
||||
for (int32 ModifierIdx = 0; ModifierIdx < UGCModifiersList.Num(); ++ModifierIdx)
|
||||
{
|
||||
ensure(UGCModifiersList[ModifierIdx]);
|
||||
|
||||
if (UGCModifiersList[ModifierIdx])
|
||||
{
|
||||
Function(UGCModifiersList[ModifierIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::DoForEachUGCModifierWithBreak(TFunction<bool(UUGC_CameraModifier*)> const& Function)
|
||||
{
|
||||
if (Function)
|
||||
{
|
||||
for (int32 ModifierIdx = 0; ModifierIdx < UGCModifiersList.Num(); ++ModifierIdx)
|
||||
{
|
||||
ensure(UGCModifiersList[ModifierIdx]);
|
||||
|
||||
if (UGCModifiersList[ModifierIdx])
|
||||
{
|
||||
if (Function(UGCModifiersList[ModifierIdx]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::DisplayDebug(class UCanvas* Canvas, const FDebugDisplayInfo& DebugDisplay, float& YL, float& YPos)
|
||||
{
|
||||
Super::DisplayDebug(Canvas, DebugDisplay, YL, YPos);
|
||||
const bool bShowModifierList = GShowCameraManagerModifiersCVar.GetValueOnGameThread();
|
||||
if (bShowModifierList)
|
||||
{
|
||||
for (int32 ModifierIdx = 0; ModifierIdx < ModifierList.Num(); ++ModifierIdx)
|
||||
{
|
||||
if (ModifierList[ModifierIdx] != nullptr)
|
||||
{
|
||||
Canvas->SetDrawColor(FColor::White);
|
||||
FString DebugString = FString::Printf(TEXT("UGC Modifier %d: %s - Priority %d"), ModifierIdx, *ModifierList[ModifierIdx]->GetName(), ModifierList[ModifierIdx]->Priority);
|
||||
Canvas->DrawText(GEngine->GetSmallFont(), DebugString, YL, YPos);
|
||||
YPos += YL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UCameraModifier* AUGC_PlayerCameraManager::AddNewCameraModifier(TSubclassOf<UCameraModifier> ModifierClass)
|
||||
{
|
||||
UCameraModifier* AddedModifier = Super::AddNewCameraModifier(ModifierClass);
|
||||
if (AddedModifier)
|
||||
{
|
||||
if (UUGC_CameraModifier* UGCModifier = Cast<UUGC_CameraModifier>(AddedModifier))
|
||||
{
|
||||
UGCModifiersList.Add(UGCModifier);
|
||||
if (UUGC_CameraAddOnModifier* UGCAddOnModifier = Cast<UUGC_CameraAddOnModifier>(AddedModifier))
|
||||
{
|
||||
UGCAddOnModifiersList.Add(UGCAddOnModifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
return AddedModifier;
|
||||
}
|
||||
|
||||
bool AUGC_PlayerCameraManager::RemoveCameraModifier(UCameraModifier* ModifierToRemove)
|
||||
{
|
||||
if (ModifierToRemove)
|
||||
{
|
||||
if (UUGC_CameraModifier* UGCModifierToRemove = Cast<UUGC_CameraModifier>(ModifierToRemove))
|
||||
{
|
||||
// Loop through each modifier in camera
|
||||
for (int32 ModifierIdx = 0; ModifierIdx < UGCModifiersList.Num(); ++ModifierIdx)
|
||||
{
|
||||
// If we found ourselves, remove ourselves from the list and return
|
||||
if (UGCModifiersList[ModifierIdx] == UGCModifierToRemove)
|
||||
{
|
||||
UGCModifiersList.RemoveAt(ModifierIdx, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (UUGC_CameraAddOnModifier* UGCAddOnModifier = Cast<UUGC_CameraAddOnModifier>(ModifierToRemove))
|
||||
{
|
||||
// Loop through each modifier in camera
|
||||
for (int32 ModifierIdx = 0; ModifierIdx < UGCAddOnModifiersList.Num(); ++ModifierIdx)
|
||||
{
|
||||
// If we found ourselves, remove ourselves from the list and return
|
||||
if (UGCAddOnModifiersList[ModifierIdx] == UGCModifierToRemove)
|
||||
{
|
||||
UGCAddOnModifiersList.RemoveAt(ModifierIdx, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Super::RemoveCameraModifier(ModifierToRemove);
|
||||
}
|
||||
|
||||
FVector AUGC_PlayerCameraManager::GetOwnerVelocity() const
|
||||
{
|
||||
FVector Velocity = FVector::ZeroVector;
|
||||
if (MovementComponent)
|
||||
{
|
||||
Velocity = MovementComponent->Velocity;
|
||||
}
|
||||
else if (OwnerPawn && OwnerPawn->GetClass()->ImplementsInterface(UUGC_PawnMovementInterface::StaticClass()))
|
||||
{
|
||||
Velocity = IUGC_PawnMovementInterface::Execute_GetOwnerVelocity(OwnerPawn);
|
||||
}
|
||||
return Velocity;
|
||||
}
|
||||
|
||||
bool AUGC_PlayerCameraManager::IsOwnerFalling() const
|
||||
{
|
||||
bool bIsFalling = false;
|
||||
if (MovementComponent)
|
||||
{
|
||||
bIsFalling = MovementComponent->IsFalling();
|
||||
}
|
||||
else if (OwnerPawn && OwnerPawn->GetClass()->ImplementsInterface(UUGC_PawnMovementInterface::StaticClass()))
|
||||
{
|
||||
bIsFalling = IUGC_PawnMovementInterface::Execute_IsOwnerFalling(OwnerPawn);
|
||||
}
|
||||
return bIsFalling;
|
||||
}
|
||||
|
||||
bool AUGC_PlayerCameraManager::IsOwnerStrafing() const
|
||||
{
|
||||
bool bIsStrafing = false;
|
||||
if (MovementComponent && OwnerPawn)
|
||||
{
|
||||
bIsStrafing = OwnerPawn->bUseControllerRotationYaw || (MovementComponent->bUseControllerDesiredRotation && !MovementComponent->bOrientRotationToMovement);
|
||||
}
|
||||
else if (OwnerPawn)
|
||||
{
|
||||
if (OwnerPawn->GetClass()->ImplementsInterface(UUGC_PawnMovementInterface::StaticClass()))
|
||||
{
|
||||
bIsStrafing = IUGC_PawnMovementInterface::Execute_IsOwnerStrafing(OwnerPawn);
|
||||
}
|
||||
else
|
||||
{
|
||||
bIsStrafing = OwnerPawn->bUseControllerRotationYaw;
|
||||
}
|
||||
}
|
||||
return bIsStrafing;
|
||||
}
|
||||
|
||||
bool AUGC_PlayerCameraManager::IsOwnerMovingOnGround() const
|
||||
{
|
||||
bool bIsMovingOnGround = false;
|
||||
if (MovementComponent)
|
||||
{
|
||||
bIsMovingOnGround = MovementComponent->IsMovingOnGround();
|
||||
}
|
||||
else if (OwnerPawn && OwnerPawn->GetClass()->ImplementsInterface(UUGC_PawnMovementInterface::StaticClass()))
|
||||
{
|
||||
bIsMovingOnGround = IUGC_PawnMovementInterface::Execute_IsOwnerMovingOnGround(OwnerPawn);
|
||||
}
|
||||
return bIsMovingOnGround;
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::ComputeOwnerFloorDist(float SweepDistance, float CapsuleRadius, bool& bOutFloorExists, float& OutFloorDistance) const
|
||||
{
|
||||
if (MovementComponent && OwnerCharacter)
|
||||
{
|
||||
CapsuleRadius = FMath::Max(CapsuleRadius, OwnerCharacter->GetCapsuleComponent()->GetScaledCapsuleRadius());
|
||||
FFindFloorResult OutFloorResult;
|
||||
MovementComponent->ComputeFloorDist(OwnerPawn->GetActorLocation(), SweepDistance, SweepDistance, OutFloorResult, CapsuleRadius);
|
||||
bOutFloorExists = OutFloorResult.bBlockingHit;
|
||||
OutFloorDistance = bOutFloorExists ? OutFloorResult.FloorDist : 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
FHitResult OutHit;
|
||||
bOutFloorExists = GetWorld()->SweepSingleByChannel(OutHit
|
||||
, OwnerPawn->GetActorLocation()
|
||||
, OwnerPawn->GetActorLocation() - SweepDistance * FVector::UpVector
|
||||
, FQuat::Identity
|
||||
, ECollisionChannel::ECC_Visibility
|
||||
, FCollisionShape::MakeSphere(CapsuleRadius));
|
||||
|
||||
OutFloorDistance = bOutFloorExists ? OutHit.Distance : 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::ComputeOwnerFloorNormal(float SweepDistance, float CapsuleRadius, bool& bOutFloorExists, FVector& OutFloorNormal) const
|
||||
{
|
||||
if (MovementComponent && OwnerCharacter)
|
||||
{
|
||||
bOutFloorExists = MovementComponent->CurrentFloor.IsWalkableFloor();
|
||||
OutFloorNormal = MovementComponent->CurrentFloor.HitResult.ImpactNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
FHitResult OutHit;
|
||||
bOutFloorExists = GetWorld()->SweepSingleByChannel(OutHit
|
||||
, OwnerPawn->GetActorLocation()
|
||||
, OwnerPawn->GetActorLocation() - SweepDistance * FVector::UpVector
|
||||
, FQuat::Identity
|
||||
, ECollisionChannel::ECC_Visibility
|
||||
, FCollisionShape::MakeSphere(CapsuleRadius));
|
||||
|
||||
bOutFloorExists = OutHit.bBlockingHit;
|
||||
OutFloorNormal = bOutFloorExists ? OutHit.ImpactNormal : FVector::ZeroVector;
|
||||
}
|
||||
}
|
||||
|
||||
void AUGC_PlayerCameraManager::ComputeOwnerSlopeAngle(float& OutSlopePitchDegrees, float& OutSlopeRollDegrees)
|
||||
{
|
||||
bool bOutFloorExists = false;
|
||||
FVector OutFloorNormal = FVector::ZeroVector;
|
||||
ComputeOwnerFloorNormal(96.f, 64.f, bOutFloorExists, OutFloorNormal);
|
||||
UKismetMathLibrary::GetSlopeDegreeAngles(OwnerPawn->GetActorRightVector(), OutFloorNormal, OwnerPawn->GetActorUpVector(), OutSlopePitchDegrees, OutSlopeRollDegrees);
|
||||
}
|
||||
|
||||
float AUGC_PlayerCameraManager::ComputeOwnerLookAndMovementDot()
|
||||
{
|
||||
if (IsOwnerStrafing())
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
FVector const Velocity = GetOwnerVelocity();
|
||||
if (Velocity.IsNearlyZero())
|
||||
{
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
float const Dot = Velocity | OwnerPawn->GetControlRotation().Vector();
|
||||
return Dot;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#include "Input/UGC_CameraTurnRateModifier.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "Camera/UGC_PlayerCameraManager.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
|
||||
FInputActionValue UUGC_CameraTurnRateModifier::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime)
|
||||
{
|
||||
EInputActionValueType ValueType = CurrentValue.GetValueType();
|
||||
if (ValueType == EInputActionValueType::Boolean)
|
||||
{
|
||||
return CurrentValue;
|
||||
}
|
||||
|
||||
if (!PlayerCameraManager)
|
||||
{
|
||||
if (!PlayerInput->GetOuterAPlayerController() || !PlayerInput->GetOuterAPlayerController()->PlayerCameraManager)
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
// Debugging
|
||||
if (GEngine)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor::Red, FString::Printf(TEXT("UGC_CameraSlowDownInputModifier: Could not find Player Camera Manager to use Camera Slow Down constraint.")));
|
||||
}
|
||||
#endif
|
||||
return CurrentValue;
|
||||
}
|
||||
|
||||
AUGC_PlayerCameraManager* PCManager = Cast<AUGC_PlayerCameraManager>(PlayerInput->GetOuterAPlayerController()->PlayerCameraManager);
|
||||
if (!PCManager)
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
// Debugging
|
||||
if (GEngine)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor::Red, FString::Printf(TEXT("UGC_CameraSlowDownInputModifier: Player's Camera Manager does not inherit from UGC_PlayerCameraManager. Angle Constraint cannot be used.")));
|
||||
}
|
||||
#endif
|
||||
return CurrentValue;
|
||||
}
|
||||
// Here we know that it's not null
|
||||
PlayerCameraManager = PCManager;
|
||||
}
|
||||
|
||||
return CurrentValue.Get<FVector>() * PlayerCameraManager->GetCameraTurnRate();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
|
||||
#include "Input/UGC_InputAccelerationModifier.h"
|
||||
|
||||
#include "Curves/CurveFloat.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
FInputActionValue UUGC_InputAccelerationModifier::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime)
|
||||
{
|
||||
EInputActionValueType ValueType = CurrentValue.GetValueType();
|
||||
if (ValueType == EInputActionValueType::Boolean)
|
||||
{
|
||||
return CurrentValue;
|
||||
}
|
||||
|
||||
if (CurrentValue.Get<FVector>() == FVector::ZeroVector || !AccelerationCurve)
|
||||
{
|
||||
Timer = 0.f;
|
||||
return CurrentValue;
|
||||
}
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
// Debugging
|
||||
if (GEngine)
|
||||
{
|
||||
bool bValidCurve = true;
|
||||
FVector2D TimeRange;
|
||||
AccelerationCurve->GetTimeRange(TimeRange.X, TimeRange.Y);
|
||||
|
||||
if (TimeRange.X != 0.f || TimeRange.Y != 1.f)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor::Red, FString::Printf(TEXT("UGC_InputAccelerationModifier: Given Curve's time range (X Axis) is not normalized i.e., between 0 and 1! Found: %s"), *TimeRange.ToString()));
|
||||
bValidCurve = false;
|
||||
}
|
||||
|
||||
if (!bValidCurve)
|
||||
{
|
||||
return CurrentValue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Timer += DeltaTime;
|
||||
float const ClampedTime = FMath::Clamp(AccelerationTime > 0.f ? Timer / AccelerationTime : 0.f, 0.f, 1.f);
|
||||
return CurrentValue.Get<FVector>() * AccelerationCurve->GetFloatValue(ClampedTime);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
|
||||
#include "Pawn/UGC_PawnInterface.h"
|
||||
|
||||
// Add default functionality here for any IUGC_PawnInterface functions that are not pure virtual.
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Animation/AnimNotifies/AnimNotifyState.h"
|
||||
#include "Curves/CurveFloat.h"
|
||||
#include "Misc/Guid.h"
|
||||
#include "UGC_CameraPropertiesRequestAnimNotifyState.generated.h"
|
||||
|
||||
/**
|
||||
* Anim Notify State to send requests to the FOVAnimNotifyModifier to change the FOV during animations.
|
||||
*/
|
||||
UCLASS()
|
||||
class AURORADEVS_UGC_API UUGC_FOVRequestAnimNotifyState : public UAnimNotifyState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference) override;
|
||||
virtual FString GetNotifyName_Implementation() const override;
|
||||
virtual FLinearColor GetEditorColor() override;
|
||||
|
||||
protected:
|
||||
/** Horizontal FOV to set. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
float TargetFOV = 90.f;
|
||||
|
||||
/** Time needed for the FOV value to ease into the TargetValue. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
float BlendInDuration = 0.5f;
|
||||
|
||||
/** Controls the blending in. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermit if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
TObjectPtr<UCurveFloat> BlendInCurve = nullptr;
|
||||
|
||||
/** Time needed for the FOV value to ease out from the TargetValue into the normal gameplay value. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
float BlendOutDuration = 0.5f;
|
||||
|
||||
/** Controls the blending Out. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermit if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
TObjectPtr<UCurveFloat> BlendOutCurve = nullptr;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/** Whether the name of the notify should include the request Id. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
bool bShowRequestIdInName = false;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
FGuid RequestId = FGuid::NewGuid();
|
||||
};
|
||||
|
||||
/**
|
||||
* Anim Notify State to send requests to the ArmOffsetAnimNotifyModifier to change the Arm Offsets during animations.
|
||||
*/
|
||||
UCLASS()
|
||||
class AURORADEVS_UGC_API UUGC_ArmOffsetRequestAnimNotifyState : public UAnimNotifyState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference) override;
|
||||
virtual FString GetNotifyName_Implementation() const override;
|
||||
virtual FLinearColor GetEditorColor() override;
|
||||
|
||||
protected:
|
||||
/** Whether to change socket offset. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|SocketOffset")
|
||||
bool bModifySocketOffset = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|SocketOffset", meta = (EditCondition="bModifySocketOffset"))
|
||||
FVector TargetSocketOffset = FVector::Zero();
|
||||
|
||||
/** Time needed for the Socket Offset value to ease into the TargetValue. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|SocketOffset", meta = (EditCondition = "bModifySocketOffset"))
|
||||
float SocketOffsetBlendInDuration = 0.5f;
|
||||
|
||||
/** Controls the blending in. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermit if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|SocketOffset", meta = (EditCondition = "bModifySocketOffset"))
|
||||
TObjectPtr<UCurveFloat> SocketOffsetBlendInCurve = nullptr;
|
||||
|
||||
/** Time needed for the Socket Offset value to ease out from the TargetValue into the normal gameplay value. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|SocketOffset", meta = (EditCondition = "bModifySocketOffset"))
|
||||
float SocketOffsetBlendOutDuration = 0.5f;
|
||||
|
||||
/** Controls the blending Out. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermit if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|SocketOffset", meta = (EditCondition = "bModifySocketOffset"))
|
||||
TObjectPtr<UCurveFloat> SocketOffsetBlendOutCurve = nullptr;
|
||||
|
||||
/** Whether to change socket offset. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|TargetOffset")
|
||||
bool bModifyTargetOffset = false;
|
||||
|
||||
/** Target Offset to set. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|TargetOffset", meta = (EditCondition = "bModifyTargetOffset"))
|
||||
FVector TargetTargetOffset = FVector::Zero();
|
||||
|
||||
/** Time needed for the Target Offset value to ease into the TargetValue. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|TargetOffset", meta = (EditCondition = "bModifyTargetOffset"))
|
||||
float TargetOffsetBlendInDuration = 0.5f;
|
||||
|
||||
/** Controls the blending in. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermit if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|TargetOffset", meta = (EditCondition = "bModifyTargetOffset"))
|
||||
TObjectPtr<UCurveFloat> TargetOffsetBlendInCurve = nullptr;
|
||||
|
||||
/** Time needed for the Target Offset value to ease out from the TargetValue into the normal gameplay value. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|TargetOffset", meta = (EditCondition = "bModifyTargetOffset"))
|
||||
float TargetOffsetBlendOutDuration = 0.5f;
|
||||
|
||||
/** Controls the blending Out. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermit if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|TargetOffset", meta = (EditCondition = "bModifyTargetOffset"))
|
||||
TObjectPtr<UCurveFloat> TargetOffsetBlendOutCurve = nullptr;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/** Whether the name of the notify should include the request Id. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
bool bShowRequestIdInName = false;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
FGuid RequestId = FGuid::NewGuid();
|
||||
};
|
||||
|
||||
/**
|
||||
* Anim Notify State to send requests to the ArmLengthAnimNotifyModifier to change the Arm Length during animations.
|
||||
*/
|
||||
UCLASS()
|
||||
class AURORADEVS_UGC_API UUGC_ArmLengthRequestAnimNotifyState : public UAnimNotifyState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference) override;
|
||||
virtual FString GetNotifyName_Implementation() const override;
|
||||
virtual FLinearColor GetEditorColor() override;
|
||||
|
||||
protected:
|
||||
/** ArmLength to set. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
float TargetArmLength = 0.f;
|
||||
|
||||
/** Time needed for the ArmLength value to ease into the TargetValue. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
float BlendInDuration = 0.5f;
|
||||
|
||||
/** Controls the blending in. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermit if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
TObjectPtr<UCurveFloat> BlendInCurve = nullptr;
|
||||
|
||||
/** Time needed for the ArmLength value to ease out from the TargetValue into the normal gameplay value. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
float BlendOutDuration = 0.5f;
|
||||
|
||||
/** Controls the blending Out. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermit if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
TObjectPtr<UCurveFloat> BlendOutCurve = nullptr;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/** Whether the name of the notify should include the request Id. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
|
||||
bool bShowRequestIdInName = false;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
FGuid RequestId = FGuid::NewGuid();
|
||||
};
|
||||
30
Plugins/UGC/Source/AuroraDevs_UGC/Public/AuroraDevs_UGC.h
Normal file
30
Plugins/UGC/Source/AuroraDevs_UGC/Public/AuroraDevs_UGC.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#ifndef AURORA_DEVS_UGC
|
||||
#define AURORA_DEVS_UGC
|
||||
#endif
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(AuroraUGC, Log, All);
|
||||
#define UGC_LOG(Verbosity, Format, ...) UE_LOG(AuroraUGC, Verbosity, Format, ##__VA_ARGS__)
|
||||
#define UGC_LOG_ONCE(LogId, Verbosity, Format, ...)\
|
||||
do\
|
||||
{\
|
||||
static bool bLogged##LogId##Already = false; \
|
||||
if (!bLogged##LogId##Already)\
|
||||
{\
|
||||
UE_LOG(AuroraUGC, Verbosity, Format, ##__VA_ARGS__); \
|
||||
bLogged##LogId##Already = true; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
class FAuroraDevs_UGCModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
#include "Camera/Data/UGC_CameraData.h"
|
||||
#include "Misc/CoreMiscDefines.h"
|
||||
#include "UGC_SpringArmComponentBase.generated.h"
|
||||
|
||||
/**
|
||||
* Custom SpringArm component with enhanced collision handling.
|
||||
*/
|
||||
UCLASS(Blueprintable, Abstract, ClassGroup = "UGC Camera", Category = "UGC|Components", meta = (BlueprintSpawnableComponent))
|
||||
class AURORADEVS_UGC_API UUGC_SpringArmComponentBase : public USpringArmComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
virtual void UpdateDesiredArmLocation(bool bDoTrace, bool bDoLocationLag, bool bDoRotationLag, float DeltaTime) override;
|
||||
virtual FVector BlendLocations(const FVector& DesiredArmLocation, const FVector& TraceHitLocation, bool bHitSomething, float DeltaTime) override;
|
||||
bool IsPlayerControlled() const;
|
||||
|
||||
protected:
|
||||
/** Camera collision settings including feelers */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CameraCollision, meta = (EditCondition = "bDoCollisionTest"))
|
||||
FCameraCollisionSettings CameraCollisionSettings;
|
||||
|
||||
/* *EXPERIMENTAL* Might cause bugs.
|
||||
* Whether we want the framing to stay the same during collisions. This is useful for games where you need to aim (bow, gun; etc.) since it
|
||||
* allows the center of the screen to not shift during collision.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CameraCollision)
|
||||
bool bMaintainFramingDuringCollisions = false;
|
||||
|
||||
/* Whether to draw debug messages regarding the spring arm collision.*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CameraCollision")
|
||||
bool bPrintCollisionDebug = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CameraCollision", meta = (EditCondition = "bPrintCollisionDebug"))
|
||||
bool bPrintHitActors = false;
|
||||
|
||||
protected:
|
||||
/** Runtime interpolated distance percentage (0 = fully blocked, 1 = clear) */
|
||||
float DistBlockedPct = 1.f;
|
||||
|
||||
// Debug-only: Track which actors were hit by feeler rays (not needed in shipping)
|
||||
#if WITH_EDITORONLY_DATA
|
||||
UPROPERTY(VisibleInstanceOnly, Transient, Category = "CameraCollision|Debug")
|
||||
TArray<class AActor*> HitActors;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,639 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Curves/CurveFloat.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "UGC_CameraData.generated.h"
|
||||
|
||||
/*
|
||||
* The settings of the camera yaw follow. This is useful for player who don't like to control the camera too much.
|
||||
* The YawFollowModifier in blueprint will adjust the yaw to face the movement direction (Used by AAA games like Hogwarts Legacy, Witcher, etc).
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraYawFollowSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Whether the yaw should follow the character, when they are moving.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Yaw Follow")
|
||||
bool bEnableYawMovementFollow = true;
|
||||
|
||||
// Whether the yaw threshold timer should be reset when the character stop moving.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Yaw Follow")
|
||||
bool bResetThresholdTimerWhenNoMovement = false;
|
||||
|
||||
// The speed at which the camera rotates its yaw in the movement direction of the character.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Yaw Follow")
|
||||
float YawFollowSpeed = 50.f;
|
||||
|
||||
/* The minimum time the player shouldn't rotate the camera manually before the yaw follow kicks in.
|
||||
* The timer can be reset when the character stops moving by enabling `bResetThresholdTimerWhenNoMovement`.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Yaw Follow")
|
||||
float YawFollowTimeThreshold = 2.f;
|
||||
|
||||
// Threshold yaw angle above which we trigger the yaw follow modifier.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Yaw Follow")
|
||||
float YawFollowAngleThreshold = 10.f;
|
||||
};
|
||||
|
||||
/*
|
||||
* The settings of the camera pitch follow. This is useful for player who don't like to control the camera too much.
|
||||
* The PitchFollowModifier in blueprint will adjust the pitch to face slopes, falling and reset the pitch if it's left untouched for long enough.
|
||||
* (Used by AAA games like Red Dead Redemption, Hogwarts Legacy, Witcher, etc)
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraPitchFollowSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Whether the pitch should reset to the resting pitch when the character is moving. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Pitch Follow")
|
||||
bool bEnablePitchMovementFollow = true;
|
||||
|
||||
/** The speed at which the camera rotates its pitch to follow when falling/moving. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Pitch Follow")
|
||||
float PitchFollowSpeed = 10.f;
|
||||
|
||||
/** The minimum time the player shouldn't rotate the camera manually before the pitch follow kicks in. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Pitch Follow")
|
||||
float PitchFollowTimeThreshold = 2.f;
|
||||
|
||||
/** Whether the pitch threshold timer should be reset when the character stop moving. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Pitch Follow")
|
||||
bool bResetThresholdTimerWhenNoMovement = false;
|
||||
|
||||
/** Threshold pitch angle above which we trigger the pitch follow modifier to reset the pitch to RestingPitch. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement Follow")
|
||||
float PitchFollowAngleThreshold = 5.f;
|
||||
|
||||
/** The pitch to go to when the the character is moving without controlling the camera's pitch is messed up. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement Follow")
|
||||
float RestingPitch = -10.f;
|
||||
|
||||
/** Should the camera look down when the character is falling for at least TimeThresholdWhenFalling in seconds and the floor is at least MinDistanceFromGround centimeters away ? */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Falling")
|
||||
bool bTriggerWhenFalling = true;
|
||||
|
||||
/** The minimun duration of time the character should fall before the modifier is triggered. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Falling", meta = (EditCondition = "bTriggerWhenFalling"))
|
||||
float PitchFollowTimeThresholdWhenFalling = 0.5f;
|
||||
|
||||
/** The minimum distance we should be above the ground to trigger the pitch follow when falling. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Falling", meta = (EditCondition = "bTriggerWhenFalling"))
|
||||
float MinDistanceFromGroundToTriggerWhenFalling = 500.f;
|
||||
|
||||
/** A multiplier to apply to the follow speed of the camera when we're feeling. */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Falling", meta = (EditCondition = "bTriggerWhenFalling"))
|
||||
float SpeedMultiplierWhenFalling = 6.f;
|
||||
|
||||
/** Should the camera pitch look toward the inclination of the slope the character is walking on? */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Slopes")
|
||||
bool bTriggerOnSlopes = true;
|
||||
|
||||
/** The minimum slope pitch inclination angle so that the pitch follow modifier is triggered (so that it's not triggered for small bumps). */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Slopes", meta = (EditCondition = "bTriggerOnSlopes"))
|
||||
float SlopeMinIncline = 25.f;
|
||||
|
||||
/** The weight of the pitch of the camera when the modifier is trying to follow a slope.
|
||||
* 1 means the camera pitch will match the slope angle exactly, 0.5 means half the angle; etc.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Slopes", meta = (MultiLine = "true", EditCondition = "bTriggerOnSlopes", UIMin = "0.1", ClampMin = "0.1", UIMax = "1", ClampMax = "1"))
|
||||
float SlopeFollowWeight = 1.f;
|
||||
};
|
||||
|
||||
/*
|
||||
* The settings of the camera Arm Offset.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraArmOffsetSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Offset at the end of the spring arm. Use this instead of the relative-space *rotation* so that the UE camera system works as expected. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Offset Modifier", meta = (MultiLine = "true"))
|
||||
FVector ArmSocketOffset = FVector(0.f, 40.f, 0.f);
|
||||
|
||||
/** How long does it take to blend to the current ArmSocketOffset. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Offset Modifier", meta = (MultiLine = "true", UIMin = "0", ClampMin = "0"))
|
||||
float ArmSocketOffsetBlendTime = 0.5f;
|
||||
|
||||
/** Controls the acceleration/deceleration of the blend. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermite if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Offset Modifier", meta = (MultiLine = "true"))
|
||||
TObjectPtr<UCurveFloat> ArmSocketOffsetBlendCurve;
|
||||
|
||||
/** Offset at start of spring, applied in world space. Use this if you want a world-space offset from the parent component instead of the usual relative-space location. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Offset Modifier", meta = (MultiLine = "true"))
|
||||
FVector ArmTargetOffset = FVector(0.f, 0.f, 58.f);
|
||||
|
||||
/** How long does it take to blend to the current ArmTargetOffset. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Offset Modifier", meta = (MultiLine = "true", UIMin = "0", ClampMin = "0"))
|
||||
float ArmTargetOffsetBlendTime = 0.5f;
|
||||
|
||||
/** Controls the acceleration/deceleration of the blend. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermite if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Offset Modifier", meta = (MultiLine = "true"))
|
||||
TObjectPtr<UCurveFloat> ArmTargetOffsetBlendCurve;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is used by the PitchToArmLengthAndFOV Camera Modifier in blueprint.
|
||||
* This makes the Arm Length and FOV change when the character is looking up/down (Used by many AAA games like all GTA games, Red Dead Redemption Assassin's Creed, Hogwarts Legacy, etc.).
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraPitchToArmAndFOVCurveSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Pitch to Arm and FOV Modifier", meta = (MultiLine = "true"))
|
||||
bool Enabled = true;
|
||||
|
||||
/** Curve with X and Y between -1.0 and 1.0. This maps the LocalMinPitch (X=-1.0) and LocalMaxPitch (X=1.0) to the MinArmLength and MaxArmLength. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Pitch to Arm and FOV Modifier", meta = (MultiLine = "true"))
|
||||
TObjectPtr<UCurveFloat> PitchToArmLengthCurve;
|
||||
|
||||
/** Curve with X and Y between -1.0 and 1.0. This maps the LocalMinPitch (X=-1.0) and LocalMaxPitch (X=1.0) to the MinFOV and MaxFOV. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Pitch to Arm and FOV Modifier", meta = (MultiLine = "true"))
|
||||
TObjectPtr<UCurveFloat> PitchToFOVCurve;
|
||||
};
|
||||
|
||||
/*
|
||||
* The settings of the camera Arm Length. Defines the range of the Arm Length and how to blend from one range to this one. This is used by the PitchToArmLengthAndFOV Camera Modifier in blueprint.
|
||||
* This makes the Arm Length change when the character is looking up/down (Used by many AAA games like all GTA games, Red Dead Redemption Assassin's Creed, Hogwarts Legacy, etc.).
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraArmLengthSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** The minimum arm length value. The length of the arm will change depending on the current pitch of the camera and the PitchToArmLengthCurve. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Length", meta = (MultiLine = "true"))
|
||||
float MinArmLength = 100.f;
|
||||
|
||||
/** The maximum arm length value. The length of the arm will change depending on the current pitch of the camera and the PitchToArmLengthCurve. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Length", meta = (MultiLine = "true"))
|
||||
float MaxArmLength = 250.f;
|
||||
|
||||
/** How long does it take to blend to the current Arm Length range. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Length", meta = (MultiLine = "true", UIMin = "0", ClampMin = "0"))
|
||||
float ArmRangeBlendTime = 0.5f;
|
||||
|
||||
/** Controls the acceleration/deceleration of the blend. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermite if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm Length", meta = (MultiLine = "true"))
|
||||
TObjectPtr<UCurveFloat> ArmRangeBlendCurve;
|
||||
};
|
||||
|
||||
/*
|
||||
* The settings of the camera FOV. Defines the range of the FOV and how to blend from one range to this one. This is used by the PitchToArmLengthAndFOV Camera Modifier in blueprint.
|
||||
* This makes the FOV change when the character is looking up/down (Used by many AAA games like GTA IV).
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraFOVSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** The minimum FOV value. The FOV of the camera will change depending on the current pitch of the camera and the PitchToFOVCurve. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera FOV", meta = (MultiLine = "true"))
|
||||
float MinFOV = 90.f;
|
||||
|
||||
/** The maximum FOV value. The FOV of the camera will change depending on the current pitch of the camera and the PitchToFOVCurve. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera FOV", meta = (MultiLine = "true"))
|
||||
float MaxFOV = 125.f;
|
||||
|
||||
/** A tolerance in degrees above which the Angle Constraints modifier will start decelerating the camera. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera FOV", meta = (MultiLine = "true", UIMin = "0", ClampMin = "0"))
|
||||
float FOVRangeBlendTime = 0.5f;
|
||||
|
||||
/** Controls the acceleration/deceleration of the blend. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermite if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera FOV", meta = (MultiLine = "true"))
|
||||
TObjectPtr<UCurveFloat> FOVRangeBlendCurve;
|
||||
};
|
||||
|
||||
/*
|
||||
* The settings of the camera yaw contraints. This is useful to limit how much the character can look left/right.
|
||||
* The looking input action needs to have a UGC_CameraTurnRateModifier for the camera to decelerate when close to the constraints.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraYawConstraintSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Whether the yaw should be constrained to LocalMinYaw and LocalMaxYaw. If the yaw was already out of the new range, it will blend into range using the BlendTime and BlendCurve. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true"))
|
||||
bool bConstrainYaw = false;
|
||||
|
||||
/** How close in degrees to the YawMin or YawMax should the camera start decelerating? */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true", UIMin = "0", ClampMin = "0"))
|
||||
float YawConstraintTolerance = 10.f;
|
||||
|
||||
/** How much can the character look right (angle in degrees). */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true"))
|
||||
float LocalMinYaw = 0.f;
|
||||
|
||||
/** How much can the character look left (angle in degrees). */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true"))
|
||||
float LocalMaxYaw = 359.998993f;
|
||||
|
||||
/** How fast should we blend from one Yaw constraint range to this one. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true", UIMin = "0", ClampMin = "0"))
|
||||
float YawConstraintsBlendTime = 0.5f;
|
||||
|
||||
/** Controls the acceleration/deceleration of the blend. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermite if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true"))
|
||||
TObjectPtr<UCurveFloat> YawConstraintsBlendCurve;
|
||||
};
|
||||
|
||||
/*
|
||||
* The settings of the camera pitch contraints. This is useful to limit how much the character can look up/down. AAA games usually do not allow the entire 180 degrees range.
|
||||
* The looking input action needs to have a UGC_CameraTurnRateModifier for the camera to decelerate when close to the constraints.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraPitchConstraintSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Whether the pitch should be constrained to LocalMinPitch and LocalMaxPitch. If the yaw was already out of the new range, it will blend into range using the BlendTime and BlendCurve. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true"))
|
||||
bool bConstrainPitch = false;
|
||||
|
||||
/** How close in degrees to the PitchMin or PitchMax should the camera start decelerating? */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true", UIMin = "0", ClampMin = "0"))
|
||||
float PitchConstraintTolerance = 10.f;
|
||||
|
||||
// How much can the character look down (angle in degrees).
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true"))
|
||||
float LocalMinPitch = -89.99f;
|
||||
|
||||
// How much can the character look up (angle in degrees).
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true"))
|
||||
float LocalMaxPitch = 89.99f;
|
||||
|
||||
/** How fast should we blend from one Pitch constraint range to this one. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true", UIMin = "0", ClampMin = "0"))
|
||||
float PitchConstraintsBlendTime = 0.5f;
|
||||
|
||||
/** Controls the acceleration/deceleration of the blend. The curve has to be normalized (going from 0 to 1). Leave empty or use Hermite if unsure. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints", meta = (MultiLine = "true"))
|
||||
TObjectPtr<UCurveFloat> PitchConstraintsBlendCurve;
|
||||
};
|
||||
|
||||
/*
|
||||
* The settings of the focus camera. This is used for hard-lock in games.
|
||||
* Has a function which retrieves the target we want the camera to look at. (Uses the Strategy Design Pattern)
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FUGCCameraFocusSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Whether the focus camera is enabled. If it is, this will use the Focus Target Method to get the target's location.*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", meta = (MultiLine = "true"))
|
||||
bool bEnabled = false;
|
||||
|
||||
/** How fast the camera will focus the target.*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", meta = (MultiLine = "true"))
|
||||
float InterpSpeed = 10.f;
|
||||
|
||||
/** An offset in degrees applied on the pitch and yaw. This is useful if you want the focused location to always be on the left/right or looked down/up.*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", meta = (MultiLine = "true"))
|
||||
FRotator RotationOffset = FRotator::ZeroRotator;
|
||||
|
||||
/** Whether the camera input should be ignored during focus.*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", meta = (MultiLine = "true"))
|
||||
bool bIgnoreCameraInput = false;
|
||||
|
||||
|
||||
/** Whether we should rotatet only the yaw angle of the camera or the pitch as well.
|
||||
* You can set this to true and combine it with the Rotation Offset if you want the cam to stay at a specific pitch. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", meta = (MultiLine = "true"))
|
||||
bool bRotateYawOnly = false;
|
||||
|
||||
/** Whether we should stop focusing if the Line of Sight is occluded.*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", meta = (MultiLine = "true"))
|
||||
bool bStopIfBlockedLOS = false;
|
||||
|
||||
/** How long should the line of sight be occluded before we stop focusing trying to focus target.*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", meta = (MultiLine = "true", EditCondition = "bStopIfBlockedLOS"))
|
||||
float BlockedLOSTimeThreshold = 3.f;
|
||||
|
||||
/** The distance from the target below which we will stop focusing the target. If this is too low, the camera might start rotating crazily around the target.*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", meta = (MultiLine = "true"))
|
||||
float MinDistanceFromTarget = 100.f;
|
||||
|
||||
/** The distance from the target above which we will stop focusing the target.*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", meta = (MultiLine = "true"))
|
||||
float MaxDistanceFromTarget = 1000.f;
|
||||
|
||||
/** Function which retrieves the target we want the camera to look at. (Uses the Strategy Design Pattern) */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Focus", Instanced, meta = (MultiLine = "true"))
|
||||
TObjectPtr<class UUGC_IFocusTargetMethod> FocusTargetMethod;
|
||||
};
|
||||
|
||||
/*
|
||||
* The settings of a camera modifier which needs to retrieve a list of actors.
|
||||
* Has a function which retrieves the all actors relevant for some camera modifier/other object with settings inheriting from this class. (Uses the Strategy Design Pattern)
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FUGCCameraSettingsWithGetActorsMethod
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Function which retrieves all relevant actors. (Uses the Strategy Design Pattern) */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Targets", Instanced, meta = (MultiLine = "true"))
|
||||
TObjectPtr<class UUGC_IGetActorsMethod> GetActorsMethod;
|
||||
};
|
||||
|
||||
/*
|
||||
* The settings of a camera modifier or other object which needs to retrieve one actor location.
|
||||
* Has a function which an actor and its location using the instanced method. (Uses the Strategy Design Pattern)
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FUGCCameraSettingsWithGetActorLocationMethod
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Function which retrieves the relevant actor and their position. (Uses the Strategy Design Pattern) */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Target", Instanced, meta = (MultiLine = "true"))
|
||||
TObjectPtr<class UUGC_IFocusTargetMethod> GetActorLocationMethod;
|
||||
};
|
||||
|
||||
/*
|
||||
* Dithering settings used to dither/hide/fade objects colliding with the camera either occluding the line of sight to the player or overlapping the camera directly.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraDitheringSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// The name of the scalar material parameter to blend in/out.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering")
|
||||
FName MaterialOpacityParameterName = "Opacity";
|
||||
|
||||
// Whether the material has a vector parameter which should be updated to the player location.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering")
|
||||
bool bUpdateMaterialPlayerPosition = false;
|
||||
|
||||
// The name of the vector parameter to set to the player position inside the Material Parameter Collection.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering", meta = (EditCondition = "bUpdateMaterialPlayerPosition"))
|
||||
FName MaterialPlayerPositionParameterName = "PlayerLocation";
|
||||
|
||||
// The minimum value of the material's Opacity when dithered.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering", meta = (UIMin = 0.f, UIMax = 1.f, ClampMin = 0.f, ClampMax = 1.f))
|
||||
float MaterialDitherMinimum = 0.1f;
|
||||
|
||||
// Controls the speed of the blend when starting to dither an object.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering", meta = (UIMin = 0.f, ClampMin = 0.f))
|
||||
float DitherInSpeed = 10.f;
|
||||
|
||||
// Controls the speed of the blend when finishing to dither an object.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering", meta = (UIMin = 0.f, ClampMin = 0.f))
|
||||
float DitherOutSpeed = 10.f;
|
||||
|
||||
// Actors with this tag will not be dithered even if they overlap DitherLOSChannel and DitherOverlapChannel.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering")
|
||||
FName IgnoreDitheringTag = "UGC_IgnoreDithering";
|
||||
|
||||
// Whether we should dither the child actors attached to the obstacle.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions")
|
||||
bool bDitherChildActors = true;
|
||||
|
||||
// Whether we should dither the components attached to the obstacle.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions")
|
||||
bool bDitherAttachedComponents = true;
|
||||
|
||||
// Whether we should dither objects that block the LINE OF SIGHT of the camera.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions|Line Of Sight")
|
||||
bool bDitherLineOfSight = false;
|
||||
|
||||
// The collision channel to use when checking for what the LINE OF SIGHT of the camera is colliding with. The other objects have to Overlap this channel in order to be dithered!
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions|Line Of Sight", meta = (EditCondition = "bDitherLineOfSight"))
|
||||
TEnumAsByte<ECollisionChannel> DitherLOSChannel = ECC_Camera;
|
||||
|
||||
// The width of the probe when testing if any object is blocking the LINE OF SIGHT from the player to the camera. The other objects have to Overlap this channel in order to be dithered!
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions|Line Of Sight", meta = (UIMin = 0.f, ClampMin = 0.f, EditCondition = "bDitherLineOfSight"))
|
||||
float LOSProbeSize = 5.f;
|
||||
|
||||
// The minimum amount of time the camera LINE OF SIGHT has to collide with an object for it to be dithered.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions|Line Of Sight", meta = (UIMin = 0.f, ClampMin = 0.f, EditCondition = "bDitherLineOfSight"))
|
||||
float CollisionTimeThreshold = 0.f;
|
||||
|
||||
// Whether we should dither objects that OVERLAP the actual camera component.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions|Overlaps")
|
||||
bool bDitherOverlaps = true;
|
||||
|
||||
// Whether we should dither the owner character when the actual camera overlaps with them.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions|Overlaps", meta = (EditCondition = "bDitherOverlaps"))
|
||||
bool bDitherOwner = true;
|
||||
|
||||
// The collision channel to use when checking for what the actual camera is overlapping with. The other objects have to Overlap this channel in order to be dithered!
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions|Overlaps", meta = (EditCondition = "bDitherOverlaps"))
|
||||
TEnumAsByte<ECollisionChannel> DitherOverlapChannel = ECC_Camera;
|
||||
|
||||
// The radius around the camera where we check for collisions.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC Camera Dithering|Collisions|Overlaps", meta = (UIMin = 0.f, ClampMin = 0.f, EditCondition = "bDitherOverlaps"))
|
||||
float SphereCollisionRadius = 15.f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct defining a feeler ray used for camera penetration avoidance. The feeler uses sphere sweeps.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FPenetrationAvoidanceFeeler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
FPenetrationAvoidanceFeeler();
|
||||
|
||||
FPenetrationAvoidanceFeeler(const FRotator& InAdjustmentRot, const float& InWorldWeight, const float& InPawnWeight, const float& InExtent);
|
||||
|
||||
// FRotator describing deviance from main ray.
|
||||
UPROPERTY(EditAnywhere, Category = PenetrationAvoidanceFeeler)
|
||||
FRotator AdjustmentRot;
|
||||
|
||||
// How much this feeler affects the final position if it hits the world.
|
||||
UPROPERTY(EditAnywhere, Category = PenetrationAvoidanceFeeler, meta = (UIMin = 0.f, UIMax = 1.f, ClampMin = 0.f, ClampMax = 1.f))
|
||||
float WorldWeight = 0.f;
|
||||
|
||||
// How much this feeler affects the final position if it hits a APawn (setting to 0 will not attempt to collide with pawns at all).
|
||||
UPROPERTY(EditAnywhere, Category = PenetrationAvoidanceFeeler, meta = (UIMin = 0.f, UIMax = 1.f, ClampMin = 0.f, ClampMax = 1.f))
|
||||
float PawnWeight = 0.f;
|
||||
|
||||
// The radius of this feeler probe.
|
||||
UPROPERTY(EditAnywhere, Category = PenetrationAvoidanceFeeler)
|
||||
float ProbeRadius = 5.f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Camera collision settings which define how the camera avoidance uses collision feelers or whiskers to avoid camera clipping through obstacles.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraCollisionSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
FCameraCollisionSettings();
|
||||
|
||||
/* The time the camera takes to go to the safe location after a collision has been detected. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collision")
|
||||
float PenetrationBlendInTime = 0.05f;
|
||||
|
||||
/* The time the camera takes to go back to its normal position after the collision has finished. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collision")
|
||||
float PenetrationBlendOutTime = 0.5f;
|
||||
|
||||
// If true, does collision checks to keep the camera out of the world.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collision")
|
||||
bool bPreventPenetration = true;
|
||||
|
||||
// If true, try to detect nearby walls and move the camera in anticipation. Helps prevent popping.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collision", meta = (EditCondition = "bPreventPenetration"))
|
||||
bool bDoPredictiveAvoidance = true;
|
||||
|
||||
/**
|
||||
* These are the feeler rays that are used to find where to place the camera.
|
||||
* Index: 0 : This is the normal feeler we use to prevent collisions.
|
||||
* Index: 1+ : These feelers are used if you bDoPredictiveAvoidance=true, to scan for potential impacts if the player
|
||||
* were to rotate towards that direction and primitively collide the camera so that it pulls in before
|
||||
* impacting the occluder.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collision")
|
||||
TArray<FPenetrationAvoidanceFeeler> PenetrationAvoidanceFeelers;
|
||||
|
||||
// Actors with this tag will ignore camera collisions.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collision")
|
||||
FName IgnoreCameraCollisionTag = "UGC_IgnoreCameraCollision";
|
||||
};
|
||||
|
||||
/**
|
||||
* Camera settings for the spring arm component.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCameraArmLagSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
/**
|
||||
* If true, these settings will be used and the settings authored directly in the spring arm will be ignored.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Lag")
|
||||
bool bOverrideSpringArmComponentSettings = false;
|
||||
|
||||
/**
|
||||
* If true, camera lags behind target position to smooth its movement.
|
||||
* @see CameraLagSpeed
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Lag", meta = (editcondition = "bOverrideSpringArmComponentSettings"))
|
||||
bool bEnableCameraLag = true;
|
||||
|
||||
/**
|
||||
* If true, camera lags behind target rotation to smooth its movement.
|
||||
* @see CameraRotationLagSpeed
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Lag", meta = (editcondition = "bOverrideSpringArmComponentSettings"))
|
||||
bool bEnableCameraRotationLag = false;
|
||||
|
||||
/** If bEnableCameraLag is true, controls how quickly camera reaches target position. Low values are slower (more lag), high values are faster (less lag), while zero is instant (no lag). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Lag", meta = (editcondition = "bEnableCameraLag && bOverrideSpringArmComponentSettings", ClampMin = "0.0", ClampMax = "1000.0", UIMin = "0.0", UIMax = "1000.0"))
|
||||
float CameraLagSpeed = 8.f;
|
||||
|
||||
/** If bEnableCameraRotationLag is true, controls how quickly camera reaches target position. Low values are slower (more lag), high values are faster (less lag), while zero is instant (no lag). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Lag", meta = (editcondition = "bEnableCameraRotationLag && bOverrideSpringArmComponentSettings", ClampMin = "0.0", ClampMax = "1000.0", UIMin = "0.0", UIMax = "1000.0"))
|
||||
float CameraRotationLagSpeed = 10.f;
|
||||
|
||||
/** Max distance the camera target may lag behind the current location. If set to zero, no max distance is enforced. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Lag", meta = (editcondition = "bEnableCameraLag && bOverrideSpringArmComponentSettings", ClampMin = "0.0", UIMin = "0.0"))
|
||||
float CameraLagMaxDistance = 0.f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic data class which holds settings for an associated Camera Add-On Modifier.
|
||||
*/
|
||||
UCLASS(abstract, Category = "UGC|Add On|Settings", EditInlineNew, Blueprintable)
|
||||
class AURORADEVS_UGC_API UUGC_CameraAddOnModifierSettings : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
};
|
||||
|
||||
/** Camera Data Asset holding all of the camera settings. */
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class UUGC_CameraDataAssetBase : public UPrimaryDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
/**
|
||||
* The settings of the camera Arm Length. Defines the range of the Arm Length and how to blend from one range to this one. This is used by the PitchToArmLengthAndFOV Camera Modifier in blueprint.
|
||||
* This makes the Arm Length change when the character is looking up/down (Used by many AAA games like all GTA games, Red Dead Redemption Assassin's Creed, Hogwarts Legacy, etc.).
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm")
|
||||
FCameraArmLengthSettings ArmLengthSettings;
|
||||
|
||||
/**
|
||||
* The settings of the camera FOV. Defines the range of the FOV and how to blend from one range to this one. This is used by the PitchToArmLengthAndFOV Camera Modifier in blueprint.
|
||||
* This makes the FOV change when the character is looking up/down (Used by many AAA games like GTA IV).
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera FOV")
|
||||
FCameraFOVSettings FOVSettings;
|
||||
|
||||
/** Camera collision settings which define how the camera avoidance uses collision feelers or whiskers to avoid camera clipping through obstacles. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "CameraCollisions")
|
||||
FCameraCollisionSettings CollisionSettings;
|
||||
|
||||
/** Dithering settings used to dither/hide/fade objects colliding with the camera either occluding the line of sight to the player or overlapping the camera directly. */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Dithering")
|
||||
FCameraDitheringSettings DitheringSettings;
|
||||
|
||||
/**
|
||||
* The settings of the camera pitch follow. This is useful for player who don't like to control the camera too much.
|
||||
* The PitchFollowModifier in blueprint will adjust the pitch to face slopes, falling and reset the pitch if it's left untouched for long enough.
|
||||
* (Used by AAA games like Red Dead Redemption, Hogwarts Legacy, Witcher, etc)
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Movement Follow|Pitch")
|
||||
FCameraPitchFollowSettings PitchFollowSettings;
|
||||
|
||||
/**
|
||||
* The settings of the camera yaw follow. This is useful for player who don't like to control the camera too much.
|
||||
* The YawFollowModifier in blueprint will adjust the yaw to face the movement direction (Used by AAA games like Hogwarts Legacy, Witcher, etc).
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Movement Follow|Yaw")
|
||||
FCameraYawFollowSettings YawFollowSettings;
|
||||
|
||||
/** The settings of the camera Spring Arm Offset. Offset at the end of the spring arm. Use this instead of the relative world location so that the UE camera system works as exptected */
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Arm")
|
||||
FCameraArmOffsetSettings ArmOffsetSettings;
|
||||
|
||||
/**
|
||||
* The settings of the camera pitch contraints. This is useful to limit how much the character can look up/down. AAA games usually do not allow the entire 180 degrees range.
|
||||
* The looking input action needs to have a UGC_CameraSlowDownInputModifier for the camera to decelerate when close to the constraints.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints")
|
||||
FCameraPitchConstraintSettings PitchConstraints;
|
||||
|
||||
/**
|
||||
* The settings of the camera yaw contraints. This is useful to limit how much the character can look left/right.
|
||||
* The looking input action needs to have a UGC_CameraSlowDownInputModifier for the camera to decelerate when close to the constraints.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Camera Angle Constraints")
|
||||
FCameraYawConstraintSettings YawConstraints;
|
||||
|
||||
/**
|
||||
* The settings of the focus camera. This is used for hard-lock in games.
|
||||
* Has a function which retrieves the target we want the camera to look at. (Uses the Strategy Design Pattern)
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "CameraFocus")
|
||||
FUGCCameraFocusSettings FocusSettings;
|
||||
|
||||
/** This makes the Arm Length and FOV change when the character is looking up/down (Used by many AAA games like all GTA games, Red Dead Redemption Assassin's Creed, Hogwarts Legacy, etc.). */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "PitchToArmLengthAndFOVCurv")
|
||||
FCameraPitchToArmAndFOVCurveSettings PitchToArmAndFOVCurveSettings;
|
||||
|
||||
/** Arm lag settings of the camera. Need to be enabled by checking `bOverrideSpringArmComponentSettings`, otherwise the settings of the component are used instead. **/
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Camera Arm")
|
||||
FCameraArmLagSettings ArmLagSettings;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Add Ons", Instanced, meta = (MultiLine = "true"))
|
||||
TArray<TObjectPtr<UUGC_CameraAddOnModifierSettings>> AddOnsSettings;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "UObject/WeakObjectPtrTemplates.h"
|
||||
|
||||
#include "UGC_IFocusTargetMethod.generated.h"
|
||||
|
||||
/**
|
||||
* Function which retrieves the target we want the camera to look at. (Uses the Strategy Design Pattern)
|
||||
*/
|
||||
UCLASS(abstract, Category = "UGC|Methods", EditInlineNew, Blueprintable)
|
||||
class AURORADEVS_UGC_API UUGC_IFocusTargetMethod : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
/*
|
||||
* Get the location of the target we want the camera to look at.
|
||||
* @param Owner The owner of the camera.
|
||||
* @param OwnerLocation The world location of the owner of camera.
|
||||
* @param ViewPointLocation Camera's location.
|
||||
* @param ViewPointRotation Camera's rotation.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "UGC|Methods")
|
||||
AActor* GetTargetLocation(class AActor* InOwner, FVector OwnerLocation, FVector ViewPointLocation, FRotator ViewPointRotation, FVector& OutTargetLocation);
|
||||
|
||||
private:
|
||||
/** Getter for the cached world pointer, will return null if the actor is not actually spawned in a level */
|
||||
virtual UWorld* GetWorld() const override;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "UObject/WeakObjectPtrTemplates.h"
|
||||
|
||||
#include "UGC_IGetActorsMethod.generated.h"
|
||||
|
||||
/**
|
||||
* Function which retrieves a vector of actors. (Uses the Strategy Design Pattern)
|
||||
*/
|
||||
UCLASS(abstract, Category = "UGC|Methods", EditInlineNew, Blueprintable)
|
||||
class AURORADEVS_UGC_API UUGC_IGetActorsMethod : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
/*
|
||||
* Get the all actors relevant for this method.
|
||||
* @param Owner The owner of the camera.
|
||||
* @param OwnerLocation The world location of the owner of camera.
|
||||
* @param ViewPointLocation Camera's location.
|
||||
* @param ViewPointRotation Camera's rotation.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "UGC|Methods")
|
||||
void GetActors(class AActor* InOwner, FVector OwnerLocation, FVector ViewPointLocation, FRotator ViewPointRotation, TArray<AActor*>& OutActors);
|
||||
|
||||
private:
|
||||
/** Getter for the cached world pointer, will return null if the actor is not actually spawned in a level */
|
||||
virtual UWorld* GetWorld() const override;
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CameraAnimationCameraModifier.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "UGC_CameraAnimationModifier.generated.h"
|
||||
|
||||
/**
|
||||
* Delegate for when a UGC Camera Animation is completed, whether the animation has been interrupted or finished.
|
||||
*
|
||||
* bInterrupted = true if it was not property finished
|
||||
*/
|
||||
DECLARE_DELEGATE_TwoParams(FOnCameraAnimationEnded, class UCameraAnimationSequence*, bool /*bInterrupted*/)
|
||||
|
||||
/**
|
||||
* Delegate for when a UGC Camera Animation started easing out, whether the animation has actually been interrupted or not.
|
||||
*
|
||||
* bInterrupted = true if it was not property finished
|
||||
*/
|
||||
DECLARE_DELEGATE_OneParam(FOnCameraAnimationEaseOutStarted, class UCameraAnimationSequence*)
|
||||
|
||||
UENUM()
|
||||
enum class ECameraAnimationResetType : uint8
|
||||
{
|
||||
BackToStart UMETA(ToolTip = "The camera will go back to the position it started from."),
|
||||
ResetToZero UMETA(ToolTip = "The camera's orientation will be reset to zero. This is usually the back of the character. If UseControllerRotationYaw is true, this is forcefully used."),
|
||||
ContinueFromEnd UMETA(ToolTip = "The camera will blend out from the last position of the animation.")
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct FUGCActiveAnimationInfo
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
ECameraAnimationResetType ResetType = ECameraAnimationResetType::ResetToZero;
|
||||
/** Runtime interpolated distance percentage (0 = fully blocked, 1 = clear) */
|
||||
float DistBlockedPct = 1.f;
|
||||
bool bDoCollisionChecks = false;
|
||||
bool bWasEasingOut = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gameplay Camera Animation Modifier which plays in the correct transform space in rgeards to the owning player.
|
||||
*/
|
||||
UCLASS(abstract, ClassGroup = "UGC Camera Modifiers")
|
||||
class AURORADEVS_UGC_API UUGC_CameraAnimationModifier : public UCameraAnimationCameraModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// UCameraModifier interface
|
||||
virtual bool ModifyCamera(float DeltaTime, FMinimalViewInfo& InOutPOV) override;
|
||||
|
||||
void CameraAnimation_SetEasingOutDelegate(FOnCameraAnimationEaseOutStarted& InOnAnimationEaseOutStarted, FCameraAnimationHandle AnimationHandle);
|
||||
void CameraAnimation_SetEndedDelegate(FOnCameraAnimationEnded& InOnAnimationEnded, FCameraAnimationHandle AnimationHandle);
|
||||
|
||||
FCameraAnimationHandle PlaySingleCameraAnimation(UCameraAnimationSequence* Sequence, FCameraAnimationParams Params, ECameraAnimationResetType ResetType, bool bInterruptOthers, bool bDoCollisionChecks);
|
||||
|
||||
/**
|
||||
* Stops the given camera animation sequence. If nullptr, will stop whatever is currently active.
|
||||
* @param Sequence The camera sequence animation.
|
||||
* @param bImmediate True to stop it right now and ignore blend out, false to let it blend out as indicated.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Modifiers|Camera Animation")
|
||||
void StopCameraAnimationSequence(UCameraAnimationSequence* Sequence, bool bImmediate = false);
|
||||
|
||||
/**
|
||||
* Get the current camera animation playing on this modifier.
|
||||
* @return The current camera animation playing.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Modifiers|Camera Animations")
|
||||
void GetCurrentCameraAnimations(TArray<UCameraAnimationSequence*>& OutAnimations) const;
|
||||
|
||||
/**
|
||||
* Returns whether the given camera animation is playing on this modifier.
|
||||
* @param Sequence The Camera Animation Sequence.
|
||||
* @return Whether the corresponding camera animation is playing or not.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Modifiers|Camera Animations")
|
||||
bool IsCameraAnimationSequenceActive(UCameraAnimationSequence* Sequence) const;
|
||||
|
||||
/**
|
||||
* Returns whether any camera animation is playing on this modifier.
|
||||
* @param Sequence The Camera Animation Sequence.
|
||||
* @return Whether any camera animation is playing or not.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Modifiers|Camera Animations")
|
||||
bool IsAnyCameraAnimationSequence() const;
|
||||
|
||||
protected:
|
||||
void UGCDeactivateCameraAnimation(FActiveCameraAnimationInfo& ActiveAnimation);
|
||||
|
||||
// UCameraAnimationCameraModifier interface
|
||||
virtual void UGCTickActiveAnimation(float DeltaTime, FMinimalViewInfo& InOutPOV);
|
||||
|
||||
// UCameraAnimationCameraModifier interface
|
||||
virtual void UGCTickAnimation(FActiveCameraAnimationInfo& CameraAnimation, float DeltaTime, FMinimalViewInfo& InOutPOV, int Index);
|
||||
virtual void UGCTickAnimCollision(FActiveCameraAnimationInfo& CameraAnimation, float DeltaTime, FMinimalViewInfo& InOutPOV, int Index);
|
||||
|
||||
FVector GetTraceSafeLocation(FMinimalViewInfo const& InPOV);
|
||||
|
||||
template<typename T>
|
||||
T* GetViewTargetAs() const
|
||||
{
|
||||
return Cast<T>(GetViewTarget());
|
||||
}
|
||||
|
||||
private:
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
void UGCDebugAnimation(FActiveCameraAnimationInfo& ActiveAnimation, float DeltaTime);
|
||||
#endif
|
||||
|
||||
private:
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<class AUGC_PlayerCameraManager> UGCCameraManager = nullptr;
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<class UUGC_CameraCollisionModifier> CollisionModifier = nullptr;
|
||||
|
||||
/** How should the camera behave after the current animation is over. */
|
||||
UPROPERTY(Transient)
|
||||
TArray<FUGCActiveAnimationInfo> UGCAnimInfo;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
int LastIndex = 0;
|
||||
|
||||
// Delegates
|
||||
FOnCameraAnimationEnded OnAnimationEnded;
|
||||
FOnCameraAnimationEaseOutStarted OnAnimationEaseOutStarted;
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "UGC_CameraModifier.h"
|
||||
#include "Camera/Data/UGC_CameraData.h"
|
||||
#include "Camera/CameraTypes.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "Containers/StaticBitArray.h"
|
||||
#include "UGC_CameraCollisionModifier.generated.h"
|
||||
|
||||
/**
|
||||
* DEPRECATED. USE UGC_SpringArmComponent INSTEAD.
|
||||
* Camera Modifier which does camera avoidance using predictive collision feelers.
|
||||
*/
|
||||
UCLASS(abstract, ClassGroup = "UGC Camera Modifiers", meta = (Deprecated = 5.5))
|
||||
class AURORADEVS_UGC_API UUGC_CameraCollisionModifier : public UUGC_CameraModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UUGC_CameraCollisionModifier();
|
||||
|
||||
public:
|
||||
// Force collision modifier to use a single ray by another modifier. Do not use this if you're not familiar with it.
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Modifiers|Collision")
|
||||
void AddSingleRayOverrider(UCameraModifier const* OverridingModifier) { if (OverridingModifier) SingleRayOverriders.AddUnique(OverridingModifier); }
|
||||
|
||||
// Remove single ray modifier override. Do not use this if you're not familiar with it.
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Modifiers|Collision")
|
||||
void RemoveSingleRayOverrider(UCameraModifier const* OverridingModifier) { if (OverridingModifier) SingleRayOverriders.Remove(OverridingModifier); }
|
||||
|
||||
protected:
|
||||
virtual bool ModifyCamera(float DeltaTime, FMinimalViewInfo& InOutPOV) override;
|
||||
|
||||
void UpdatePreventPenetration(float DeltaTime, FMinimalViewInfo& InOutPOV);
|
||||
|
||||
void PreventCameraPenetration(class AActor const& ViewTarget, FVector const& SafeLoc, FVector& OutCameraLoc, float const& DeltaTime, float& OutDistBlockedPct, bool bSingleRayOnly);
|
||||
|
||||
FVector GetTraceSafeLocation(FMinimalViewInfo const& POV);
|
||||
|
||||
void ResetSingleRayOverriders() { SingleRayOverriders.Reset(); }
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC|Modifiers|Collision")
|
||||
FCameraCollisionSettings CollisionSettings;
|
||||
|
||||
// If you don't want the camera to start close to the character and smoothly pan out once your character is spawned, default-initialize this variable to 1.f.
|
||||
UPROPERTY(Transient)
|
||||
float AimLineToDesiredPosBlockedPct = 1.f;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TArray<TObjectPtr<const AActor>> DebugActorsHitDuringCameraPenetration;
|
||||
|
||||
protected:
|
||||
TArray<UCameraModifier const*> SingleRayOverriders;
|
||||
TStaticBitArray<128u> CollidingFeelers;
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
mutable float LastDrawDebugTime = -MAX_FLT;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Camera/Modifiers/UGC_CameraModifier.h"
|
||||
#include "Camera/Data/UGC_CameraData.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "UGC_CameraDitheringModifier.generated.h"
|
||||
|
||||
UENUM()
|
||||
enum class EDitherType : uint8
|
||||
{
|
||||
None,
|
||||
BlockingLOS,
|
||||
OverlappingCamera
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FDitheredActorState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC|Modifiers|Camera Dithering")
|
||||
TObjectPtr<AActor> Actor;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "UGC|Modifiers|Camera Dithering")
|
||||
float CurrentOpacity = 1.f;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "UGC|Modifiers|Camera Dithering")
|
||||
float CollisionTime = 0.f;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "UGC|Modifiers|Camera Dithering")
|
||||
bool bIsDitheringIn = false;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "UGC|Modifiers|Camera Dithering")
|
||||
bool bIsDitheringOut = false;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "UGC|Modifiers|Camera Dithering")
|
||||
EDitherType DitherType = EDitherType::None;
|
||||
|
||||
bool IsValid() const { return Actor != nullptr && DitherType != EDitherType::None; }
|
||||
|
||||
void StartDithering(AActor* InActor, EDitherType InDitherType);
|
||||
|
||||
void Invalidate();
|
||||
|
||||
friend bool operator==(FDitheredActorState const& lhs, FDitheredActorState const& rhs)
|
||||
{
|
||||
return lhs.Actor == rhs.Actor;
|
||||
}
|
||||
|
||||
void ComputeOpacity(float DeltaTime, float DitherInTime, float DitherOutTime, float DitherMin);
|
||||
};
|
||||
|
||||
/**
|
||||
* UGC Camera Modifier used to dither objects colliding with the camera
|
||||
*/
|
||||
UCLASS()
|
||||
class AURORADEVS_UGC_API UUGC_CameraDitheringModifier : public UUGC_CameraModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UUGC_CameraDitheringModifier();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "UGC|Modifiers|Dithering")
|
||||
void ResetDitheredActors();
|
||||
|
||||
protected:
|
||||
virtual bool ModifyCamera(float DeltaTime, FMinimalViewInfo& InOutPOV) override;
|
||||
|
||||
virtual void ApplyDithering(float DeltaTime, FDitheredActorState& DitherState);
|
||||
|
||||
private:
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
void UGCDebugDithering(FDitheredActorState& DitherState, float DeltaTime, float DitherMin);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC|Modifiers|Dithering")
|
||||
FCameraDitheringSettings DitheringSettings;
|
||||
|
||||
/** Material Parameter Collection for everything dithering-related */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UGC|Modifiers|Dithering")
|
||||
TSoftObjectPtr<class UMaterialParameterCollection> DitheringMPC;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TSoftObjectPtr<class UMaterialParameterCollectionInstance> DitheringMPCInstance;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TArray<FDitheredActorState> DitheredActorStates;
|
||||
};
|
||||
@@ -0,0 +1,233 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Camera/CameraModifier.h"
|
||||
#include "UGC_CameraModifier.generated.h"
|
||||
|
||||
/**
|
||||
* Base Camera Modifier Class
|
||||
*/
|
||||
UCLASS(abstract, ClassGroup = "UGC|Camera Modifiers")
|
||||
class AURORADEVS_UGC_API UUGC_CameraModifier : public UCameraModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void EnableModifier() override;
|
||||
virtual void DisableModifier(bool bImmediate) override;
|
||||
bool GetDebugEnabled() const { return bDebug; }
|
||||
|
||||
/**
|
||||
* Function called once this modifier gets enabled.
|
||||
* @param LastPOV - the last view POV of the camera.
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
void OnModifierEnabled(FMinimalViewInfo const& LastPOV);
|
||||
|
||||
/**
|
||||
* Function called once this modifier gets disabled.
|
||||
* @param bWasImmediate - true if modifier was disabled without a blend out.
|
||||
* @param LastPOV - the last view POV of the camera.
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
void OnModifierDisabled(FMinimalViewInfo const& LastPOV, bool bWasImmediate);
|
||||
|
||||
/**
|
||||
* Called to give modifiers a chance to adjust view rotation updates before they are applied.
|
||||
*
|
||||
* Default just returns ViewRotation unchanged
|
||||
* @param ViewTarget - Current view target.
|
||||
* @param InLocalControlRotation - The difference between the actor rotation and the control rotation.
|
||||
* @param DeltaTime - Frame time in seconds.
|
||||
* @param InViewLocation - In. The view location of the camera.
|
||||
* @param InViewRotation - In. The view rotation of the camera.
|
||||
* @param InDeltaRot - In/out. How much the rotation changed this frame.
|
||||
* @param OutDeltaRot - Out. How much the control rotation should change this frame.
|
||||
* @return Return true to prevent subsequent (lower priority) modifiers to further adjust rotation, false otherwise.
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
bool ProcessControlRotation(AActor* ViewTarget, float DeltaTime, FVector InViewLocation, FRotator InViewRotation, FRotator InLocalControlRotation, FRotator InDeltaRot, FRotator& OutDeltaRot);
|
||||
|
||||
/**
|
||||
* Called to give modifiers a chance to adjust arm length and FOV before they are applied.
|
||||
*
|
||||
* @param DeltaTime - Frame time in seconds.
|
||||
* @param InFOV - The Current FOV of the camera.
|
||||
* @param InArmLength - The Current Arm Length of the camera.
|
||||
* @param ViewLocation - The view location of the camera.
|
||||
* @param ViewRotation - The view rotation of the camera.
|
||||
* @param OutFOV - The New FOV of the camera.
|
||||
* @param OutArmLength - The New Arm Length of the camera.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
void ProcessBoomLengthAndFOV(float DeltaTime, float InFOV, float InArmLength, FVector ViewLocation, FRotator ViewRotation, float& OutFOV, float& OutArmLength);
|
||||
|
||||
/**
|
||||
* Called to give modifiers a chance to adjust arm offsets before they are applied.
|
||||
*
|
||||
* @param DeltaTime - Frame time in seconds.
|
||||
* @param InSocketOffset - The Current Socket Offset of the camera.
|
||||
* @param InTargetOffset - The Current Target Offset of the camera.
|
||||
* @param ViewLocation - The view location of the camera.
|
||||
* @param ViewRotation - The view rotation of the camera.
|
||||
* @param OutSocketOffset - New Socket Offset of the camera.
|
||||
* @param OutTargetOffset - New Target Offset of the camera.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
void ProcessBoomOffsets(float DeltaTime, FVector InSocketOffset, FVector InTargetOffset, FVector ViewLocation, FRotator ViewRotation, FVector& OutSocketOffset, FVector& OutTargetOffset);
|
||||
|
||||
/**
|
||||
* Called to give modifiers a chance to adjust miscelaneous stuff at the end of the update order.
|
||||
*
|
||||
* @param DeltaTime - Frame time in seconds.
|
||||
* @param ViewLocation - The view location of the camera.
|
||||
* @param ViewRotation - The view rotation of the camera.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
void PostUpdate(float DeltaTime, FVector ViewLocation, FRotator ViewRotation);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
void OnAnyLevelSequenceStarted();
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
void OnAnyLevelSequenceEnded();
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
void OnSetViewTarget(bool bImmediate, bool bNewTargetIsOwner);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier")
|
||||
bool IsDebugEnabled() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Modifier")
|
||||
void ToggleDebug(bool const bEnabled);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Modifier")
|
||||
void SetAlpha(float InAlpha) { Alpha = InAlpha; }
|
||||
|
||||
/**
|
||||
* Called to give modifiers a chance to adjust both the yaw turn rate and pitch turn rate. However the input for looking needs to have UGC_CameraTurnRateModifier.
|
||||
*
|
||||
* @param DeltaTime - Frame time in seconds.
|
||||
* @param InLocalControlRotation - The difference between the actor rotation and the control rotation.
|
||||
* @param OutPitchTurnRate - Out. New value of the pitch turn rate (between 0 and 1).
|
||||
* @param OutYawTurnRate - Out. New value of the yaw turn rate (between 0 and 1).
|
||||
* @return Return true to prevent subsequent (lower priority) modifiers to further adjust rotation, false otherwise.
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "UGC|Camera Modifier")
|
||||
bool ProcessTurnRate(float DeltaTime, FRotator InLocalControlRotation, float InPitchTurnRate, float InYawTurnRate, float& OutPitchTurnRate, float& OutYawTurnRate);
|
||||
|
||||
bool CanPlayDuringCameraAnimation() const { return bPlayDuringCameraAnimations; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
FVector GetOwnerVelocity() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
bool IsOwnerFalling() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
bool IsOwnerStrafing() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
bool IsOwnerMovingOnGround() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
void ComputeOwnerFloorDistance(float SweepDistance, float CapsuleRadius, bool& bOutFloorExists, float& OutFloorDistance) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
void ComputeOwnerFloorNormal(float SweepDistance, float CapsuleRadius, bool& bOutFloorExists, FVector& OutFloorNormal) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
void ComputeOwnerSlopeAngle(float& OutSlopePitchDegrees, float& OutSlopeRollDegrees);
|
||||
|
||||
/*
|
||||
* Returns value betwen 1 (the character is looking where they're moving) or -1 (looking in the opposite direction they're moving).
|
||||
* Will return 0 if the character isn't moving.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
float ComputeOwnerLookAndMovementDot();
|
||||
|
||||
protected:
|
||||
virtual bool ModifyCamera(float DeltaTime, FMinimalViewInfo& InOutPOV) override;
|
||||
virtual void ModifyCamera(float DeltaTime, FVector ViewLocation, FRotator ViewRotation, float FOV, FVector& OutViewLocation, FRotator& OutViewRotation, float& OutFOV) override;
|
||||
virtual bool ProcessViewRotation(AActor* ViewTarget, float DeltaTime, FRotator& OutViewRotation, FRotator& OutDeltaRot) override;
|
||||
|
||||
template<typename T>
|
||||
T* GetViewTargetAs() const { return Cast<T>(GetViewTarget()); }
|
||||
|
||||
void UpdateOwnerReferences();
|
||||
|
||||
void UpdateInternalVariables(float DeltaTime);
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Modifier Settings")
|
||||
bool bPlayDuringCameraAnimations = false;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
TObjectPtr<class AUGC_PlayerCameraManager> UGCCameraManager = nullptr;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
TObjectPtr<class APlayerController> OwnerController = nullptr;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
TObjectPtr<class ACharacter> OwnerCharacter = nullptr;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
TObjectPtr<class APawn> OwnerPawn = nullptr;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
TObjectPtr<class USpringArmComponent> SpringArm = nullptr;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
TObjectPtr<class UCharacterMovementComponent> MovementComponent = nullptr;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
FVector CurrentSocketOffset;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
FVector CurrentTargetOffset;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
float CurrentArmLength;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
bool bHasMovementInput;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
FVector PreviousMovementInput;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
FVector MovementInput;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
float TimeSinceMovementInput;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
bool bHasRotationInput;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
FRotator RotationInput;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Modifier|Internal")
|
||||
float TimeSinceRotationInput;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base Camera Modifier Class for Add-on modifiers
|
||||
*/
|
||||
UCLASS(abstract, ClassGroup = "UGC|Add On|Camera Modifiers")
|
||||
class AURORADEVS_UGC_API UUGC_CameraAddOnModifier : public UUGC_CameraModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "UGC|Add On|Camera Modifiers")
|
||||
void SetSettings(class UUGC_CameraAddOnModifierSettings* InSettings);
|
||||
|
||||
// Add-On Settings class associated to this add-on modifier.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings")
|
||||
TSubclassOf<class UUGC_CameraAddOnModifierSettings> SettingsClass;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "Settings")
|
||||
TObjectPtr<UUGC_CameraAddOnModifierSettings> Settings;
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Camera/Modifiers/UGC_CameraModifier.h"
|
||||
#include "Misc/Build.h"
|
||||
#include "Misc/Guid.h"
|
||||
#include "UGC_CameraPropertyRequestStackHelper.h"
|
||||
#include "UGC_CameraPropertiesAnimNotifyModifiers.generated.h"
|
||||
|
||||
/**
|
||||
* Camera Modifier in charge of handling FOV change requests from Anim Notifies.
|
||||
*/
|
||||
UCLASS(abstract, ClassGroup = "UGC Camera Modifiers")
|
||||
class AURORADEVS_UGC_API UUGC_FOVAnimNotifyCameraModifier : public UUGC_CameraModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UUGC_FOVAnimNotifyCameraModifier();
|
||||
|
||||
protected:
|
||||
void PushFOVAnimNotifyRequest(FGuid RequestId, float TargetFOV, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve);
|
||||
void PopFOVAnimNotifyRequest(FGuid RequestId);
|
||||
|
||||
protected:
|
||||
void ProcessBoomLengthAndFOV_Implementation(float DeltaTime, float InFOV, float InArmLength, FVector ViewLocation, FRotator ViewRotation, float& OutFOV, float& OutArmLength) override;
|
||||
void OnModifierDisabled_Implementation(FMinimalViewInfo const& LastPOV, bool bWasImmediate);
|
||||
|
||||
protected:
|
||||
friend class UUGC_FOVRequestAnimNotifyState;
|
||||
UGC_CameraPropertyRequestStackHelper<float> RequestHelper;
|
||||
};
|
||||
|
||||
/**
|
||||
* Camera Modifier in charge of handling Arm Offset changes requests from Anim Notifies.
|
||||
*/
|
||||
UCLASS(abstract, ClassGroup = "UGC Camera Modifiers")
|
||||
class AURORADEVS_UGC_API UUGC_ArmOffsetAnimNotifyCameraModifier : public UUGC_CameraModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UUGC_ArmOffsetAnimNotifyCameraModifier();
|
||||
|
||||
protected:
|
||||
void PushArmSocketOffsetAnimNotifyRequest(FGuid RequestId, FVector TargetOffset, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve);
|
||||
void PopArmSocketOffsetAnimNotifyRequest(FGuid RequestId);
|
||||
|
||||
void PushArmTargetOffsetAnimNotifyRequest(FGuid RequestId, FVector TargetOffset, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve);
|
||||
void PopArmTargetOffsetAnimNotifyRequest(FGuid RequestId);
|
||||
|
||||
protected:
|
||||
void ProcessBoomOffsets_Implementation(float DeltaTime, FVector InSocketOffset, FVector InTargetOffset, FVector ViewLocation, FRotator ViewRotation, FVector& OutSocketOffset, FVector& OutTargetOffset) override;
|
||||
void OnModifierDisabled_Implementation(FMinimalViewInfo const& LastPOV, bool bWasImmediate);
|
||||
|
||||
protected:
|
||||
friend class UUGC_ArmOffsetRequestAnimNotifyState;
|
||||
UGC_CameraPropertyRequestStackHelper<FVector> SocketOffsetRequestHelper;
|
||||
UGC_CameraPropertyRequestStackHelper<FVector> TargetOffsetRequestHelper;
|
||||
};
|
||||
|
||||
/**
|
||||
* Camera Modifier in charge of handling Arm Length changes requests from Anim Notifies.
|
||||
*/
|
||||
UCLASS(abstract, ClassGroup = "UGC Camera Modifiers")
|
||||
class AURORADEVS_UGC_API UUGC_ArmLengthAnimNotifyCameraModifier : public UUGC_CameraModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UUGC_ArmLengthAnimNotifyCameraModifier();
|
||||
|
||||
protected:
|
||||
void PushArmLengthAnimNotifyRequest(FGuid RequestId, float TargetLength, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve);
|
||||
void PopArmLengthAnimNotifyRequest(FGuid RequestId);
|
||||
|
||||
protected:
|
||||
void ProcessBoomLengthAndFOV_Implementation(float DeltaTime, float InFOV, float InArmLength, FVector ViewLocation, FRotator ViewRotation, float& OutFOV, float& OutArmLength) override;
|
||||
void OnModifierDisabled_Implementation(FMinimalViewInfo const& LastPOV, bool bWasImmediate);
|
||||
|
||||
protected:
|
||||
friend class UUGC_ArmLengthRequestAnimNotifyState;
|
||||
UGC_CameraPropertyRequestStackHelper<float> RequestHelper;
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Misc/Build.h"
|
||||
#include "Misc/Guid.h"
|
||||
#include "Camera/CameraTypes.h"
|
||||
|
||||
class UUGC_CameraModifier;
|
||||
|
||||
template<typename ValueType>
|
||||
struct UGC_CameraPropertyValueRequest
|
||||
{
|
||||
public:
|
||||
bool IsValid() const { return RequestId.IsValid(); }
|
||||
void Invalidate();
|
||||
|
||||
public:
|
||||
FGuid RequestId = FGuid();
|
||||
|
||||
ValueType TargetValue = {};
|
||||
float TotalDuration = 0.f;
|
||||
float BlendInDuration = 0.f;
|
||||
float BlendOutDuration = 0.f;
|
||||
float CurrentTime = 0.f;
|
||||
float BlendInCurrentTime = 0.f;
|
||||
float BlendOutCurrentTime = 0.f;
|
||||
|
||||
class UCurveFloat* BlendInCurve = nullptr;
|
||||
class UCurveFloat* BlendOutCurve = nullptr;
|
||||
|
||||
bool bBlendingIn = false;
|
||||
bool bBlendingOut = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Camera Modifier Helper in charge of handling value change requests from Anim Notifies.
|
||||
*/
|
||||
template<typename ValueType>
|
||||
class AURORADEVS_UGC_API UGC_CameraPropertyRequestStackHelper final
|
||||
{
|
||||
public:
|
||||
void Init(const FName& PropertyName, const TFunction<bool()>& DebugGetterFunction);
|
||||
|
||||
public:
|
||||
void PushValueRequest(FGuid RequestId, ValueType TargetValue, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve);
|
||||
void PopValueRequest(FGuid RequestId);
|
||||
void ProcessValue(float DeltaTime, ValueType InValue, FVector ViewLocation, FRotator ViewRotation, ValueType& OutValue);
|
||||
void OnModifierDisabled(FMinimalViewInfo const& LastPOV, bool bWasImmediate);
|
||||
|
||||
protected:
|
||||
void InvalidateRequest(int32 RequestIndex);
|
||||
int32 FindInactiveRequest();
|
||||
|
||||
protected:
|
||||
friend class UUGC_FOVRequestAnimNotifyState;
|
||||
TArray<UGC_CameraPropertyValueRequest<ValueType>> Requests;
|
||||
TFunction<bool()> DebugGetterFunction;
|
||||
|
||||
FName PropertyName = TEXT("");
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
public:
|
||||
FLinearColor PropertyColor = FColor::Red;
|
||||
|
||||
protected:
|
||||
uint32 NumberActive = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "UGC_CameraPropertyRequestStackHelper.inl"
|
||||
@@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
#include "Curves/CurveFloat.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "Templates/IsArithmetic.h"
|
||||
#include "Templates/Decay.h"
|
||||
|
||||
template<typename ValueType>
|
||||
void UGC_CameraPropertyValueRequest<ValueType>::Invalidate()
|
||||
{
|
||||
*this = UGC_CameraPropertyValueRequest<ValueType>();
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void UGC_CameraPropertyRequestStackHelper<ValueType>::Init(const FName& InPropertyName, const TFunction<bool()>& BoolDebugGetterFunction)
|
||||
{
|
||||
PropertyName = InPropertyName;
|
||||
DebugGetterFunction = BoolDebugGetterFunction;
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void UGC_CameraPropertyRequestStackHelper<ValueType>::PushValueRequest(FGuid RequestId, ValueType TargetValue, float TotalDuration, float BlendInDuration, UCurveFloat* BlendInCurve, float BlendOutDuration, UCurveFloat* BlendOutCurve)
|
||||
{
|
||||
if (!RequestId.IsValid() || TotalDuration <= 0.f)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__ ": Wrong request."));
|
||||
return;
|
||||
}
|
||||
|
||||
UGC_CameraPropertyValueRequest<ValueType> Request;
|
||||
Request.RequestId = RequestId;
|
||||
Request.CurrentTime = 0.f;
|
||||
Request.TargetValue = TargetValue;
|
||||
Request.TotalDuration = TotalDuration;
|
||||
|
||||
Request.bBlendingIn = BlendInDuration > 0.f;
|
||||
Request.BlendInDuration = BlendInDuration;
|
||||
Request.BlendInCurve = BlendInCurve;
|
||||
Request.BlendInCurrentTime = 0.f;
|
||||
|
||||
Request.bBlendingOut = false;
|
||||
Request.BlendOutDuration = BlendOutDuration;
|
||||
Request.BlendOutCurve = BlendOutCurve;
|
||||
Request.BlendOutCurrentTime = 0.f;
|
||||
|
||||
const int32 NewIndex = FindInactiveRequest();
|
||||
check(NewIndex < MAX_uint16);
|
||||
Requests[NewIndex] = MoveTemp(Request);
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
NumberActive++;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void UGC_CameraPropertyRequestStackHelper<ValueType>::PopValueRequest(FGuid RequestId)
|
||||
{
|
||||
int32 FoundIndex = -1;
|
||||
for (int32 Index = 0; Index < Requests.Num(); ++Index)
|
||||
{
|
||||
if (Requests[Index].RequestId == RequestId)
|
||||
{
|
||||
FoundIndex = Index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (FoundIndex >= 0)
|
||||
{
|
||||
InvalidateRequest(FoundIndex);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
int32 UGC_CameraPropertyRequestStackHelper<ValueType>::FindInactiveRequest()
|
||||
{
|
||||
for (int32 Index = 0; Index < Requests.Num(); ++Index)
|
||||
{
|
||||
const UGC_CameraPropertyValueRequest<ValueType>& Request(Requests[Index]);
|
||||
if (!Request.IsValid())
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return Requests.Emplace();
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void UGC_CameraPropertyRequestStackHelper<ValueType>::InvalidateRequest(int32 RequestIndex)
|
||||
{
|
||||
if (RequestIndex >= 0 && RequestIndex < Requests.Num())
|
||||
{
|
||||
Requests[RequestIndex].Invalidate();
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
--NumberActive;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void UGC_CameraPropertyRequestStackHelper<ValueType>::ProcessValue(float DeltaTime, ValueType InValue, FVector ViewLocation, FRotator ViewRotation, ValueType& OutValue)
|
||||
{
|
||||
ValueType FinalValue = InValue;
|
||||
for (int32 Index = 0; Index < Requests.Num(); ++Index)
|
||||
{
|
||||
UGC_CameraPropertyValueRequest<ValueType>& Request = Requests[Index];
|
||||
if (!Request.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Request.CurrentTime += DeltaTime;
|
||||
|
||||
// Advance any easing times.
|
||||
if (Request.bBlendingIn)
|
||||
{
|
||||
Request.BlendInCurrentTime += DeltaTime;
|
||||
}
|
||||
if (Request.bBlendingOut)
|
||||
{
|
||||
Request.BlendOutCurrentTime += DeltaTime;
|
||||
}
|
||||
|
||||
// Start easing out if we're nearing the end.
|
||||
if (!Request.bBlendingOut)
|
||||
{
|
||||
const float BlendOutStartTime = Request.TotalDuration - Request.BlendOutDuration;
|
||||
if (Request.CurrentTime > BlendOutStartTime)
|
||||
{
|
||||
Request.bBlendingOut = true;
|
||||
Request.BlendOutCurrentTime = Request.CurrentTime - BlendOutStartTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're done easing in or out.
|
||||
bool bIsDoneEasingOut = false;
|
||||
if (Request.bBlendingIn)
|
||||
{
|
||||
if (Request.BlendInCurrentTime > Request.BlendInDuration || Request.BlendInDuration == 0.f)
|
||||
{
|
||||
Request.bBlendingIn = false;
|
||||
}
|
||||
}
|
||||
if (Request.bBlendingOut)
|
||||
{
|
||||
if (Request.BlendOutCurrentTime > Request.BlendOutDuration)
|
||||
{
|
||||
bIsDoneEasingOut = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out the final easing weight.
|
||||
const float EasingInT = FMath::Clamp((Request.BlendInCurrentTime / Request.BlendInDuration), 0.f, 1.f);
|
||||
const float EasingInWeight = Request.bBlendingIn ?
|
||||
(Request.BlendInCurve ? Request.BlendInCurve->GetFloatValue(EasingInT) : EasingInT) : 1.f;
|
||||
|
||||
const float EasingOutT = FMath::Clamp((1.f - Request.BlendOutCurrentTime / Request.BlendOutDuration), 0.f, 1.f);
|
||||
const float EasingOutWeight = Request.bBlendingOut ?
|
||||
(Request.BlendOutCurve ? Request.BlendOutCurve->GetFloatValue(EasingOutT) : EasingOutT) : 1.f;
|
||||
|
||||
const float TotalEasingWeight = FMath::Min(EasingInWeight, EasingOutWeight);
|
||||
|
||||
// We might be done playing or there was a DisableModifier() call with bImmediate=false to let a value request blend out.
|
||||
if (bIsDoneEasingOut || TotalEasingWeight <= 0.f)
|
||||
{
|
||||
InvalidateRequest(Index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Using FinalValue so that having multiple value requests at the same time doesn't cause jumps in values.
|
||||
FinalValue = FMath::Lerp(FinalValue, Request.TargetValue, TotalEasingWeight);
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
if (DebugGetterFunction && DebugGetterFunction() && GEngine)
|
||||
{
|
||||
const float TextAlpha = (TotalEasingWeight * static_cast<float>(Index + 1) / static_cast<float>(NumberActive));
|
||||
const FLinearColor TextColor = FMath::Lerp(FLinearColor::White, PropertyColor, TextAlpha);
|
||||
|
||||
FString ValueStr = "";
|
||||
if constexpr (TIsArithmetic<ValueType>::Value) ValueStr = LexToSanitizedString<FString, ValueType>(FinalValue);
|
||||
else ValueStr = FinalValue.ToCompactString();
|
||||
|
||||
GEngine->AddOnScreenDebugMessage(-1, DeltaTime, TextColor.ToFColor(true),
|
||||
FString::Printf(TEXT("UUGC_%sAnimNotifyRequest: Notify %d - %s"), *PropertyName.ToString(), Index, *ValueStr));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
OutValue = FinalValue;
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void UGC_CameraPropertyRequestStackHelper<ValueType>::OnModifierDisabled(FMinimalViewInfo const& LastPOV, bool bWasImmediate)
|
||||
{
|
||||
for (int32 Index = 0; Index < Requests.Num(); ++Index)
|
||||
{
|
||||
if (!Requests[Index].IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bWasImmediate)
|
||||
{
|
||||
InvalidateRequest(Index);
|
||||
}
|
||||
else if (!Requests[Index].bBlendingOut)
|
||||
{
|
||||
Requests[Index].bBlendingOut = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "UObject/ScriptMacros.h"
|
||||
#include "UGC_CameraAnimationModifier.h"
|
||||
#include "UGC_PlayCameraAnimCallbackProxy.generated.h"
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCameraAnimationPlayDelegate);
|
||||
|
||||
/** Parameter struct for adding new camera animations to UGCCameraAnimationCameraModifier */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FUGCCameraAnimationParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Time scale for playing the new camera animation */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Animation")
|
||||
float PlayRate = 1.f;
|
||||
|
||||
/** Ease-in function type */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Animation")
|
||||
ECameraAnimationEasingType EaseInType = ECameraAnimationEasingType::Linear;
|
||||
/** Ease-in duration in seconds */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Animation")
|
||||
float EaseInDuration = 0.f;
|
||||
|
||||
/** Ease-out function type */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Animation")
|
||||
ECameraAnimationEasingType EaseOutType = ECameraAnimationEasingType::Linear;
|
||||
/** Ease-out duration in seconds */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Animation")
|
||||
float EaseOutDuration = 0.f;
|
||||
|
||||
/** How should the camera behave after the animation is over. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera Animation")
|
||||
ECameraAnimationResetType ResetType = ECameraAnimationResetType::ResetToZero;
|
||||
|
||||
explicit operator FCameraAnimationParams() const;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class AURORADEVS_UGC_API UUGC_PlayCameraAnimCallbackProxy : public UObject
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
// Called when Camera Animation finished playing and wasn't interrupted
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnCameraAnimationPlayDelegate OnCompleted;
|
||||
|
||||
// Called when Camera Animation starts blending out and is not interrupted
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnCameraAnimationPlayDelegate OnEaseOut;
|
||||
|
||||
// Called when Camera Animation has been interrupted (or failed to play)
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnCameraAnimationPlayDelegate OnInterrupted;
|
||||
|
||||
// Called to perform the query internally
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
|
||||
static UUGC_PlayCameraAnimCallbackProxy* CreateProxyObjectForPlayCameraAnim(
|
||||
class APlayerCameraManager* InPlayerCameraManager,
|
||||
TSubclassOf<class UUGC_CameraAnimationModifier> ModifierClass,
|
||||
class UCameraAnimationSequence* CameraSequence,
|
||||
FUGCCameraAnimationParams Params,
|
||||
struct FCameraAnimationHandle& Handle,
|
||||
bool bInterruptOthers,
|
||||
bool bDoCollisionChecks);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
|
||||
static UUGC_PlayCameraAnimCallbackProxy* CreateProxyObjectForPlayCameraAnimForModifier(
|
||||
class UUGC_CameraAnimationModifier* CameraAnimationModifier,
|
||||
class UCameraAnimationSequence* CameraSequence,
|
||||
FUGCCameraAnimationParams Params,
|
||||
struct FCameraAnimationHandle& Handle,
|
||||
bool bInterruptOthers,
|
||||
bool bDoCollisionChecks);
|
||||
|
||||
protected:
|
||||
UFUNCTION()
|
||||
void OnCameraAnimationEasingOut(UCameraAnimationSequence* CameraAnimation);
|
||||
|
||||
UFUNCTION()
|
||||
void OnCameraAnimationEnded(UCameraAnimationSequence* CameraAnimation, bool bInterrupted);
|
||||
|
||||
private:
|
||||
TWeakObjectPtr<class UUGC_CameraAnimationModifier> CameraAnimationModifierPtr;
|
||||
|
||||
bool bInterruptedCalledBeforeBlendingOut = false;
|
||||
|
||||
FOnCameraAnimationEaseOutStarted CameraAnimationEasingOutDelegate;
|
||||
FOnCameraAnimationEnded CameraAnimationEndedDelegate;
|
||||
|
||||
void PlayCameraAnimation(
|
||||
class APlayerCameraManager* InPlayerCameraManager,
|
||||
TSubclassOf<class UUGC_CameraAnimationModifier> ModifierClass,
|
||||
class UCameraAnimationSequence* CameraSequence,
|
||||
FUGCCameraAnimationParams Params,
|
||||
struct FCameraAnimationHandle& Handle,
|
||||
bool bInterruptOthers,
|
||||
bool bDoCollisionChecks);
|
||||
|
||||
void PlayCameraAnimation(
|
||||
class UUGC_CameraAnimationModifier* CameraAnimationModifier,
|
||||
class UCameraAnimationSequence* CameraSequence,
|
||||
FUGCCameraAnimationParams Params,
|
||||
struct FCameraAnimationHandle& Handle,
|
||||
bool bInterruptOthers,
|
||||
bool bDoCollisionChecks);
|
||||
};
|
||||
@@ -0,0 +1,326 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Camera/PlayerCameraManager.h"
|
||||
#include "UGC_PlayerCameraManager.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class AURORADEVS_UGC_API AUGC_PlayerCameraManager : public APlayerCameraManager
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AUGC_PlayerCameraManager();
|
||||
|
||||
virtual void InitializeFor(class APlayerController* PC) override;
|
||||
|
||||
/*
|
||||
* This returns the real camera view, this isn't necessarily the view of the camera attached to your character.
|
||||
* For example, during camera animations, the camera used is different than the normal gameplay camera.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager")
|
||||
void GetRealCameraView(FVector& OutViewLocation, FRotator& OutViewRotation) { OutViewLocation = ViewTarget.POV.Location; OutViewRotation = ViewTarget.POV.Rotation; }
|
||||
|
||||
/*
|
||||
* Prepare the possession of a new pawn. Doesn't actually possess the pawn.
|
||||
* APlayerController::Possess needs to be called directly after this function.
|
||||
* @param NewPawn The new pawn that we intend on possessing.
|
||||
* @param NewCameraDA The camera data asset that will be pushed after possessing.
|
||||
*Requires call to PostPosses after the APlayerController::Possess has been called!*
|
||||
* @param bBlendSpringArmProperties Whether the new pawn's spring arm should prepare to blend from the previous spring arm.
|
||||
*This doesn't work with SetViewTargetWithBlend!*
|
||||
*Requires call to PostPosses after the APlayerController::Possess has been called!*
|
||||
* @param bMatchCameraRotation Whether the new pawn should have the same camera spring arm rotation (aka Control Rotation) or not.
|
||||
*This doesn't work with SetViewTargetWithBlend!*
|
||||
*Requires call to PostPosses after the APlayerController::Possess has been called!*
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void PrePossess(class APawn* NewPawn, class UUGC_CameraDataAssetBase* NewCameraDA, bool bBlendSpringArmProperties = false, bool bMatchCameraRotation = false);
|
||||
|
||||
/*
|
||||
* Finush the possession of a new pawn. APlayerController::Possess needs to have been called directly before this function.
|
||||
* @param bReplaceCurrentCameraDA Whether to push the Camera DA that was passed in the PrePossess function (if any) should
|
||||
replace the current camera DA or be pushed on top of it.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void PostPossess(bool bReplaceCurrentCameraDA = true);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void RefreshLevelSequences();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager")
|
||||
bool IsPlayingAnyLevelSequence() const { return NbrActiveLevelSequences > 0 || NbrActivePausedLevelSequences > 0; }
|
||||
|
||||
// Plays a single new camera animation sequence. Any subsequent calls while this animation runs will interrupt the current animation.
|
||||
// This variation can be used in contexts where async nodes aren't allowd (e.g., AnimNotifies).
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void PlayCameraAnimation(class UCameraAnimationSequence* CameraSequence, struct FUGCCameraAnimationParams const& Params, bool bInterruptOthers, bool bDoCollisionChecks);
|
||||
|
||||
/**
|
||||
* Enables/Disables all camera modifiers ONLY if they inherit from UGC Camera Modifier.
|
||||
* @param bEnabled - true to enable all UGC camera modifiers, false to disable.
|
||||
* @param bImmediate - If bEnabled is false: true to disable immediately with no blend out, false (default) to allow blend out
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void ToggleUGCCameraModifiers(bool const bEnabled, bool const bImmediate = true);
|
||||
|
||||
/**
|
||||
* Enables/Disables all camera modifiers, regardless of whether they inherit from UGC Camera Modifier.
|
||||
* @param bEnabled - true to enable all camera modifiers, false to disable.
|
||||
* @param bImmediate - If bEnabled is false: true to disable immediately with no blend out, false (default) to allow blend out
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void ToggleCameraModifiers(bool const bEnabled, bool const bImmediate = true);
|
||||
|
||||
/**
|
||||
* Enables/Disables all debugging of camera modifiers ONLY if they inherit from UGC Camera Modifier.
|
||||
* @param bEnabled - true to enable all debugging of UGC camera modifiers, false to disable.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void ToggleAllUGCModifiersDebug(bool const bEnabled);
|
||||
/**
|
||||
*
|
||||
* Enables/Disables all debugging of camera modifiers regardless of whether they inherit from UGC Camera Modifier.
|
||||
* @param bEnabled - true to enable all debugging of all camera modifiers, false to disable.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void ToggleAllModifiersDebug(bool const bEnabled);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager")
|
||||
class USpringArmComponent* GetOwnerSpringArmComponent() const { return CameraArm.Get(); }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager")
|
||||
class UUGC_CameraDataAssetBase* GetCurrentCameraDataAsset() const { return CameraDataStack.IsEmpty() ? nullptr : CameraDataStack[CameraDataStack.Num() - 1]; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void PushCameraData(class UUGC_CameraDataAssetBase* CameraDA);
|
||||
|
||||
// Pops the most recent Camera DA.
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void PopCameraDataHead();
|
||||
|
||||
// Pops the given Camera DA. If it isn't in stack, it returns false.
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void PopCameraData(class UUGC_CameraDataAssetBase* CameraDA);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "UGC|Camera Manager")
|
||||
void OnCameraDataStackChanged(class UUGC_CameraDataAssetBase* CameraDA, bool bBlendCameraProperties = true);
|
||||
|
||||
/** Draw a debug camera shape at the real camera's location, which can be different from the character's attached camera.
|
||||
* For example, camera animations use a different camera then the character's attached camera.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager|Debug")
|
||||
void DrawRealDebugCamera(float Duration, FLinearColor CameraColor = FLinearColor::Red, float Thickness = 1.f) const;
|
||||
|
||||
/** Draw a debug camera shape at the character's attached camera's location, which can be different from the real camera.
|
||||
* For example, camera animations use a different camera then the character's attached camera.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager|Debug")
|
||||
void DrawGameDebugCamera(float Duration, bool bDrawCamera = true, FLinearColor CameraColor = FLinearColor::Blue, bool bDrawSpringArm = true, FLinearColor SpringArmColor = FLinearColor::Blue, float Thickness = 1.f) const;
|
||||
|
||||
/* Draw Spring Arm.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager|Debug")
|
||||
void DrawDebugSpringArm(FVector const& CameraLocation, float Duration, FLinearColor SpringArmColor = FLinearColor::Blue, float Thickness = 1.f) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
bool IsPlayingAnyCameraAnimation() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager|Movement")
|
||||
FVector GetOwnerVelocity() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager|Movement")
|
||||
void ComputeOwnerFloorDist(float SweepDistance, float CapsuleRadius, bool& bOutFloorExists, float& OutFloorDistance) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager|Movement")
|
||||
bool IsOwnerFalling() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager|Movement")
|
||||
bool IsOwnerStrafing() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager|Movement")
|
||||
bool IsOwnerMovingOnGround() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Manager|Movement")
|
||||
void ComputeOwnerFloorNormal(float SweepDistance, float CapsuleRadius, bool& bOutFloorExists, FVector& OutFloorNormal) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
void ComputeOwnerSlopeAngle(float& OutSlopePitchDegrees, float& OutSlopeRollDegrees);
|
||||
|
||||
/*
|
||||
* Returns value betwen 1 (the character is looking where they're moving) or -1 (looking in the opposite direction they're moving).
|
||||
* Will return 0 if the character isn't moving.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "UGC|Camera Modifier|Movement")
|
||||
float ComputeOwnerLookAndMovementDot();
|
||||
|
||||
/*
|
||||
* Only use this when your pawn has multiple spring arm components and you need to use a different one.
|
||||
* The spring arm is automatically reset to the first found spring arm component on the pawn when we initialize
|
||||
* the camera manager or when we possess a new pawn.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager")
|
||||
void SetSpringArmComponent(class USpringArmComponent* Arm) { if (Arm) CameraArm = Arm; }
|
||||
|
||||
/**
|
||||
* Returns camera modifier for this camera of the given class, if it exists.
|
||||
* Looks for inherited classes too. If there are multiple modifiers which fit, the first one is returned.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "UGC|Camera Manager", meta = (DeterminesOutputType = "ModifierClass"))
|
||||
UCameraModifier* FindCameraModifierOfClass(TSubclassOf<UCameraModifier> ModifierClass, bool bIncludeInherited);
|
||||
|
||||
FVector GetCameraTurnRate() const { return FVector(YawTurnRate, PitchTurnRate, 0.f); }
|
||||
|
||||
UCameraModifier const* FindCameraModifierOfClass(TSubclassOf<UCameraModifier> ModifierClass, bool bIncludeInherited) const;
|
||||
|
||||
template<typename T>
|
||||
T* FindCameraModifierOfType()
|
||||
{
|
||||
bool constexpr bIncludeInherited = true;
|
||||
UCameraModifier* Modifier = FindCameraModifierOfClass(T::StaticClass(), bIncludeInherited);
|
||||
return Modifier != nullptr ? Cast<T>(Modifier) : nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T const* FindCameraModifierOfType() const
|
||||
{
|
||||
bool constexpr bIncludeInherited = true;
|
||||
UCameraModifier const* Modifier = FindCameraModifierOfClass(T::StaticClass(), bIncludeInherited);
|
||||
return Modifier != nullptr ? Cast<T>(Modifier) : nullptr;
|
||||
}
|
||||
protected:
|
||||
void PushCameraData_Internal(class UUGC_CameraDataAssetBase* CameraDA, bool bBlendCameraProperties);
|
||||
|
||||
void DoForEachUGCModifier(TFunction<void(class UUGC_CameraModifier*)> const& Function);
|
||||
|
||||
// Breaks when the function returns true.
|
||||
void DoForEachUGCModifierWithBreak(TFunction<bool(class UUGC_CameraModifier*)> const& Function);
|
||||
|
||||
virtual void DisplayDebug(class UCanvas* Canvas, const FDebugDisplayInfo& DebugDisplay, float& YL, float& YPos) override;
|
||||
virtual UCameraModifier* AddNewCameraModifier(TSubclassOf<UCameraModifier> ModifierClass) override;
|
||||
virtual bool RemoveCameraModifier(UCameraModifier* ModifierToRemove) override;
|
||||
|
||||
virtual void SetViewTarget(AActor* NewViewTarget, FViewTargetTransitionParams TransitionParams) override;
|
||||
|
||||
/**
|
||||
* Called to give PlayerCameraManager a chance to adjust view rotation updates before they are applied.
|
||||
* e.g. The base implementation enforces view rotation limits using LimitViewPitch, et al.
|
||||
* @param DeltaTime - Frame time in seconds.
|
||||
* @param OutViewRotation - In/out. The view rotation to modify.
|
||||
* @param OutDeltaRot - In/out. How much the rotation changed this frame.
|
||||
*/
|
||||
virtual void ProcessViewRotation(float DeltaTime, FRotator& OutViewRotation, FRotator& OutDeltaRot);
|
||||
|
||||
/**
|
||||
* Called to give PlayerCameraManager a chance to adjust both the yaw turn rate and pitch turn rate.
|
||||
*
|
||||
* @param DeltaTime - Frame time in seconds.
|
||||
* @param InLocalControlRotation - The difference between the actor rotation and the control rotation.
|
||||
* @param OutPitchTurnRate - Out. New value of the pitch turn rate (between 0 and 1).
|
||||
* @param OutYawTurnRate - Out. New value of the yaw turn rate (between 0 and 1).
|
||||
* @return Return true to prevent subsequent (lower priority) modifiers to further adjust rotation, false otherwise.
|
||||
*/
|
||||
virtual void ProcessTurnRate(float DeltaTime, FRotator InLocalControlRotation, float& OutPitchTurnRate, float& OutYawTurnRate);
|
||||
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
virtual void LimitViewYaw(FRotator& ViewRotation, float InViewYawMin, float InViewYawMax) override;
|
||||
|
||||
// This updates the internal variables of the UGC Player Camera Manager. Make sure to call the parent function if you override this in BP.
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "UGC|Camera Manager|Internal")
|
||||
void UpdateInternalVariables(float DeltaTime);
|
||||
|
||||
// Usually uses the UGC Pawn Interface to fetch the rotation input of the camera (Mouse or Right Thumbstick). Override this if you want to provide your own way of getting the camera rotation input.
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "UGC|Camera Manager|Internal")
|
||||
FRotator GetRotationInput() const;
|
||||
|
||||
// Usually uses the UGC Pawn Interface to fetch the movement input of the character (WASD or Left Thumbstick). Override this if you want to provide your own way of getting the camera rotation input.
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "UGC|Camera Manager|Internal")
|
||||
FVector GetMovementControlInput() const;
|
||||
|
||||
UFUNCTION()
|
||||
void OnLevelSequenceStarted();
|
||||
|
||||
UFUNCTION()
|
||||
void OnLevelSequencePaused();
|
||||
|
||||
UFUNCTION()
|
||||
void OnLevelSequenceEnded();
|
||||
protected:
|
||||
friend class UUGC_CameraModifier;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
TArray<class UUGC_CameraDataAssetBase*> CameraDataStack;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "UGC|Camera Manager|Internal")
|
||||
TObjectPtr<class ACharacter> OwnerCharacter;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "UGC|Camera Manager|Internal")
|
||||
TObjectPtr<class APawn> OwnerPawn;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "UGC|Camera Manager|Internal")
|
||||
TObjectPtr<class USpringArmComponent> CameraArm;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "UGC|Camera Manager|Internal")
|
||||
TObjectPtr<class UCharacterMovementComponent> MovementComponent;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
TArray<class ALevelSequenceActor*> LevelSequences;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "UGC|Camera Manager|Angle Constraints", meta = (UIMin = 0.f, UIMax = 1.f, ClampMin = 0.f, ClampMax = 1.f))
|
||||
float PitchTurnRate = 1.f;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "UGC|Camera Manager|Angle Constraints", meta = (UIMin = 0.f, UIMax = 1.f, ClampMin = 0.f, ClampMax = 1.f))
|
||||
float YawTurnRate = 1.f;
|
||||
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
float AspectRatio = 0.f;
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
float VerticalFOV = 0.f;
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
float HorizontalFOV = 0.f;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
bool bHasMovementInput = false;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
FVector MovementInput = FVector::ZeroVector;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
float TimeSinceMovementInput = 0.f;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
bool bHasRotationInput = false;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
FRotator RotationInput = FRotator::ZeroRotator;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
float TimeSinceRotationInput = 0.f;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
float OriginalArmLength = 0.f;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
TArray<class UUGC_CameraModifier*> UGCModifiersList;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UGC|Camera Manager|Internal")
|
||||
TArray<class UUGC_CameraAddOnModifier*> UGCAddOnModifiersList;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
int NbrActiveLevelSequences = 0;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
int NbrActivePausedLevelSequences = 0;
|
||||
|
||||
struct PossessPayload
|
||||
{
|
||||
TObjectPtr<class UUGC_CameraDataAssetBase> PendingCameraDA = nullptr;
|
||||
FRotator PendingControlRotation = FRotator::ZeroRotator;
|
||||
bool bBlendCameraProperties = false;
|
||||
bool bMatchCameraRotation = false;
|
||||
} PendingPossessPayload;
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "InputModifiers.h"
|
||||
#include "UGC_CameraTurnRateModifier.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class AURORADEVS_UGC_API UUGC_CameraTurnRateModifier : public UInputModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual FInputActionValue ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) override;
|
||||
|
||||
private:
|
||||
TObjectPtr<class AUGC_PlayerCameraManager> PlayerCameraManager;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "InputModifiers.h"
|
||||
#include "UGC_InputAccelerationModifier.generated.h"
|
||||
|
||||
/**
|
||||
* Apply an acceleration curve to your input so that it accelerates as time goes by. The given curve should be time-normalized, i.e., between 0 and 1.
|
||||
*/
|
||||
UCLASS(ClassGroup = "UGC Input Modifiers")
|
||||
class AURORADEVS_UGC_API UUGC_InputAccelerationModifier : public UInputModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual FInputActionValue ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) override;
|
||||
|
||||
public:
|
||||
// The time it takes to reach full speed.
|
||||
UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = Settings, Config)
|
||||
float AccelerationTime = 1.f;
|
||||
|
||||
// Apply an acceleration curve to your input so that it accelerates as time goes by. The given curve should be time-normalized, i.e., between 0 and 1.
|
||||
UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = Settings, meta = (DisplayThumbnail = "false"))
|
||||
TObjectPtr<UCurveFloat> AccelerationCurve;
|
||||
|
||||
private:
|
||||
float Timer = 0.f;
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright(c) Aurora Devs 2022-2025. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "UGC_PawnInterface.generated.h"
|
||||
|
||||
// This class does not need to be modified.
|
||||
UINTERFACE(MinimalAPI)
|
||||
class UUGC_PawnInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
/**
|
||||
* Pawn Interface used to get the movement/rotation input values.
|
||||
*/
|
||||
class AURORADEVS_UGC_API IUGC_PawnInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Get the value of the camera rotation input (usually the right thumbstick or the mouse).
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "UGC|Camera Interface")
|
||||
FRotator GetRotationInput() const;
|
||||
|
||||
// Get the value of the movement input (usually WASD or the left thumbstick).
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "UGC|Camera Interface")
|
||||
FVector GetMovementInput() const;
|
||||
};
|
||||
|
||||
// This class does not need to be modified.
|
||||
UINTERFACE(MinimalAPI)
|
||||
class UUGC_PawnMovementInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
/**
|
||||
* This interface should only be used with player classes NOT using the default Unreal Movement Component or components inheriting from it.
|
||||
* This interface can be used to query important movement properties (velocity, falling; etc.) from your custom movement components.
|
||||
*/
|
||||
class AURORADEVS_UGC_API IUGC_PawnMovementInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "UGC|Movement Interface")
|
||||
FVector GetOwnerVelocity() const;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "UGC|Movement Interface")
|
||||
bool IsOwnerFalling() const;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "UGC|Movement Interface")
|
||||
bool IsOwnerStrafing() const;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "UGC|Movement Interface")
|
||||
bool IsOwnerMovingOnGround() const;
|
||||
};
|
||||
Reference in New Issue
Block a user