第一次提交
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user