第一次提交
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "GCMS_CameraMode.h"
|
||||
|
||||
#include "GCMS_CameraSystemComponent.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "Engine/Canvas.h"
|
||||
#include "GameFramework/Character.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GCMS_CameraMode)
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// FGMS_CameraModeView
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
FGCMS_CameraModeView::FGCMS_CameraModeView()
|
||||
: Location(ForceInit)
|
||||
, Rotation(ForceInit)
|
||||
, ControlRotation(ForceInit)
|
||||
, FieldOfView(80.0f)
|
||||
{
|
||||
}
|
||||
|
||||
void FGCMS_CameraModeView::Blend(const FGCMS_CameraModeView& Other, float OtherWeight)
|
||||
{
|
||||
if (OtherWeight <= 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (OtherWeight >= 1.0f)
|
||||
{
|
||||
*this = Other;
|
||||
return;
|
||||
}
|
||||
|
||||
Location = FMath::Lerp(Location, Other.Location, OtherWeight);
|
||||
|
||||
const FRotator DeltaRotation = (Other.Rotation - Rotation).GetNormalized();
|
||||
Rotation = Rotation + (OtherWeight * DeltaRotation);
|
||||
|
||||
const FRotator DeltaControlRotation = (Other.ControlRotation - ControlRotation).GetNormalized();
|
||||
ControlRotation = ControlRotation + (OtherWeight * DeltaControlRotation);
|
||||
|
||||
SprintArmSocketOffset = FMath::Lerp(SprintArmSocketOffset, Other.SprintArmSocketOffset, OtherWeight);
|
||||
SprintArmTargetOffset = FMath::Lerp(SprintArmTargetOffset, Other.SprintArmTargetOffset, OtherWeight);
|
||||
SprintArmLength = FMath::Lerp(SprintArmLength, Other.SprintArmLength, OtherWeight);
|
||||
|
||||
FieldOfView = FMath::Lerp(FieldOfView, Other.FieldOfView, OtherWeight);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// UGCMS_CameraMode
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
UGCMS_CameraMode::UGCMS_CameraMode()
|
||||
{
|
||||
FieldOfView = 80.0f;
|
||||
ViewPitchMin = -89.0f;
|
||||
ViewPitchMax = 89.0f;
|
||||
|
||||
BlendTime = 0.5f;
|
||||
BlendFunction = EGCMS_CameraModeBlendFunction::EaseOut;
|
||||
BlendExponent = 4.0f;
|
||||
BlendAlpha = 1.0f;
|
||||
BlendWeight = 1.0f;
|
||||
ActiveTime = 0.0f;
|
||||
MaxActiveTime = 0.0f;
|
||||
}
|
||||
|
||||
UWorld* UGCMS_CameraMode::GetWorld() const
|
||||
{
|
||||
return HasAnyFlags(RF_ClassDefaultObject) ? nullptr : GetOuter()->GetWorld();
|
||||
}
|
||||
|
||||
AActor* UGCMS_CameraMode::GetTargetActor() const
|
||||
{
|
||||
if (UGCMS_CameraSystemComponent* Component = Cast<UGCMS_CameraSystemComponent>(GetOuter()))
|
||||
{
|
||||
return Component->GetOwner();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FVector UGCMS_CameraMode::GetPivotLocation_Implementation() const
|
||||
{
|
||||
const AActor* TargetActor = GetTargetActor();
|
||||
check(TargetActor);
|
||||
|
||||
if (const APawn* TargetPawn = Cast<APawn>(TargetActor))
|
||||
{
|
||||
// Height adjustments for characters to account for crouching.
|
||||
if (const ACharacter* TargetCharacter = Cast<ACharacter>(TargetPawn))
|
||||
{
|
||||
const ACharacter* TargetCharacterCDO = TargetCharacter->GetClass()->GetDefaultObject<ACharacter>();
|
||||
check(TargetCharacterCDO);
|
||||
|
||||
const UCapsuleComponent* CapsuleComp = TargetCharacter->GetCapsuleComponent();
|
||||
check(CapsuleComp);
|
||||
|
||||
const UCapsuleComponent* CapsuleCompCDO = TargetCharacterCDO->GetCapsuleComponent();
|
||||
check(CapsuleCompCDO);
|
||||
|
||||
const float DefaultHalfHeight = CapsuleCompCDO->GetUnscaledCapsuleHalfHeight();
|
||||
const float ActualHalfHeight = CapsuleComp->GetUnscaledCapsuleHalfHeight();
|
||||
const float HeightAdjustment = (DefaultHalfHeight - ActualHalfHeight) + TargetCharacterCDO->BaseEyeHeight;
|
||||
|
||||
return TargetCharacter->GetActorLocation() + (FVector::UpVector * HeightAdjustment);
|
||||
}
|
||||
|
||||
return TargetPawn->GetPawnViewLocation();
|
||||
}
|
||||
|
||||
return TargetActor->GetActorLocation();
|
||||
}
|
||||
|
||||
FRotator UGCMS_CameraMode::GetPivotRotation_Implementation() const
|
||||
{
|
||||
const AActor* TargetActor = GetTargetActor();
|
||||
check(TargetActor);
|
||||
|
||||
if (const APawn* TargetPawn = Cast<APawn>(TargetActor))
|
||||
{
|
||||
return TargetPawn->GetViewRotation();
|
||||
}
|
||||
|
||||
return TargetActor->GetActorRotation();
|
||||
}
|
||||
|
||||
void UGCMS_CameraMode::UpdateCameraMode(float DeltaTime)
|
||||
{
|
||||
ActiveTime += DeltaTime;
|
||||
|
||||
if (MaxActiveTime > 0 && ActiveTime >= MaxActiveTime)
|
||||
{
|
||||
if (UGCMS_CameraSystemComponent* Component = Cast<UGCMS_CameraSystemComponent>(GetOuter()))
|
||||
{
|
||||
Component->PushDefaultCameraMode();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateView(DeltaTime);
|
||||
UpdateBlending(DeltaTime);
|
||||
}
|
||||
|
||||
void UGCMS_CameraMode::UpdateView(float DeltaTime)
|
||||
{
|
||||
FVector PivotLocation = GetPivotLocation();
|
||||
FRotator PivotRotation = GetPivotRotation();
|
||||
|
||||
PivotRotation.Pitch = FMath::ClampAngle(PivotRotation.Pitch, ViewPitchMin, ViewPitchMax);
|
||||
|
||||
OnUpdateView(DeltaTime, PivotLocation, PivotRotation);
|
||||
}
|
||||
|
||||
void UGCMS_CameraMode::SetBlendWeight(float Weight)
|
||||
{
|
||||
BlendWeight = FMath::Clamp(Weight, 0.0f, 1.0f);
|
||||
|
||||
// Since we're setting the blend weight directly, we need to calculate the blend alpha to account for the blend function.
|
||||
const float InvExponent = (BlendExponent > 0.0f) ? (1.0f / BlendExponent) : 1.0f;
|
||||
|
||||
switch (BlendFunction)
|
||||
{
|
||||
case EGCMS_CameraModeBlendFunction::Linear:
|
||||
BlendAlpha = BlendWeight;
|
||||
break;
|
||||
|
||||
case EGCMS_CameraModeBlendFunction::EaseIn:
|
||||
BlendAlpha = FMath::InterpEaseIn(0.0f, 1.0f, BlendWeight, InvExponent);
|
||||
break;
|
||||
|
||||
case EGCMS_CameraModeBlendFunction::EaseOut:
|
||||
BlendAlpha = FMath::InterpEaseOut(0.0f, 1.0f, BlendWeight, InvExponent);
|
||||
break;
|
||||
|
||||
case EGCMS_CameraModeBlendFunction::EaseInOut:
|
||||
BlendAlpha = FMath::InterpEaseInOut(0.0f, 1.0f, BlendWeight, InvExponent);
|
||||
break;
|
||||
|
||||
default:
|
||||
checkf(false, TEXT("SetBlendWeight: Invalid BlendFunction [%d]\n"), (uint8)BlendFunction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UCameraComponent* UGCMS_CameraMode::GetAssociatedCamera() const
|
||||
{
|
||||
if (UGCMS_CameraSystemComponent* Component = Cast<UGCMS_CameraSystemComponent>(GetOuter()))
|
||||
{
|
||||
return Component->GetAssociatedCamera();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
USpringArmComponent* UGCMS_CameraMode::GetAssociatedSprintArm() const
|
||||
{
|
||||
if (UGCMS_CameraSystemComponent* Component = Cast<UGCMS_CameraSystemComponent>(GetOuter()))
|
||||
{
|
||||
return Component->GetAssociatedSprintArm();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UGCMS_CameraMode::UpdateBlending(float DeltaTime)
|
||||
{
|
||||
if (BlendTime > 0.0f)
|
||||
{
|
||||
BlendAlpha += (DeltaTime / BlendTime);
|
||||
BlendAlpha = FMath::Min(BlendAlpha, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlendAlpha = 1.0f;
|
||||
}
|
||||
|
||||
const float Exponent = (BlendExponent > 0.0f) ? BlendExponent : 1.0f;
|
||||
|
||||
switch (BlendFunction)
|
||||
{
|
||||
case EGCMS_CameraModeBlendFunction::Linear:
|
||||
BlendWeight = BlendAlpha;
|
||||
break;
|
||||
|
||||
case EGCMS_CameraModeBlendFunction::EaseIn:
|
||||
BlendWeight = FMath::InterpEaseIn(0.0f, 1.0f, BlendAlpha, Exponent);
|
||||
break;
|
||||
|
||||
case EGCMS_CameraModeBlendFunction::EaseOut:
|
||||
BlendWeight = FMath::InterpEaseOut(0.0f, 1.0f, BlendAlpha, Exponent);
|
||||
break;
|
||||
|
||||
case EGCMS_CameraModeBlendFunction::EaseInOut:
|
||||
BlendWeight = FMath::InterpEaseInOut(0.0f, 1.0f, BlendAlpha, Exponent);
|
||||
break;
|
||||
|
||||
default:
|
||||
checkf(false, TEXT("UpdateBlending: Invalid BlendFunction [%d]\n"), (uint8)BlendFunction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraMode::OnUpdateView_Implementation(float DeltaTime, FVector PivotLocation, FRotator PivotRotation)
|
||||
{
|
||||
View.Location = PivotLocation;
|
||||
View.Rotation = PivotRotation;
|
||||
View.ControlRotation = View.Rotation;
|
||||
View.FieldOfView = FieldOfView;
|
||||
}
|
||||
|
||||
void UGCMS_CameraMode::DrawDebug(UCanvas* Canvas) const
|
||||
{
|
||||
check(Canvas);
|
||||
|
||||
FDisplayDebugManager& DisplayDebugManager = Canvas->DisplayDebugManager;
|
||||
|
||||
DisplayDebugManager.SetDrawColor(FColor::White);
|
||||
DisplayDebugManager.DrawString(FString::Printf(TEXT(" GMS_CameraMode: %s (%f)"), *GetName(), BlendWeight));
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GCMS_CameraModeStack.h"
|
||||
|
||||
#include "Engine/Canvas.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GCMS_CameraModeStack)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// UGCMS_CameraModeStack
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
UGCMS_CameraModeStack::UGCMS_CameraModeStack()
|
||||
{
|
||||
bIsActive = true;
|
||||
}
|
||||
|
||||
void UGCMS_CameraModeStack::ActivateStack()
|
||||
{
|
||||
if (!bIsActive)
|
||||
{
|
||||
bIsActive = true;
|
||||
|
||||
// Notify camera modes that they are being activated.
|
||||
for (UGCMS_CameraMode* CameraMode : CameraModeStack)
|
||||
{
|
||||
check(CameraMode);
|
||||
CameraMode->OnActivation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraModeStack::DeactivateStack()
|
||||
{
|
||||
if (bIsActive)
|
||||
{
|
||||
bIsActive = false;
|
||||
|
||||
// Notify camera modes that they are being deactivated.
|
||||
for (UGCMS_CameraMode* CameraMode : CameraModeStack)
|
||||
{
|
||||
check(CameraMode);
|
||||
CameraMode->OnDeactivation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraModeStack::PushCameraMode(TSubclassOf<UGCMS_CameraMode> CameraModeClass)
|
||||
{
|
||||
if (!CameraModeClass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// get camera from pool.
|
||||
UGCMS_CameraMode* NewCameraMode = GetCameraModeInstance(CameraModeClass);
|
||||
check(NewCameraMode);
|
||||
|
||||
int32 StackSize = CameraModeStack.Num();
|
||||
|
||||
if ((StackSize > 0) && (CameraModeStack[0] == NewCameraMode))
|
||||
{
|
||||
// Already top of stack.
|
||||
return;
|
||||
}
|
||||
|
||||
// See if it's already in the stack and remove it.
|
||||
// Figure out how much it was contributing to the stack.
|
||||
int32 ExistingStackIndex = INDEX_NONE;
|
||||
float ExistingStackContribution = 1.0f;
|
||||
|
||||
for (int32 StackIndex = 0; StackIndex < StackSize; ++StackIndex)
|
||||
{
|
||||
if (CameraModeStack[StackIndex] == NewCameraMode)
|
||||
{
|
||||
ExistingStackIndex = StackIndex;
|
||||
ExistingStackContribution *= NewCameraMode->GetBlendWeight();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExistingStackContribution *= (1.0f - CameraModeStack[StackIndex]->GetBlendWeight());
|
||||
}
|
||||
}
|
||||
|
||||
// existing in stack, remove it before add.
|
||||
if (ExistingStackIndex != INDEX_NONE)
|
||||
{
|
||||
CameraModeStack.RemoveAt(ExistingStackIndex);
|
||||
StackSize--;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExistingStackContribution = 0.0f;
|
||||
}
|
||||
|
||||
// Decide what initial weight to start with.
|
||||
const bool bShouldBlend = ((NewCameraMode->GetBlendTime() > 0.0f) && (StackSize > 0));
|
||||
const float BlendWeight = (bShouldBlend ? ExistingStackContribution : 1.0f);
|
||||
|
||||
NewCameraMode->SetBlendWeight(BlendWeight);
|
||||
|
||||
// Add new entry to top of stack.
|
||||
CameraModeStack.Insert(NewCameraMode, 0);
|
||||
|
||||
// Make sure stack bottom is always weighted 100%.
|
||||
CameraModeStack.Last()->SetBlendWeight(1.0f);
|
||||
|
||||
// Let the camera mode know if it's being added to the stack.
|
||||
if (ExistingStackIndex == INDEX_NONE)
|
||||
{
|
||||
NewCameraMode->OnActivation();
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraModeStack::PopCameraMode(TSubclassOf<UGCMS_CameraMode> CameraModeClass)
|
||||
{
|
||||
}
|
||||
|
||||
bool UGCMS_CameraModeStack::EvaluateStack(float DeltaTime, FGCMS_CameraModeView& OutCameraModeView)
|
||||
{
|
||||
if (!bIsActive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Update camera modes.
|
||||
UpdateStack(DeltaTime);
|
||||
|
||||
//Blend values from camera modes.
|
||||
BlendStack(OutCameraModeView);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UGCMS_CameraMode* UGCMS_CameraModeStack::GetCameraModeInstance(TSubclassOf<UGCMS_CameraMode> CameraModeClass)
|
||||
{
|
||||
check(CameraModeClass);
|
||||
|
||||
// First see if we already created one.
|
||||
for (UGCMS_CameraMode* CameraMode : CameraModeInstances)
|
||||
{
|
||||
if ((CameraMode != nullptr) && (CameraMode->GetClass() == CameraModeClass))
|
||||
{
|
||||
return CameraMode;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, so we need to create it.
|
||||
UGCMS_CameraMode* NewCameraMode = NewObject<UGCMS_CameraMode>(GetOuter(), CameraModeClass, NAME_None, RF_NoFlags);
|
||||
check(NewCameraMode);
|
||||
|
||||
CameraModeInstances.Add(NewCameraMode);
|
||||
|
||||
return NewCameraMode;
|
||||
}
|
||||
|
||||
void UGCMS_CameraModeStack::UpdateStack(float DeltaTime)
|
||||
{
|
||||
const int32 StackSize = CameraModeStack.Num();
|
||||
if (StackSize <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int32 RemoveCount = 0;
|
||||
int32 RemoveIndex = INDEX_NONE;
|
||||
|
||||
for (int32 StackIndex = 0; StackIndex < StackSize; ++StackIndex)
|
||||
{
|
||||
UGCMS_CameraMode* CameraMode = CameraModeStack[StackIndex];
|
||||
check(CameraMode);
|
||||
|
||||
CameraMode->UpdateCameraMode(DeltaTime);
|
||||
|
||||
if (CameraMode->GetBlendWeight() >= 1.0f)
|
||||
{
|
||||
// Everything below this mode is now irrelevant and can be removed.
|
||||
RemoveIndex = (StackIndex + 1);
|
||||
RemoveCount = (StackSize - RemoveIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (RemoveCount > 0)
|
||||
{
|
||||
// Let the camera modes know they being removed from the stack.
|
||||
for (int32 StackIndex = RemoveIndex; StackIndex < StackSize; ++StackIndex)
|
||||
{
|
||||
UGCMS_CameraMode* CameraMode = CameraModeStack[StackIndex];
|
||||
check(CameraMode);
|
||||
|
||||
CameraMode->OnDeactivation();
|
||||
}
|
||||
|
||||
CameraModeStack.RemoveAt(RemoveIndex, RemoveCount);
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraModeStack::BlendStack(FGCMS_CameraModeView& OutCameraModeView) const
|
||||
{
|
||||
const int32 StackSize = CameraModeStack.Num();
|
||||
if (StackSize <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Start at the bottom and blend up the stack
|
||||
const UGCMS_CameraMode* CameraMode = CameraModeStack[StackSize - 1];
|
||||
check(CameraMode);
|
||||
|
||||
OutCameraModeView = CameraMode->GetCameraModeView();
|
||||
|
||||
for (int32 StackIndex = (StackSize - 2); StackIndex >= 0; --StackIndex)
|
||||
{
|
||||
CameraMode = CameraModeStack[StackIndex];
|
||||
check(CameraMode);
|
||||
|
||||
OutCameraModeView.Blend(CameraMode->GetCameraModeView(), CameraMode->GetBlendWeight());
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraModeStack::DrawDebug(UCanvas* Canvas) const
|
||||
{
|
||||
check(Canvas);
|
||||
|
||||
FDisplayDebugManager& DisplayDebugManager = Canvas->DisplayDebugManager;
|
||||
|
||||
DisplayDebugManager.SetDrawColor(FColor::Green);
|
||||
DisplayDebugManager.DrawString(FString(TEXT(" --- Camera Modes (Begin) ---")));
|
||||
|
||||
for (const UGCMS_CameraMode* CameraMode : CameraModeStack)
|
||||
{
|
||||
check(CameraMode);
|
||||
CameraMode->DrawDebug(Canvas);
|
||||
}
|
||||
|
||||
DisplayDebugManager.SetDrawColor(FColor::Green);
|
||||
DisplayDebugManager.DrawString(FString::Printf(TEXT(" --- Camera Modes (End) ---")));
|
||||
}
|
||||
|
||||
void UGCMS_CameraModeStack::GetBlendInfo(float& OutWeightOfTopLayer, FGameplayTag& OutTagOfTopLayer) const
|
||||
{
|
||||
if (CameraModeStack.Num() == 0)
|
||||
{
|
||||
OutWeightOfTopLayer = 1.0f;
|
||||
OutTagOfTopLayer = FGameplayTag();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
UGCMS_CameraMode* TopEntry = CameraModeStack.Last();
|
||||
check(TopEntry);
|
||||
OutWeightOfTopLayer = TopEntry->GetBlendWeight();
|
||||
OutTagOfTopLayer = TopEntry->GetCameraTypeTag();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// // Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
//
|
||||
// #include "GCMS_CameraMode_ThirdPerson.h"
|
||||
//
|
||||
// #include "GCMS_CameraAssistInterface.h"
|
||||
// #include "GCMS_CameraMode.h"
|
||||
// #include "Components/PrimitiveComponent.h"
|
||||
// #include "GCMS_CameraPenetrationAvoidanceFeeler.h"
|
||||
// #include "Curves/CurveVector.h"
|
||||
// #include "Engine/Canvas.h"
|
||||
// #include "GameFramework/Controller.h"
|
||||
// #include "GameFramework/Character.h"
|
||||
// #include "Math/RotationMatrix.h"
|
||||
//
|
||||
// #include UE_INLINE_GENERATED_CPP_BY_NAME(GCMS_CameraMode_ThirdPerson)
|
||||
//
|
||||
//
|
||||
// UGCMS_CameraMode_ThirdPerson::UGCMS_CameraMode_ThirdPerson()
|
||||
// {
|
||||
// TargetOffsetCurve = nullptr;
|
||||
// }
|
||||
//
|
||||
// void UGCMS_CameraMode_ThirdPerson::UpdateView_Implementation(float DeltaTime)
|
||||
// {
|
||||
// UpdateForTarget(DeltaTime);
|
||||
// UpdateCrouchOffset(DeltaTime);
|
||||
//
|
||||
// FVector PivotLocation = GetPivotLocation() + CurrentCrouchOffset;
|
||||
// FRotator PivotRotation = GetPivotRotation();
|
||||
//
|
||||
// PivotRotation.Pitch = FMath::ClampAngle(PivotRotation.Pitch, ViewPitchMin, ViewPitchMax);
|
||||
//
|
||||
// View.Location = PivotLocation;
|
||||
// View.Rotation = PivotRotation;
|
||||
// View.ControlRotation = View.Rotation;
|
||||
// View.FieldOfView = FieldOfView;
|
||||
//
|
||||
// // Apply third person offset using pitch.
|
||||
// if (!bUseRuntimeFloatCurves)
|
||||
// {
|
||||
// if (TargetOffsetCurve)
|
||||
// {
|
||||
// const FVector TargetOffset = TargetOffsetCurve->GetVectorValue(PivotRotation.Pitch);
|
||||
// View.Location = PivotLocation + PivotRotation.RotateVector(TargetOffset);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// FVector TargetOffset(0.0f);
|
||||
//
|
||||
// TargetOffset.X = TargetOffsetX.GetRichCurveConst()->Eval(PivotRotation.Pitch);
|
||||
// TargetOffset.Y = TargetOffsetY.GetRichCurveConst()->Eval(PivotRotation.Pitch);
|
||||
// TargetOffset.Z = TargetOffsetZ.GetRichCurveConst()->Eval(PivotRotation.Pitch);
|
||||
//
|
||||
// View.Location = PivotLocation + PivotRotation.RotateVector(TargetOffset);
|
||||
// }
|
||||
//
|
||||
// // Adjust final desired camera location to prevent any penetration
|
||||
// UpdatePreventPenetration(DeltaTime);
|
||||
// }
|
||||
//
|
||||
// void UGCMS_CameraMode_ThirdPerson::UpdateForTarget(float DeltaTime)
|
||||
// {
|
||||
// if (const ACharacter* TargetCharacter = Cast<ACharacter>(GetTargetActor()))
|
||||
// {
|
||||
// if (TargetCharacter->bIsCrouched)
|
||||
// {
|
||||
// const ACharacter* TargetCharacterCDO = TargetCharacter->GetClass()->GetDefaultObject<ACharacter>();
|
||||
// const float CrouchedHeightAdjustment = TargetCharacterCDO->CrouchedEyeHeight - TargetCharacterCDO->BaseEyeHeight;
|
||||
//
|
||||
// SetTargetCrouchOffset(FVector(0.f, 0.f, CrouchedHeightAdjustment));
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// SetTargetCrouchOffset(FVector::ZeroVector);
|
||||
// }
|
||||
//
|
||||
// void UGCMS_CameraMode_ThirdPerson::SetTargetCrouchOffset(FVector NewTargetOffset)
|
||||
// {
|
||||
// CrouchOffsetBlendPct = 0.0f;
|
||||
// InitialCrouchOffset = CurrentCrouchOffset;
|
||||
// TargetCrouchOffset = NewTargetOffset;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// void UGCMS_CameraMode_ThirdPerson::UpdateCrouchOffset(float DeltaTime)
|
||||
// {
|
||||
// if (CrouchOffsetBlendPct < 1.0f)
|
||||
// {
|
||||
// CrouchOffsetBlendPct = FMath::Min(CrouchOffsetBlendPct + DeltaTime * CrouchOffsetBlendMultiplier, 1.0f);
|
||||
// CurrentCrouchOffset = FMath::InterpEaseInOut(InitialCrouchOffset, TargetCrouchOffset, CrouchOffsetBlendPct, 1.0f);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// CurrentCrouchOffset = TargetCrouchOffset;
|
||||
// CrouchOffsetBlendPct = 1.0f;
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,290 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GCMS_CameraMode_WithPenetrationAvoidance.h"
|
||||
#include "Engine/Canvas.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "Components/PrimitiveComponent.h"
|
||||
#include "GameFramework/Controller.h"
|
||||
#include "Engine/HitResult.h"
|
||||
#include "GCMS_CameraAssistInterface.h"
|
||||
#include "GameFramework/CameraBlockingVolume.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GCMS_CameraMode_WithPenetrationAvoidance)
|
||||
|
||||
namespace GMS_CameraMode_WithPenetrationAvoidance_Statics
|
||||
{
|
||||
static const FName NAME_IgnoreCameraCollision = TEXT("IgnoreCameraCollision");
|
||||
}
|
||||
|
||||
|
||||
UGCMS_CameraMode_WithPenetrationAvoidance::UGCMS_CameraMode_WithPenetrationAvoidance()
|
||||
{
|
||||
PenetrationAvoidanceFeelers.Add(FGCMS_CameraPenetrationAvoidanceFeeler(FRotator(+00.0f, +00.0f, 0.0f), 1.00f, 1.00f, 14.f, 0));
|
||||
PenetrationAvoidanceFeelers.Add(FGCMS_CameraPenetrationAvoidanceFeeler(FRotator(+00.0f, +16.0f, 0.0f), 0.75f, 0.75f, 00.f, 3));
|
||||
PenetrationAvoidanceFeelers.Add(FGCMS_CameraPenetrationAvoidanceFeeler(FRotator(+00.0f, -16.0f, 0.0f), 0.75f, 0.75f, 00.f, 3));
|
||||
PenetrationAvoidanceFeelers.Add(FGCMS_CameraPenetrationAvoidanceFeeler(FRotator(+00.0f, +32.0f, 0.0f), 0.50f, 0.50f, 00.f, 5));
|
||||
PenetrationAvoidanceFeelers.Add(FGCMS_CameraPenetrationAvoidanceFeeler(FRotator(+00.0f, -32.0f, 0.0f), 0.50f, 0.50f, 00.f, 5));
|
||||
PenetrationAvoidanceFeelers.Add(FGCMS_CameraPenetrationAvoidanceFeeler(FRotator(+20.0f, +00.0f, 0.0f), 1.00f, 1.00f, 00.f, 4));
|
||||
PenetrationAvoidanceFeelers.Add(FGCMS_CameraPenetrationAvoidanceFeeler(FRotator(-20.0f, +00.0f, 0.0f), 0.50f, 0.50f, 00.f, 4));
|
||||
}
|
||||
|
||||
void UGCMS_CameraMode_WithPenetrationAvoidance::UpdatePreventPenetration(float DeltaTime)
|
||||
{
|
||||
if (!bPreventPenetration)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AActor* TargetActor = GetTargetActor();
|
||||
|
||||
APawn* TargetPawn = Cast<APawn>(TargetActor);
|
||||
AController* TargetController = TargetPawn ? TargetPawn->GetController() : nullptr;
|
||||
IGCMS_CameraAssistInterface* TargetControllerAssist = Cast<IGCMS_CameraAssistInterface>(TargetController);
|
||||
|
||||
IGCMS_CameraAssistInterface* TargetActorAssist = Cast<IGCMS_CameraAssistInterface>(TargetActor);
|
||||
|
||||
TOptional<AActor*> OptionalPPTarget = TargetActorAssist ? TargetActorAssist->GetCameraPreventPenetrationTarget() : TOptional<AActor*>();
|
||||
AActor* PPActor = OptionalPPTarget.IsSet() ? OptionalPPTarget.GetValue() : TargetActor;
|
||||
IGCMS_CameraAssistInterface* PPActorAssist = OptionalPPTarget.IsSet() ? Cast<IGCMS_CameraAssistInterface>(PPActor) : nullptr;
|
||||
|
||||
const UPrimitiveComponent* PPActorRootComponent = Cast<UPrimitiveComponent>(PPActor->GetRootComponent());
|
||||
if (PPActorRootComponent)
|
||||
{
|
||||
// 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;
|
||||
FVector SafeLocation = PPActor->GetActorLocation();
|
||||
FMath::PointDistToLine(SafeLocation, View.Rotation.Vector(), View.Location, ClosestPointOnLineToCapsuleCenter);
|
||||
|
||||
// Adjust Safe distance height to be same as aim line, but within capsule.
|
||||
float const PushInDistance = PenetrationAvoidanceFeelers[0].Extent + CollisionPushOutDistance;
|
||||
float const MaxHalfHeight = PPActor->GetSimpleCollisionHalfHeight() - PushInDistance;
|
||||
SafeLocation.Z = FMath::Clamp(ClosestPointOnLineToCapsuleCenter.Z, SafeLocation.Z - MaxHalfHeight, SafeLocation.Z + MaxHalfHeight);
|
||||
|
||||
float DistanceSqr;
|
||||
PPActorRootComponent->GetSquaredDistanceToCollision(ClosestPointOnLineToCapsuleCenter, DistanceSqr, SafeLocation);
|
||||
// Push back inside capsule to avoid initial penetration when doing line checks.
|
||||
if (PenetrationAvoidanceFeelers.Num() > 0)
|
||||
{
|
||||
SafeLocation += (SafeLocation - ClosestPointOnLineToCapsuleCenter).GetSafeNormal() * PushInDistance;
|
||||
}
|
||||
|
||||
// Then aim line to desired camera position
|
||||
bool const bSingleRayPenetrationCheck = !bDoPredictiveAvoidance;
|
||||
PreventCameraPenetration(bSingleRayPenetrationCheck, DeltaTime, PPActor, SafeLocation, View.Location, AimLineToDesiredPosBlockedPct);
|
||||
|
||||
IGCMS_CameraAssistInterface* AssistArray[] = {TargetControllerAssist, TargetActorAssist, PPActorAssist};
|
||||
|
||||
if (AimLineToDesiredPosBlockedPct < ReportPenetrationPercent)
|
||||
{
|
||||
for (IGCMS_CameraAssistInterface* Assist : AssistArray)
|
||||
{
|
||||
if (Assist)
|
||||
{
|
||||
// camera is too close, tell the assists
|
||||
Assist->OnCameraPenetratingTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraMode_WithPenetrationAvoidance::PreventCameraPenetration(bool bSingleRayOnly, const float& DeltaTime, const AActor* ViewTarget, FVector const& SafeLoc, FVector& CameraLoc,
|
||||
float& DistBlockedPct)
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
DebugActorsHitDuringCameraPenetration.Reset();
|
||||
#endif
|
||||
|
||||
float HardBlockedPct = DistBlockedPct;
|
||||
float SoftBlockedPct = DistBlockedPct;
|
||||
|
||||
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, PenetrationAvoidanceFeelers.Num()) : PenetrationAvoidanceFeelers.Num();
|
||||
FCollisionQueryParams SphereParams(SCENE_QUERY_STAT(CameraPen), false, nullptr/*PlayerCamera*/);
|
||||
|
||||
SphereParams.AddIgnoredActor(ViewTarget);
|
||||
|
||||
//TODO ILyraCameraTarget.GetIgnoredActorsForCameraPentration();
|
||||
//if (IgnoreActorForCameraPenetration)
|
||||
//{
|
||||
// SphereParams.AddIgnoredActor(IgnoreActorForCameraPenetration);
|
||||
//}
|
||||
|
||||
FCollisionShape SphereShape = FCollisionShape::MakeSphere(0.f);
|
||||
UWorld* World = GetWorld();
|
||||
|
||||
for (int32 RayIdx = 0; RayIdx < NumRaysToShoot; ++RayIdx)
|
||||
{
|
||||
FGCMS_CameraPenetrationAvoidanceFeeler& Feeler = PenetrationAvoidanceFeelers[RayIdx];
|
||||
if (Feeler.FramesUntilNextTrace <= 0)
|
||||
{
|
||||
// calc ray target
|
||||
FVector RayTarget;
|
||||
{
|
||||
FVector RotatedRay = BaseRay.RotateAngleAxis(Feeler.AdjustmentRot.Yaw, BaseRayLocalUp);
|
||||
RotatedRay = RotatedRay.RotateAngleAxis(Feeler.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
|
||||
SphereShape.Sphere.Radius = Feeler.Extent;
|
||||
ECollisionChannel TraceChannel = ECC_Camera; //(Feeler.PawnWeight > 0.f) ? ECC_Pawn : ECC_Camera;
|
||||
|
||||
// do multi-line check to make sure the hits we throw out aren't
|
||||
// masking real hits behind (these are important rays).
|
||||
|
||||
// MT-> 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
|
||||
|
||||
Feeler.FramesUntilNextTrace = Feeler.TraceInterval;
|
||||
|
||||
const AActor* HitActor = Hit.GetActor();
|
||||
|
||||
if (bHit && HitActor)
|
||||
{
|
||||
bool bIgnoreHit = false;
|
||||
|
||||
if (HitActor->ActorHasTag(GMS_CameraMode_WithPenetrationAvoidance_Statics::NAME_IgnoreCameraCollision))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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() - CollisionPushOutDistance) / (RayTarget - SafeLoc).Size();
|
||||
DistBlockedPctThisFrame = FMath::Min(NewBlockPct, DistBlockedPctThisFrame);
|
||||
|
||||
// This feeler got a hit, so do another trace next frame
|
||||
Feeler.FramesUntilNextTrace = 0;
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
DebugActorsHitDuringCameraPenetration.AddUnique(TObjectPtr<const AActor>(HitActor));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (RayIdx == 0)
|
||||
{
|
||||
// don't interpolate toward this one, snap to it
|
||||
// assumes ray 0 is the center/main ray
|
||||
HardBlockedPct = DistBlockedPctThisFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
SoftBlockedPct = DistBlockedPctThisFrame;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
--Feeler.FramesUntilNextTrace;
|
||||
}
|
||||
}
|
||||
|
||||
if (bResetInterpolation)
|
||||
{
|
||||
DistBlockedPct = DistBlockedPctThisFrame;
|
||||
}
|
||||
else if (DistBlockedPct < DistBlockedPctThisFrame)
|
||||
{
|
||||
// interpolate smoothly out
|
||||
if (PenetrationBlendOutTime > DeltaTime)
|
||||
{
|
||||
DistBlockedPct = DistBlockedPct + DeltaTime / PenetrationBlendOutTime * (DistBlockedPctThisFrame - DistBlockedPct);
|
||||
}
|
||||
else
|
||||
{
|
||||
DistBlockedPct = DistBlockedPctThisFrame;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DistBlockedPct > HardBlockedPct)
|
||||
{
|
||||
DistBlockedPct = HardBlockedPct;
|
||||
}
|
||||
else if (DistBlockedPct > SoftBlockedPct)
|
||||
{
|
||||
// interpolate smoothly in
|
||||
if (PenetrationBlendInTime > DeltaTime)
|
||||
{
|
||||
DistBlockedPct = DistBlockedPct - DeltaTime / PenetrationBlendInTime * (DistBlockedPct - SoftBlockedPct);
|
||||
}
|
||||
else
|
||||
{
|
||||
DistBlockedPct = SoftBlockedPct;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DistBlockedPct = FMath::Clamp<float>(DistBlockedPct, 0.f, 1.f);
|
||||
if (DistBlockedPct < (1.f - ZERO_ANIMWEIGHT_THRESH))
|
||||
{
|
||||
CameraLoc = SafeLoc + (CameraLoc - SafeLoc) * DistBlockedPct;
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraMode_WithPenetrationAvoidance::DrawDebug(UCanvas* Canvas) const
|
||||
{
|
||||
Super::DrawDebug(Canvas);
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
FDisplayDebugManager& DisplayDebugManager = Canvas->DisplayDebugManager;
|
||||
for (int i = 0; i < DebugActorsHitDuringCameraPenetration.Num(); i++)
|
||||
{
|
||||
DisplayDebugManager.DrawString(
|
||||
FString::Printf(TEXT("HitActorDuringPenetration[%d]: %s")
|
||||
, i
|
||||
, *DebugActorsHitDuringCameraPenetration[i]->GetName()));
|
||||
}
|
||||
|
||||
LastDrawDebugTime = GetWorld()->GetTimeSeconds();
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GCMS_CameraPenetrationAvoidanceFeeler.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GCMS_CameraPenetrationAvoidanceFeeler)
|
||||
@@ -0,0 +1,188 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "GCMS_CameraSystemComponent.h"
|
||||
#include "GameFramework/HUD.h"
|
||||
#include "DisplayDebugHelpers.h"
|
||||
#include "Engine/Canvas.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GCMS_CameraMode.h"
|
||||
#include "GCMS_CameraModeStack.h"
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GCMS_CameraSystemComponent)
|
||||
|
||||
|
||||
UGCMS_CameraSystemComponent::UGCMS_CameraSystemComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
CameraModeStack = nullptr;
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
FieldOfViewOffset = 0.0f;
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::OnShowDebugInfo(AHUD* HUD, UCanvas* Canvas, const FDebugDisplayInfo& DisplayInfo, float& YL, float& YPos)
|
||||
{
|
||||
if (DisplayInfo.IsDisplayOn(TEXT("CAMERA")))
|
||||
{
|
||||
if (const UGCMS_CameraSystemComponent* CameraComponent = GetCameraSystemComponent(HUD->GetCurrentDebugTargetActor()))
|
||||
{
|
||||
CameraComponent->DrawDebug(Canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::OnRegister()
|
||||
{
|
||||
Super::OnRegister();
|
||||
|
||||
if (!CameraModeStack)
|
||||
{
|
||||
CameraModeStack = NewObject<UGCMS_CameraModeStack>(this);
|
||||
check(CameraModeStack);
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
if (CameraModeStack && CameraModeStack->IsStackActivate() && AssociatedCameraComponent && AssociatedSprintArmComponent)
|
||||
{
|
||||
UpdateCameraModes();
|
||||
FGCMS_CameraModeView CameraModeView;
|
||||
CameraModeStack->EvaluateStack(DeltaTime, CameraModeView);
|
||||
|
||||
// Keep player controller in sync with the latest view.
|
||||
if (APawn* TargetPawn = Cast<APawn>(GetTargetActor()))
|
||||
{
|
||||
if (APlayerController* PC = TargetPawn->GetController<APlayerController>())
|
||||
{
|
||||
PC->SetControlRotation(CameraModeView.ControlRotation);
|
||||
}
|
||||
}
|
||||
|
||||
AssociatedCameraComponent->FieldOfView = CameraModeView.FieldOfView;
|
||||
|
||||
AssociatedSprintArmComponent->TargetArmLength = CameraModeView.SprintArmLength;
|
||||
|
||||
AssociatedSprintArmComponent->SocketOffset = CameraModeView.SprintArmSocketOffset;
|
||||
AssociatedSprintArmComponent->TargetOffset = CameraModeView.SprintArmTargetOffset;
|
||||
}
|
||||
}
|
||||
|
||||
UCameraComponent* UGCMS_CameraSystemComponent::GetAssociatedCamera() const
|
||||
{
|
||||
return AssociatedCameraComponent;
|
||||
}
|
||||
|
||||
USpringArmComponent* UGCMS_CameraSystemComponent::GetAssociatedSprintArm() const
|
||||
{
|
||||
return AssociatedSprintArmComponent;
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::Activate(bool bReset)
|
||||
{
|
||||
Super::Activate(bReset);
|
||||
if (CameraModeStack)
|
||||
{
|
||||
if (IsActive())
|
||||
{
|
||||
CameraModeStack->ActivateStack();
|
||||
}
|
||||
else
|
||||
{
|
||||
CameraModeStack->DeactivateStack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::Deactivate()
|
||||
{
|
||||
Super::Deactivate();
|
||||
if (CameraModeStack)
|
||||
{
|
||||
CameraModeStack->DeactivateStack();
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::UpdateCameraModes()
|
||||
{
|
||||
check(CameraModeStack);
|
||||
|
||||
if (CameraModeStack->IsStackActivate())
|
||||
{
|
||||
if (DetermineCameraModeDelegate.IsBound())
|
||||
{
|
||||
if (const TSubclassOf<UGCMS_CameraMode> CameraMode = DetermineCameraModeDelegate.Execute())
|
||||
{
|
||||
CameraModeStack->PushCameraMode(CameraMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::PushCameraMode(TSubclassOf<UGCMS_CameraMode> NewCameraMode)
|
||||
{
|
||||
if (CameraModeStack->IsStackActivate())
|
||||
{
|
||||
if (NewCameraMode)
|
||||
{
|
||||
CameraModeStack->PushCameraMode(NewCameraMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::PushDefaultCameraMode()
|
||||
{
|
||||
if (CameraModeStack->IsStackActivate())
|
||||
{
|
||||
if (DefaultCameraMode)
|
||||
{
|
||||
CameraModeStack->PushCameraMode(DefaultCameraMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::Initialize(UCameraComponent* NewCameraComponent, USpringArmComponent* NewSpringArmComponent)
|
||||
{
|
||||
if (!CameraModeStack)
|
||||
{
|
||||
CameraModeStack = NewObject<UGCMS_CameraModeStack>(this);
|
||||
check(CameraModeStack);
|
||||
}
|
||||
|
||||
AssociatedCameraComponent = NewCameraComponent;
|
||||
AssociatedSprintArmComponent = NewSpringArmComponent;
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::DrawDebug(UCanvas* Canvas) const
|
||||
{
|
||||
check(Canvas);
|
||||
|
||||
FDisplayDebugManager& DisplayDebugManager = Canvas->DisplayDebugManager;
|
||||
|
||||
DisplayDebugManager.SetFont(GEngine->GetSmallFont());
|
||||
DisplayDebugManager.SetDrawColor(FColor::Yellow);
|
||||
DisplayDebugManager.DrawString(FString::Printf(TEXT("GCMS_CameraSystemComponent: %s"), *GetNameSafe(GetTargetActor())));
|
||||
|
||||
DisplayDebugManager.SetDrawColor(FColor::White);
|
||||
DisplayDebugManager.DrawString(FString::Printf(TEXT(" Location: %s"), *AssociatedCameraComponent->GetComponentLocation().ToCompactString()));
|
||||
DisplayDebugManager.DrawString(FString::Printf(TEXT(" Rotation: %s"), *AssociatedCameraComponent->GetComponentRotation().ToCompactString()));
|
||||
DisplayDebugManager.DrawString(FString::Printf(TEXT(" SprintArmLength: %f"), AssociatedSprintArmComponent->TargetArmLength));
|
||||
DisplayDebugManager.DrawString(FString::Printf(TEXT(" SprintArmSocketOffset: %s"), *AssociatedSprintArmComponent->SocketOffset.ToCompactString()));
|
||||
DisplayDebugManager.DrawString(FString::Printf(TEXT(" SprintArmTargetOffset: %s"), *AssociatedSprintArmComponent->TargetOffset.ToCompactString()));
|
||||
|
||||
DisplayDebugManager.DrawString(FString::Printf(TEXT(" FOV: %f"), AssociatedCameraComponent->FieldOfView));
|
||||
|
||||
check(CameraModeStack);
|
||||
CameraModeStack->DrawDebug(Canvas);
|
||||
}
|
||||
|
||||
void UGCMS_CameraSystemComponent::GetBlendInfo(float& OutWeightOfTopLayer, FGameplayTag& OutTagOfTopLayer) const
|
||||
{
|
||||
check(CameraModeStack);
|
||||
CameraModeStack->GetBlendInfo(/*out*/ OutWeightOfTopLayer, /*out*/ OutTagOfTopLayer);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "GenericCameraSystem.h"
|
||||
|
||||
#include "GameFramework/HUD.h"
|
||||
#include "GCMS_CameraSystemComponent.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FGenericCameraSystemModule"
|
||||
|
||||
void FGenericCameraSystemModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
#if WITH_EDITOR
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
if (!IsRunningDedicatedServer())
|
||||
{
|
||||
AHUD::OnShowDebugInfo.AddStatic(&UGCMS_CameraSystemComponent::OnShowDebugInfo);
|
||||
}
|
||||
#endif // ENABLE_DRAW_DEBUG
|
||||
#endif
|
||||
}
|
||||
|
||||
void FGenericCameraSystemModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FGenericCameraSystemModule, GenericCameraSystem)
|
||||
Reference in New Issue
Block a user