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