From 57b53b9c0c336d71b27bc4b26e7adecd09c50bf2 Mon Sep 17 00:00:00 2001 From: LongLy Date: Mon, 20 Jan 2025 14:08:07 -0700 Subject: [PATCH] Implemented OLSCameraMode. Addressed @TODOs related to custom logs and OLSCameraMode --- .../Abilities/OLSGameplayAbility.cpp | 33 +- .../OLSAbilitySystemComponent.cpp | 52 +- .../Camera/Components/OLSCameraComponent.cpp | 134 +++++ .../OLSUICameraManagerComponent.cpp | 69 +++ .../Interfaces/OLSCameraAssistInterface.cpp | 20 + Source/ols/Private/Camera/OLSCameraMode.cpp | 495 ++++++++++++++++++ .../Private/Camera/OLSPlayerCameraManager.cpp | 61 +++ .../Private/Components/OLSHeroComponent.cpp | 37 +- Source/ols/Private/GameModes/OLSGameMode.cpp | 66 +-- .../Abilities/OLSGameplayAbility.h | 18 +- .../AbilitySystem/OLSAbilitySystemComponent.h | 2 +- .../Camera/Components/OLSCameraComponent.h | 61 +++ .../Components/OLSUICameraManagerComponent.h | 37 ++ .../Interfaces/OLSCameraAssistInterface.h | 39 ++ Source/ols/Public/Camera/OLSCameraMode.h | 191 +++++++ .../Public/Camera/OLSPlayerCameraManager.h | 40 ++ .../ols/Public/Components/OLSHeroComponent.h | 10 +- .../ols/Public/DataAssets/OLSPawnDataAsset.h | 5 +- Source/ols/Public/GameModes/OLSGameMode.h | 3 +- 19 files changed, 1283 insertions(+), 90 deletions(-) create mode 100644 Source/ols/Private/Camera/Components/OLSCameraComponent.cpp create mode 100644 Source/ols/Private/Camera/Components/OLSUICameraManagerComponent.cpp create mode 100644 Source/ols/Private/Camera/Interfaces/OLSCameraAssistInterface.cpp create mode 100644 Source/ols/Private/Camera/OLSCameraMode.cpp create mode 100644 Source/ols/Private/Camera/OLSPlayerCameraManager.cpp create mode 100644 Source/ols/Public/Camera/Components/OLSCameraComponent.h create mode 100644 Source/ols/Public/Camera/Components/OLSUICameraManagerComponent.h create mode 100644 Source/ols/Public/Camera/Interfaces/OLSCameraAssistInterface.h create mode 100644 Source/ols/Public/Camera/OLSCameraMode.h create mode 100644 Source/ols/Public/Camera/OLSPlayerCameraManager.h diff --git a/Source/ols/Private/AbilitySystem/Abilities/OLSGameplayAbility.cpp b/Source/ols/Private/AbilitySystem/Abilities/OLSGameplayAbility.cpp index 857bbc9..996c5b8 100644 --- a/Source/ols/Private/AbilitySystem/Abilities/OLSGameplayAbility.cpp +++ b/Source/ols/Private/AbilitySystem/Abilities/OLSGameplayAbility.cpp @@ -11,6 +11,7 @@ #include "AbilitySystemGlobals.h" #include "AbilitySystem/OLSGameplayEffectContext.h" #include "AbilitySystem/Interfaces/OLSAbilitySourceInterface.h" +#include "Camera/OLSCameraMode.h" #include "Components/OLSHeroComponent.h" #include "GameFramework/GameplayMessageSubsystem.h" #include "Physics/OLSPhysicalMaterialWithTags.h" @@ -43,8 +44,7 @@ UOLSGameplayAbility::UOLSGameplayAbility(const FObjectInitializer& objectInitial bShouldLogCancellation = false; - // @TODO: Implement OLSCameraMode. - // ActiveCameraMode = nullptr; + ActiveCameraMode = nullptr; } bool UOLSGameplayAbility::CanActivateAbility(const FGameplayAbilitySpecHandle handle, @@ -122,8 +122,7 @@ void UOLSGameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) { - // @TODO: Implement UOLSCameraMode. - // ClearCameraMode(); + ClearCameraMode(); Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); } @@ -450,6 +449,32 @@ bool UOLSGameplayAbility::ChangeActivationGroup(EOLSAbilityActivationGroup newGr return true; } +void UOLSGameplayAbility::SetCameraMode(TSubclassOf cameraMode) +{ + ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(SetCameraMode, ); + + if (UOLSHeroComponent* heroComponent = GetHeroComponentFromActorInfo()) + { + heroComponent->SetAbilityCameraMode(cameraMode, CurrentSpecHandle); + ActiveCameraMode = cameraMode; + } +} + +void UOLSGameplayAbility::ClearCameraMode() +{ + ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(ClearCameraMode, ); + + if (ActiveCameraMode) + { + if (UOLSHeroComponent* heroComponent = GetHeroComponentFromActorInfo()) + { + heroComponent->ClearAbilityCameraMode(CurrentSpecHandle); + } + + ActiveCameraMode = nullptr; + } +} + EOLSAbilityActivationPolicy UOLSGameplayAbility::GetActivationPolicy() const { return ActivationPolicy; diff --git a/Source/ols/Private/AbilitySystem/OLSAbilitySystemComponent.cpp b/Source/ols/Private/AbilitySystem/OLSAbilitySystemComponent.cpp index 4f9c9f2..105daec 100644 --- a/Source/ols/Private/AbilitySystem/OLSAbilitySystemComponent.cpp +++ b/Source/ols/Private/AbilitySystem/OLSAbilitySystemComponent.cpp @@ -302,8 +302,7 @@ void UOLSAbilitySystemComponent::TryActivateAbilitiesOnSpawn() { if (const UOLSGameplayAbility* abilityCDO = Cast(abilitySpec.Ability)) { - // @TODO: Implement UOLSGameplayAbility. - // abilityCDO->TryActivateAbilityOnSpawn(AbilityActorInfo.Get(), abilitySpec); + abilityCDO->TryActivateAbilityOnSpawn(AbilityActorInfo.Get(), abilitySpec); } } } @@ -355,8 +354,7 @@ void UOLSAbilitySystemComponent::NotifyAbilityActivated(const FGameplayAbilitySp if (UOLSGameplayAbility* olsAbility = Cast(ability)) { - // @TODO: Implement UOLSGameplayAbility. - // AddAbilityToActivationGroup(olsAbility->GetActivationGroup(), ability); + AddAbilityToActivationGroup(olsAbility->GetActivationGroup(), olsAbility); } } @@ -378,15 +376,15 @@ void UOLSAbilitySystemComponent::NotifyAbilityFailed(const FGameplayAbilitySpecH HandleAbilityFailed(ability, failureReason); } -void UOLSAbilitySystemComponent::NotifyAbilityEnded(FGameplayAbilitySpecHandle handle, UGameplayAbility* Ability, +void UOLSAbilitySystemComponent::NotifyAbilityEnded(FGameplayAbilitySpecHandle handle, + UGameplayAbility* ability, bool wasCancelled) { - Super::NotifyAbilityEnded(handle, Ability, wasCancelled); + Super::NotifyAbilityEnded(handle, ability, wasCancelled); - if (UOLSGameplayAbility* olsAbility = Cast(Ability)) + if (UOLSGameplayAbility* olsAbility = Cast(ability)) { - // @TODO: Implement UOLSGameplayAbility. - // RemoveAbilityFromActivationGroup(olsAbility->GetActivationGroup(), olsAbility); + RemoveAbilityFromActivationGroup(olsAbility->GetActivationGroup(), olsAbility); } } @@ -432,8 +430,7 @@ void UOLSAbilitySystemComponent::HandleAbilityFailed(const UGameplayAbility* abi { if (const UOLSGameplayAbility* olsAbility = Cast(ability)) { - // @TODO: Implement UOLSGameplayAbility. - // olsAbility->OnAbilityFailedToActivate(failureReason); + olsAbility->OnAbilityFailedToActivate(failureReason); } } @@ -514,14 +511,13 @@ void UOLSAbilitySystemComponent::CancelAbilitiesByFunc(TShouldCancelAbilityFunc void UOLSAbilitySystemComponent::CancelInputActivatedAbilities(bool shouldReplicateCancelAbility) { - // @TODO: Implement UOLSGameplayAbility - // auto shouldCancelFunc = [this](const UOLSGameplayAbility* ability, FGameplayAbilitySpecHandle handle) - // { - // const ELyraAbilityActivationPolicy ActivationPolicy = ability->GetActivationPolicy(); - // return ((ActivationPolicy == ELyraAbilityActivationPolicy::OnInputTriggered) || (ActivationPolicy == ELyraAbilityActivationPolicy::WhileInputActive)); - // }; - // - // CancelAbilitiesByFunc(shouldCancelFunc, shouldReplicateCancelAbility); + auto shouldCancelFunc = [this](const UOLSGameplayAbility* ability, FGameplayAbilitySpecHandle handle) + { + const EOLSAbilityActivationPolicy activationPolicy = ability->GetActivationPolicy(); + return ((activationPolicy == EOLSAbilityActivationPolicy::OnInputTriggered) || (activationPolicy == EOLSAbilityActivationPolicy::WhileInputActive)); + }; + + CancelAbilitiesByFunc(shouldCancelFunc, shouldReplicateCancelAbility); } void UOLSAbilitySystemComponent::AbilityInputTagPressed(const FGameplayTag& inputTag) @@ -578,11 +574,10 @@ void UOLSAbilitySystemComponent::ProcessAbilityInput(float deltaTime, bool shoul { const UOLSGameplayAbility* abilityCDO = Cast(abilitySpec->Ability); - // @TODO: Implement UOLSGameplayAbility. - // if (abilityCDO && abilityCDO->GetActivationPolicy() == ELyraAbilityActivationPolicy::WhileInputActive) - // { - // AbilitiesToActivate.AddUnique(AbilitySpec->Handle); - // } + if (abilityCDO && abilityCDO->GetActivationPolicy() == EOLSAbilityActivationPolicy::WhileInputActive) + { + abilitiesToActivate.AddUnique(abilitySpec->Handle); + } } } } @@ -607,11 +602,10 @@ void UOLSAbilitySystemComponent::ProcessAbilityInput(float deltaTime, bool shoul { const UOLSGameplayAbility* abilityCDO = Cast(abilitySpec->Ability); - // @TODO: Implement UOLSGameplayAbility. - // if (abilityCDO && abilityCDO->GetActivationPolicy() == ELyraAbilityActivationPolicy::OnInputTriggered) - // { - // abilitiesToActivate.AddUnique(abilitySpec->Handle); - // } + if (abilityCDO && abilityCDO->GetActivationPolicy() == EOLSAbilityActivationPolicy::OnInputTriggered) + { + abilitiesToActivate.AddUnique(abilitySpec->Handle); + } } } } diff --git a/Source/ols/Private/Camera/Components/OLSCameraComponent.cpp b/Source/ols/Private/Camera/Components/OLSCameraComponent.cpp new file mode 100644 index 0000000..88e46f5 --- /dev/null +++ b/Source/ols/Private/Camera/Components/OLSCameraComponent.cpp @@ -0,0 +1,134 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + + +#include "Camera/Components/OLSCameraComponent.h" + +#include "Camera/OLSCameraMode.h" +#include "Engine/Canvas.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSCameraComponent) + +UOLSCameraComponent::UOLSCameraComponent(const FObjectInitializer& objectInitializer) : Super(objectInitializer) +{ + CameraModeStack = nullptr; + FieldOfViewOffset = 0.0f; +} + +UOLSCameraComponent* UOLSCameraComponent::FindCameraComponent(const AActor* Actor) +{ + return (Actor ? Actor->FindComponentByClass() : nullptr); +} + +AActor* UOLSCameraComponent::GetTargetActor() const +{ + return GetOwner(); +} + +void UOLSCameraComponent::AddFieldOfViewOffset(float FovOffset) +{ + FieldOfViewOffset += FovOffset; +} + +void UOLSCameraComponent::DrawDebug(UCanvas* canvas) const +{ + check(canvas); + + FDisplayDebugManager& DisplayDebugManager = canvas->DisplayDebugManager; + + DisplayDebugManager.SetFont(GEngine->GetSmallFont()); + DisplayDebugManager.SetDrawColor(FColor::Yellow); + DisplayDebugManager.DrawString(FString::Printf(TEXT("LyraCameraComponent: %s"), *GetNameSafe(GetTargetActor()))); + + DisplayDebugManager.SetDrawColor(FColor::White); + DisplayDebugManager.DrawString(FString::Printf(TEXT(" Location: %s"), *GetComponentLocation().ToCompactString())); + DisplayDebugManager.DrawString(FString::Printf(TEXT(" Rotation: %s"), *GetComponentRotation().ToCompactString())); + DisplayDebugManager.DrawString(FString::Printf(TEXT(" FOV: %f"), FieldOfView)); + + check(CameraModeStack); + CameraModeStack->DrawDebug(canvas); +} + +void UOLSCameraComponent::GetBlendInfo(float& outWeightOfTopLayer, FGameplayTag& outTagOfTopLayer) const +{ + check(CameraModeStack); + CameraModeStack->GetBlendInfo(/*out*/ outWeightOfTopLayer, /*out*/ outTagOfTopLayer); +} + +void UOLSCameraComponent::OnRegister() +{ + Super::OnRegister(); + + if (!CameraModeStack) + { + CameraModeStack = NewObject(this); + check(CameraModeStack); + } +} + +void UOLSCameraComponent::GetCameraView(float deltaTime, FMinimalViewInfo& desiredView) +{ + check(CameraModeStack); + + UpdateCameraModes(); + + FOLSCameraModeView cameraModeView; + CameraModeStack->EvaluateStack(deltaTime, cameraModeView); + + // Keep player controller in sync with the latest view. + if (APawn* targetPawn = Cast(GetTargetActor())) + { + if (APlayerController* playerController = targetPawn->GetController()) + { + playerController->SetControlRotation(cameraModeView.ControlRotation); + } + } + + // Apply any offset that was added to the field of view. + cameraModeView.FieldOfView += FieldOfViewOffset; + FieldOfViewOffset = 0.0f; + + // Keep camera component in sync with the latest view. + SetWorldLocationAndRotation(cameraModeView.Location, cameraModeView.Rotation); + FieldOfView = cameraModeView.FieldOfView; + + // Fill in desired view. + desiredView.Location = cameraModeView.Location; + desiredView.Rotation = cameraModeView.Rotation; + desiredView.FOV = cameraModeView.FieldOfView; + desiredView.OrthoWidth = OrthoWidth; + desiredView.OrthoNearClipPlane = OrthoNearClipPlane; + desiredView.OrthoFarClipPlane = OrthoFarClipPlane; + desiredView.AspectRatio = AspectRatio; + desiredView.bConstrainAspectRatio = bConstrainAspectRatio; + desiredView.bUseFieldOfViewForLOD = bUseFieldOfViewForLOD; + desiredView.ProjectionMode = ProjectionMode; + + // See if the CameraActor wants to override the PostProcess settings used. + desiredView.PostProcessBlendWeight = PostProcessBlendWeight; + if (PostProcessBlendWeight > 0.0f) + { + desiredView.PostProcessSettings = PostProcessSettings; + } + + if (IsXRHeadTrackedCamera()) + { + // In XR much of the camera behavior above is irrellevant, but the post process settings are not. + Super::GetCameraView(deltaTime, desiredView); + } +} + +void UOLSCameraComponent::UpdateCameraModes() +{ + check(CameraModeStack); + + if (CameraModeStack->IsStackActivate()) + { + if (DetermineCameraModeDelegate.IsBound()) + { + if (const TSubclassOf cameraMode = DetermineCameraModeDelegate.Execute()) + { + CameraModeStack->PushCameraMode(cameraMode); + } + } + } +} diff --git a/Source/ols/Private/Camera/Components/OLSUICameraManagerComponent.cpp b/Source/ols/Private/Camera/Components/OLSUICameraManagerComponent.cpp new file mode 100644 index 0000000..5b2f21c --- /dev/null +++ b/Source/ols/Private/Camera/Components/OLSUICameraManagerComponent.cpp @@ -0,0 +1,69 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + + +#include "Camera/Components/OLSUICameraManagerComponent.h" + +#include "Camera/OLSPlayerCameraManager.h" +#include "GameFramework/HUD.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSUICameraManagerComponent) + +UOLSUICameraManagerComponent* UOLSUICameraManagerComponent::GetComponent(APlayerController* playerController) +{ + if (playerController) + { + if (AOLSPlayerCameraManager* playerCamera = Cast(playerController->PlayerCameraManager)) + { + return playerCamera->GetUICameraComponent(); + } + } + + return nullptr; +} + +UOLSUICameraManagerComponent::UOLSUICameraManagerComponent() +{ + bWantsInitializeComponent = true; + + if (!HasAnyFlags(RF_ClassDefaultObject)) + { + // Register "showdebug" hook. + if (!IsRunningDedicatedServer()) + { + AHUD::OnShowDebugInfo.AddUObject(this, &ThisClass::OnShowDebugInfo); + } + } +} + +bool UOLSUICameraManagerComponent::IsSettingViewTarget() const +{ + return bShouldUpdatingViewTarget; +} + +AActor* UOLSUICameraManagerComponent::GetViewTarget() const +{ + return ViewTarget; +} + +void UOLSUICameraManagerComponent::SetViewTarget(AActor* viewTarget, FViewTargetTransitionParams transitionParams) +{ + TGuardValue UpdatingViewTargetGuard(bShouldUpdatingViewTarget, true); + + ViewTarget = viewTarget; + CastChecked(GetOwner())->SetViewTarget(ViewTarget, transitionParams); +} + +bool UOLSUICameraManagerComponent::NeedsToUpdateViewTarget() const +{ + return false; +} + +void UOLSUICameraManagerComponent::UpdateViewTarget(FTViewTarget& outVT, float deltaTime) +{ +} + +void UOLSUICameraManagerComponent::OnShowDebugInfo(AHUD* hud, UCanvas* canvas, + const FDebugDisplayInfo& displayInfo, + float& yl, float& yPos) +{ +} diff --git a/Source/ols/Private/Camera/Interfaces/OLSCameraAssistInterface.cpp b/Source/ols/Private/Camera/Interfaces/OLSCameraAssistInterface.cpp new file mode 100644 index 0000000..bde6fed --- /dev/null +++ b/Source/ols/Private/Camera/Interfaces/OLSCameraAssistInterface.cpp @@ -0,0 +1,20 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + + +#include "Camera/Interfaces/OLSCameraAssistInterface.h" + + +// Add default functionality here for any IOLSCameraAssistInterface functions that are not pure virtual. +void IOLSCameraAssistInterface::GetIgnoredActorsForCameraPenetration( + TArray& outActorsAllowPenetration) const +{ +} + +TOptional IOLSCameraAssistInterface::GetCameraPreventPenetrationTarget() const +{ + return TOptional(); +} + +void IOLSCameraAssistInterface::OnCameraPenetratingTarget() +{ +} diff --git a/Source/ols/Private/Camera/OLSCameraMode.cpp b/Source/ols/Private/Camera/OLSCameraMode.cpp new file mode 100644 index 0000000..d1da043 --- /dev/null +++ b/Source/ols/Private/Camera/OLSCameraMode.cpp @@ -0,0 +1,495 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + + +#include "Camera/OLSCameraMode.h" + +#include "Camera/OLSPlayerCameraManager.h" +#include "Camera/Components/OLSCameraComponent.h" +#include "Components/CapsuleComponent.h" +#include "Engine/Canvas.h" +#include "GameFramework/Character.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSCameraMode) + +////////////////////////////////////////////////////////////////////////// +// FLyraCameraModeView +////////////////////////////////////////////////////////////////////////// + +FOLSCameraModeView::FOLSCameraModeView() + : Location(ForceInit) + , Rotation(ForceInit) + , ControlRotation(ForceInit) + , FieldOfView(OLS_CAMERA_DEFAULT_FOV) +{ +} + +void FOLSCameraModeView::Blend(const FOLSCameraModeView& 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); + + FieldOfView = FMath::Lerp(FieldOfView, other.FieldOfView, otherWeight); +} + +////////////////////////////////////////////////////////////////////////// +// UOLSCameraMode +////////////////////////////////////////////////////////////////////////// + +UOLSCameraMode::UOLSCameraMode() +{ + FieldOfView = OLS_CAMERA_DEFAULT_FOV; + ViewPitchMin = OLS_CAMERA_DEFAULT_PITCH_MIN; + ViewPitchMax = OLS_CAMERA_DEFAULT_PITCH_MAX; + + BlendTime = 0.5f; + BlendFunction = EOLSCameraModeBlendFunction::EaseOut; + BlendExponent = 4.0f; + BlendAlpha = 1.0f; + BlendWeight = 1.0f; +} + +UOLSCameraComponent* UOLSCameraMode::GetOLSCameraComponent() const +{ + return CastChecked(GetOuter()); +} + +UWorld* UOLSCameraMode::GetWorld() const +{ + return HasAnyFlags(RF_ClassDefaultObject) ? nullptr : GetOuter()->GetWorld(); +} + +AActor* UOLSCameraMode::GetTargetActor() const +{ + const UOLSCameraComponent* cameraComponent = GetOLSCameraComponent(); + + return cameraComponent->GetTargetActor(); +} + +const FOLSCameraModeView& UOLSCameraMode::GetCameraModeView() const +{ + return View; +} + +void UOLSCameraMode::OnActivation() +{ +} + +void UOLSCameraMode::OnDeactivation() +{ +} + +void UOLSCameraMode::UpdateCameraMode(float deltaTime) +{ + UpdateView(deltaTime); + UpdateBlending(deltaTime); +} + +float UOLSCameraMode::GetBlendTime() const +{ + return BlendTime; +} + +float UOLSCameraMode::GetBlendWeight() const +{ + return BlendWeight; +} + +void UOLSCameraMode::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 EOLSCameraModeBlendFunction::Linear: + BlendAlpha = BlendWeight; + break; + + case EOLSCameraModeBlendFunction::EaseIn: + BlendAlpha = FMath::InterpEaseIn(0.0f, 1.0f, BlendWeight, InvExponent); + break; + + case EOLSCameraModeBlendFunction::EaseOut: + BlendAlpha = FMath::InterpEaseOut(0.0f, 1.0f, BlendWeight, InvExponent); + break; + + case EOLSCameraModeBlendFunction::EaseInOut: + BlendAlpha = FMath::InterpEaseInOut(0.0f, 1.0f, BlendWeight, InvExponent); + break; + + default: + checkf(false, TEXT("SetBlendWeight: Invalid BlendFunction [%d]\n"), (uint8)BlendFunction); + break; + } +} + +FGameplayTag UOLSCameraMode::GetCameraTypeTag() const +{ + return CameraTypeTag; +} + +void UOLSCameraMode::DrawDebug(UCanvas* canvas) const +{ + check(canvas); + + FDisplayDebugManager& displayDebugManager = canvas->DisplayDebugManager; + + displayDebugManager.SetDrawColor(FColor::White); + displayDebugManager.DrawString(FString::Printf(TEXT(" OLSCameraMode: %s (%f)"), *GetName(), BlendWeight)); +} + +FVector UOLSCameraMode::GetPivotLocation() const +{ + const AActor* targetActor = GetTargetActor(); + check(targetActor); + + if (const APawn* targetPawn = Cast(targetActor)) + { + // Height adjustments for characters to account for crouching. + if (const ACharacter* targetCharacter = Cast(targetPawn)) + { + const ACharacter* targetCharacterCDO = targetCharacter->GetClass()->GetDefaultObject(); + 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 UOLSCameraMode::GetPivotRotation() const +{ + const AActor* targetActor = GetTargetActor(); + check(targetActor); + + if (const APawn* targetPawn = Cast(targetActor)) + { + return targetPawn->GetViewRotation(); + } + + return targetActor->GetActorRotation(); +} + +void UOLSCameraMode::UpdateView(float deltaTime) +{ + FVector pivotLocation = GetPivotLocation(); + FRotator pivotRotation = GetPivotRotation(); + + pivotRotation.Pitch = FMath::ClampAngle(pivotRotation.Pitch, ViewPitchMin, ViewPitchMax); + + View.Location = pivotLocation; + View.Rotation = pivotRotation; + View.ControlRotation = View.Rotation; + View.FieldOfView = FieldOfView; +} + +void UOLSCameraMode::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 EOLSCameraModeBlendFunction::Linear: + BlendWeight = BlendAlpha; + break; + + case EOLSCameraModeBlendFunction::EaseIn: + BlendWeight = FMath::InterpEaseIn(0.0f, 1.0f, BlendAlpha, exponent); + break; + + case EOLSCameraModeBlendFunction::EaseOut: + BlendWeight = FMath::InterpEaseOut(0.0f, 1.0f, BlendAlpha, exponent); + break; + + case EOLSCameraModeBlendFunction::EaseInOut: + BlendWeight = FMath::InterpEaseInOut(0.0f, 1.0f, BlendAlpha, exponent); + break; + + default: + checkf(false, TEXT("UpdateBlending: Invalid BlendFunction [%d]\n"), (uint8)BlendFunction); + break; + } +} + +////////////////////////////////////////////////////////////////////////// +// UOLSCameraModeStack +////////////////////////////////////////////////////////////////////////// + +UOLSCameraModeStack::UOLSCameraModeStack() +{ + bIsStackActive = true; +} + +void UOLSCameraModeStack::ActivateStack() +{ + if (!IsStackActivate()) + { + bIsStackActive = true; + + // Notify camera modes that they are being activated. + for (UOLSCameraMode* cameraMode : CameraModeStack) + { + check(cameraMode); + cameraMode->OnActivation(); + } + } +} + +void UOLSCameraModeStack::DeactivateStack() +{ + if (IsStackActivate()) + { + bIsStackActive = false; + + // Notify camera modes that they are being deactivated. + for (UOLSCameraMode* cameraMode : CameraModeStack) + { + check(cameraMode); + cameraMode->OnDeactivation(); + } + } +} + +bool UOLSCameraModeStack::IsStackActivate() const +{ + return bIsStackActive; +} + +void UOLSCameraModeStack::PushCameraMode(TSubclassOf cameraModeClass) +{ + if (!cameraModeClass) + { + return; + } + + UOLSCameraMode* cameraMode = GetCameraModeInstance(cameraModeClass); + check(cameraMode); + + int32 stackSize = CameraModeStack.Num(); + + if ((stackSize > 0) && (CameraModeStack[0] == cameraMode)) + { + // 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] == cameraMode) + { + existingStackIndex = stackIndex; + existingStackContribution *= cameraMode->GetBlendWeight(); + break; + } + else + { + existingStackContribution *= (1.0f - CameraModeStack[stackIndex]->GetBlendWeight()); + } + } + + if (existingStackIndex != INDEX_NONE) + { + CameraModeStack.RemoveAt(existingStackIndex); + stackSize--; + } + else + { + existingStackContribution = 0.0f; + } + + // Decide what initial weight to start with. + const bool shouldBlend = ((cameraMode->GetBlendTime() > 0.0f) && (stackSize > 0)); + const float blendWeight = (shouldBlend ? existingStackContribution : 1.0f); + + cameraMode->SetBlendWeight(blendWeight); + + // Add new entry to top of stack. + CameraModeStack.Insert(cameraMode, 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) + { + cameraMode->OnActivation(); + } +} + +bool UOLSCameraModeStack::EvaluateStack(float deltaTime, FOLSCameraModeView& outCameraModeView) +{ + if (!IsStackActivate()) + { + return false; + } + + UpdateStack(deltaTime); + BlendStack(outCameraModeView); + return true; +} + +void UOLSCameraModeStack::DrawDebug(UCanvas* canvas) const +{ + check(canvas); + + FDisplayDebugManager& displayDebugManager = canvas->DisplayDebugManager; + + displayDebugManager.SetDrawColor(FColor::Green); + displayDebugManager.DrawString(FString(TEXT(" --- Camera Modes (Begin) ---"))); + + for (const UOLSCameraMode* cameraMode : CameraModeStack) + { + check(cameraMode); + cameraMode->DrawDebug(canvas); + } + + displayDebugManager.SetDrawColor(FColor::Green); + displayDebugManager.DrawString(FString::Printf(TEXT(" --- Camera Modes (End) ---"))); +} + +void UOLSCameraModeStack::GetBlendInfo(float& outWeightOfTopLayer, FGameplayTag& outTagOfTopLayer) const +{ + if (CameraModeStack.Num() == 0) + { + outWeightOfTopLayer = 1.0f; + outTagOfTopLayer = FGameplayTag(); + return; + } + + UOLSCameraMode* topEntry = CameraModeStack.Last(); + check(topEntry); + outWeightOfTopLayer = topEntry->GetBlendWeight(); + outTagOfTopLayer = topEntry->GetCameraTypeTag(); +} + +UOLSCameraMode* UOLSCameraModeStack::GetCameraModeInstance(TSubclassOf cameraModeClass) +{ + check(cameraModeClass); + + // First see if we already created one. + for (UOLSCameraMode* cameraMode : CameraModeInstances) + { + if ((cameraMode != nullptr) && (cameraMode->GetClass() == cameraModeClass)) + { + return cameraMode; + } + } + + // Not found, so we need to create it. + UOLSCameraMode* newCameraMode = NewObject(GetOuter(), cameraModeClass, NAME_None, RF_NoFlags); + check(newCameraMode); + + CameraModeInstances.Add(newCameraMode); + + return newCameraMode; +} + +void UOLSCameraModeStack::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) + { + UOLSCameraMode* 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) + { + UOLSCameraMode* cameraMode = CameraModeStack[stackIndex]; + check(cameraMode); + + cameraMode->OnDeactivation(); + } + + CameraModeStack.RemoveAt(removeIndex, removeCount); + } +} + +void UOLSCameraModeStack::BlendStack(FOLSCameraModeView& outCameraModeView) const +{ + const int32 stackSize = CameraModeStack.Num(); + if (stackSize <= 0) + { + return; + } + + // Start at the bottom and blend up the stack + const UOLSCameraMode* 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()); + } +} diff --git a/Source/ols/Private/Camera/OLSPlayerCameraManager.cpp b/Source/ols/Private/Camera/OLSPlayerCameraManager.cpp new file mode 100644 index 0000000..a69bd92 --- /dev/null +++ b/Source/ols/Private/Camera/OLSPlayerCameraManager.cpp @@ -0,0 +1,61 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + + +#include "Camera/OLSPlayerCameraManager.h" + +#include "Camera/Components/OLSCameraComponent.h" +#include "Camera/Components/OLSUICameraManagerComponent.h" +#include "Engine/Canvas.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSPlayerCameraManager) + +static FName UICameraComponentName(TEXT("UICameraComponent")); + +AOLSPlayerCameraManager::AOLSPlayerCameraManager(const FObjectInitializer& objectInitializer) + : Super(objectInitializer) +{ + DefaultFOV = OLS_CAMERA_DEFAULT_FOV; + ViewPitchMin = OLS_CAMERA_DEFAULT_PITCH_MIN; + ViewPitchMax = OLS_CAMERA_DEFAULT_PITCH_MAX; + + UICameraComponent = CreateDefaultSubobject(UICameraComponentName); +} + +UOLSUICameraManagerComponent* AOLSPlayerCameraManager::GetUICameraComponent() const +{ + return UICameraComponent; +} + +void AOLSPlayerCameraManager::UpdateViewTarget(FTViewTarget& outVT, float deltaTime) +{ + // If the UI Camera is looking at something, let it have priority. + if (UICameraComponent->NeedsToUpdateViewTarget()) + { + Super::UpdateViewTarget(outVT, deltaTime); + UICameraComponent->UpdateViewTarget(outVT, deltaTime); + return; + } + + Super::UpdateViewTarget(outVT, deltaTime); +} + +void AOLSPlayerCameraManager::DisplayDebug(UCanvas* canvas, const FDebugDisplayInfo& debugDisplay, + float& yl, float& yPos) +{ + check(canvas); + + FDisplayDebugManager& displayDebugManager = canvas->DisplayDebugManager; + + displayDebugManager.SetFont(GEngine->GetSmallFont()); + displayDebugManager.SetDrawColor(FColor::Yellow); + displayDebugManager.DrawString(FString::Printf(TEXT("LyraPlayerCameraManager: %s"), *GetNameSafe(this))); + + Super::DisplayDebug(canvas, debugDisplay, yl, yPos); + + const APawn* pawn = (PCOwner ? PCOwner->GetPawn() : nullptr); + + if (const UOLSCameraComponent* cameraComponent = UOLSCameraComponent::FindCameraComponent(pawn)) + { + cameraComponent->DrawDebug(canvas); + } +} diff --git a/Source/ols/Private/Components/OLSHeroComponent.cpp b/Source/ols/Private/Components/OLSHeroComponent.cpp index 78df7e4..dc0600f 100644 --- a/Source/ols/Private/Components/OLSHeroComponent.cpp +++ b/Source/ols/Private/Components/OLSHeroComponent.cpp @@ -6,6 +6,7 @@ #include "EnhancedInputSubsystems.h" #include "OLSLog.h" #include "AbilitySystem/OLSAbilitySystemComponent.h" +#include "Camera/OLSCameraMode.h" #include "Characters/OLSCharacter.h" #include "Components/GameFrameworkComponentManager.h" #include "Components/OLSPawnExtensionComponent.h" @@ -31,8 +32,7 @@ const FName UOLSHeroComponent::NAME_ActorFeatureName("Hero"); UOLSHeroComponent::UOLSHeroComponent(const FObjectInitializer& objectInitializer) : Super(objectInitializer) { - //@TODO: implement UOLSCameraMode. - // AbilityCameraMode = nullptr; + AbilityCameraMode = nullptr; bIsReadyToBindInputs = false; } @@ -154,11 +154,11 @@ void UOLSHeroComponent::HandleChangeInitState(UGameFrameworkComponentManager* ma // } // } // - // //@TODO: implement UOLSCameraMode. + // // // Hook up the delegate for all pawns, in case we spectate later // // if (pawnData) // // { - // // if (ULyraCameraComponent* CameraComponent = ULyraCameraComponent::FindCameraComponent(pawn)) + // // if (UOLSCameraComponent* CameraComponent = UOLSCameraComponent::FindCameraComponent(pawn)) // // { // // CameraComponent->DetermineCameraModeDelegate.BindUObject(this, &ThisClass::DetermineCameraMode); // // } @@ -199,6 +199,11 @@ UOLSHeroComponent* UOLSHeroComponent::FindHeroComponent(const AActor* actor) return (actor ? actor->FindComponentByClass() : nullptr); } +void UOLSHeroComponent::SetAbilityCameraMode(TSubclassOf CameraMode, + const FGameplayAbilitySpecHandle& OwningSpecHandle) +{ +} + void UOLSHeroComponent::ClearAbilityCameraMode(const FGameplayAbilitySpecHandle& owningSpecHandle) { if (AbilityCameraModeOwningSpecHandle == owningSpecHandle) @@ -520,3 +525,27 @@ void UOLSHeroComponent::Input_AutoRun(const FInputActionValue& inputActionValue) } } } + +TSubclassOf UOLSHeroComponent::DetermineCameraMode() const +{ + if (AbilityCameraMode) + { + return AbilityCameraMode; + } + + const APawn* Pawn = GetPawn(); + if (!Pawn) + { + return nullptr; + } + + if (UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) + { + if (const UOLSPawnDataAsset* pawnData = pawnExtComp->GetPawnData()) + { + return pawnData->DefaultCameraMode; + } + } + + return nullptr; +} diff --git a/Source/ols/Private/GameModes/OLSGameMode.cpp b/Source/ols/Private/GameModes/OLSGameMode.cpp index 1c3a908..8bbe897 100644 --- a/Source/ols/Private/GameModes/OLSGameMode.cpp +++ b/Source/ols/Private/GameModes/OLSGameMode.cpp @@ -5,7 +5,9 @@ #include "CommonUserSubsystem.h" #include "GameMapsSettings.h" +#include "OLSLog.h" #include "Characters/OLSCharacter.h" +#include "Components/OLSPawnExtensionComponent.h" #include "DataAssets/OLSExperienceDefinitionDataAsset.h" #include "DataAssets/OLSPawnDataAsset.h" #include "GameModes/OLSExperienceManagerComponent.h" @@ -15,11 +17,15 @@ #include "Player/OLSPlayerState.h" #include "Systems/OLSAssetManager.h" #include "Systems/OLSGameSession.h" +#include "CommonUserSubsystem.h" +#include "CommonSessionSubsystem.h" #include "UI/OLSHUD.h" - #include UE_INLINE_GENERATED_CPP_BY_NAME(OLSGameMode) + +DEFINE_LOG_CATEGORY(LogOLSGameMode); + AOLSGameMode::AOLSGameMode(const FObjectInitializer& objectInitializer) : Super(objectInitializer) { GameStateClass = AOLSGameState::StaticClass(); @@ -100,32 +106,29 @@ APawn* AOLSGameMode::SpawnDefaultPawnAtTransform_Implementation( { if (APawn* spawnedPawn = GetWorld()->SpawnActor(pawnClass, spawnTransform, spawnInfo)) { - // @TODO: Implement this as well as implement UOLSawnExtensionComponent - // if (UOLSawnExtensionComponent* PawnExtComp = UOLSawnExtensionComponent::FindPawnExtensionComponent(spawnedPawn)) - // { - // if (const ULyraPawnData* PawnData = GetPawnDataForController(newPlayer)) - // { - // PawnExtComp->SetPawnData(PawnData); - // } - // else - // { // @TODO: Replace this with out custom log. - // UE_LOG(LogLyra, Error, TEXT("Game mode was unable to set PawnData on the spawned pawn [%s]."), *GetNameSafe(spawnedPawn } - // } + if (UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(spawnedPawn)) + { + if (const UOLSPawnDataAsset* pawnData = GetPawnDataForController(newPlayer)) + { + pawnExtComp->SetPawnData(pawnData); + } + else + { + OLS_LOG(LogOLSGameMode, Error, + TEXT("Game mode was unable to set PawnData on the spawned pawn [%s]."), + *GetNameSafe(spawnedPawn)); + } + } spawnedPawn->FinishSpawning(spawnTransform); return spawnedPawn; } - else - { - // @TODO: Replace this with out custom log. - // UE_LOG(LogLyra, Error, TEXT("Game mode was unable to spawn Pawn of class [%s] at [%s]."), *GetNameSafe(pawnClass), *spawnTransform.ToHumanReadableString()); - } + OLS_LOG(LogOLSGameMode, Error, TEXT("Game mode was unable to spawn Pawn of class [%s] at [%s]."), *GetNameSafe(pawnClass), *spawnTransform.ToHumanReadableString()); } else { - // @TODO: Replace this with out custom log. - // UE_LOG(LogLyra, Error, TEXT("Game mode was unable to spawn Pawn due to NULL pawn class.")); + OLS_LOG(LogOLSGameMode, Error, TEXT("Game mode was unable to spawn Pawn due to NULL pawn class.")); } return nullptr; @@ -308,8 +311,7 @@ void AOLSGameMode::OnMatchAssignmentGiven(FPrimaryAssetId experienceId, const FS { if (experienceId.IsValid()) { - // @TODO: Replace this with our custom. - // UE_LOG(LogLyraExperience, Log, TEXT("Identified experience %s (Source: %s)"), *ExperienceId.ToString(), *ExperienceIdSource); + OLS_LOG(LogOLSGameMode, Log, TEXT("Identified experience %s (Source: %s)"), *experienceId.ToString(), *experienceIdSource); UOLSExperienceManagerComponent* experienceComponent = GameState->FindComponentByClass(); check(experienceComponent); @@ -317,8 +319,7 @@ void AOLSGameMode::OnMatchAssignmentGiven(FPrimaryAssetId experienceId, const FS } else { - // @TODO: Replace this with our custom. - // UE_LOG(LogLyraExperience, Error, TEXT("Failed to identify experience, loading screen will stay up forever")); + OLS_LOG(LogOLSGameMode, Error, TEXT("Failed to identify experience, loading screen will stay up forever")); } } @@ -382,8 +383,7 @@ void AOLSGameMode::HandleMatchAssignmentIfNotExpectingOne() FAssetData dummy; if (experienceId.IsValid() && !assetManager.GetPrimaryAssetData(experienceId, /*out*/ dummy)) { - // @TODO: Replace this with our custom. - // UE_LOG(LogLyraExperience, Error, TEXT("EXPERIENCE: Wanted to use %s but couldn't find it, falling back to the default)"), *experienceIding()); + OLS_LOG(LogOLSGameMode, Error, TEXT("EXPERIENCE: Wanted to use %s but couldn't find it, falling back to the default)"), *experienceId.ToString()); experienceId = FPrimaryAssetId(); } @@ -397,7 +397,7 @@ void AOLSGameMode::HandleMatchAssignmentIfNotExpectingOne() } //@TODO: Pull this from a config setting or something - experienceId = FPrimaryAssetId(FPrimaryAssetType("LyraExperienceDefinition"), FName("B_LyraDefaultExperience")); + experienceId = FPrimaryAssetId(FPrimaryAssetType("OLSExperienceDefinition"), FName("B_OLSDefaultExperience")); experienceIdSource = TEXT("Default"); } @@ -513,19 +513,21 @@ void AOLSGameMode::OnUserInitializedForDedicatedServer( if (gameInstance) { // Unbind - UCommonUserSubsystem* UserSubsystem = gameInstance->GetSubsystem(); - UserSubsystem->OnUserInitializeComplete.RemoveDynamic(this, &ThisClass::OnUserInitializedForDedicatedServer); + UCommonUserSubsystem* userSubsystem = gameInstance->GetSubsystem(); + userSubsystem->OnUserInitializeComplete.RemoveDynamic(this, &ThisClass::OnUserInitializedForDedicatedServer); // Dedicated servers do not require user login, but some online subsystems may expect it if (isSuccess && ensure(userInfo)) { - // @TODO: replace this with our custom. - // UE_LOG(LogLyraExperience, Log, TEXT("Dedicated server user login succeeded for id %s, starting online server"), *UserInfo->GetNetId().ToString()); + // @TODO: Fix compiler error. + // OLS_LOG(LogOLSGameMode, Log, + // TEXT("Dedicated server user login succeeded for id %s, starting online server"), + // *userInfo->GetNetId().ToString()); } else { - // @TODO: replace this with our custom. - // UE_LOG(LogLyraExperience, Log, TEXT("Dedicated server user login unsuccessful, starting online server as login is not required")); + OLS_LOG(LogOLSGameMode, Log, + TEXT("Dedicated server user login unsuccessful, starting online server as login is not required")); } HostDedicatedServerMatch(ECommonSessionOnlineMode::Online); diff --git a/Source/ols/Public/AbilitySystem/Abilities/OLSGameplayAbility.h b/Source/ols/Public/AbilitySystem/Abilities/OLSGameplayAbility.h index dff546f..ce67fea 100644 --- a/Source/ols/Public/AbilitySystem/Abilities/OLSGameplayAbility.h +++ b/Source/ols/Public/AbilitySystem/Abilities/OLSGameplayAbility.h @@ -118,14 +118,13 @@ public: UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "OLS|Ability", Meta = (ExpandBoolAsExecs = "ReturnValue")) bool ChangeActivationGroup(EOLSAbilityActivationGroup newGroup); - // @TODO: Implement OLSCameraMode. - // // Sets the ability's camera mode. - // UFUNCTION(BlueprintCallable, Category = "Lyra|Ability") - // void SetCameraMode(TSubclassOf CameraMode); - // - // // Clears the ability's camera mode. Automatically called if needed when the ability ends. - // UFUNCTION(BlueprintCallable, Category = "Lyra|Ability") - // void ClearCameraMode(); + // Sets the ability's camera mode. + UFUNCTION(BlueprintCallable, Category = "Lyra|Ability") + void SetCameraMode(TSubclassOf cameraMode); + + // Clears the ability's camera mode. Automatically called if needed when the ability ends. + UFUNCTION(BlueprintCallable, Category = "Lyra|Ability") + void ClearCameraMode(); public: @@ -188,7 +187,6 @@ protected: UPROPERTY(EditDefaultsOnly, Category = "Advanced") uint8 bShouldLogCancellation : 1 = false; - // @TODO: Implement OLSCameraMode // Current camera mode set by the ability. - // TSubclassOf ActiveCameraMode; + TSubclassOf ActiveCameraMode; }; diff --git a/Source/ols/Public/AbilitySystem/OLSAbilitySystemComponent.h b/Source/ols/Public/AbilitySystem/OLSAbilitySystemComponent.h index aa54e6f..38db66f 100644 --- a/Source/ols/Public/AbilitySystem/OLSAbilitySystemComponent.h +++ b/Source/ols/Public/AbilitySystem/OLSAbilitySystemComponent.h @@ -160,7 +160,7 @@ protected: virtual void NotifyAbilityActivated(const FGameplayAbilitySpecHandle handle, class UGameplayAbility* ability) override; virtual void NotifyAbilityFailed(const FGameplayAbilitySpecHandle handle, class UGameplayAbility* ability, const FGameplayTagContainer& failureReason) override; - virtual void NotifyAbilityEnded(FGameplayAbilitySpecHandle handle, class UGameplayAbility* Ability, bool bWasCancelled) override; + virtual void NotifyAbilityEnded(FGameplayAbilitySpecHandle handle, class UGameplayAbility* ability, bool bWasCancelled) override; virtual void ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& abilityTags, class UGameplayAbility* requestingAbility, bool shouldEnableBlockTags, const FGameplayTagContainer& blockTags, bool shouldExecuteCancelTags, const FGameplayTagContainer& cancelTags) override; virtual void HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& abilityTags, class UGameplayAbility* requestingAbility, bool canBeCanceled) override; diff --git a/Source/ols/Public/Camera/Components/OLSCameraComponent.h b/Source/ols/Public/Camera/Components/OLSCameraComponent.h new file mode 100644 index 0000000..e6529cb --- /dev/null +++ b/Source/ols/Public/Camera/Components/OLSCameraComponent.h @@ -0,0 +1,61 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + +#pragma once + +#include "CoreMinimal.h" +#include "Camera/CameraComponent.h" +#include "GameplayTagContainer.h" +#include "OLSCameraComponent.generated.h" + + +DECLARE_DELEGATE_RetVal(TSubclassOf, FOLSCameraModeDelegate); + +/** + * UOLSCameraComponent + * + * The base camera component class used by this project. + */ +UCLASS() +class OLS_API UOLSCameraComponent : public UCameraComponent +{ + GENERATED_BODY() + +public: + + UOLSCameraComponent(const FObjectInitializer& objectInitializer); + + // Returns the camera component if one exists on the specified actor. + UFUNCTION(BlueprintPure, Category = "OLS|Camera") + static UOLSCameraComponent* FindCameraComponent(const AActor* Actor); + + // Returns the target actor that the camera is looking at. + virtual AActor* GetTargetActor() const; + + // Delegate used to query for the best camera mode. + FOLSCameraModeDelegate DetermineCameraModeDelegate; + + // Add an offset to the field of view. The offset is only for one frame, it gets cleared once it is applied. + void AddFieldOfViewOffset(float FovOffset); + + virtual void DrawDebug(UCanvas* canvas) const; + + // Gets the tag associated with the top layer and the blend weight of it + void GetBlendInfo(float& outWeightOfTopLayer, FGameplayTag& outTagOfTopLayer) const; + +protected: + + virtual void OnRegister() override; + virtual void GetCameraView(float deltaTime, FMinimalViewInfo& desiredView) override; + + virtual void UpdateCameraModes(); + +protected: + + // Stack used to blend the camera modes. + UPROPERTY() + TObjectPtr CameraModeStack = nullptr; + + // Offset applied to the field of view. The offset is only for one frame, it gets cleared once it is applied. + float FieldOfViewOffset = 0.0f; + +}; diff --git a/Source/ols/Public/Camera/Components/OLSUICameraManagerComponent.h b/Source/ols/Public/Camera/Components/OLSUICameraManagerComponent.h new file mode 100644 index 0000000..29c95be --- /dev/null +++ b/Source/ols/Public/Camera/Components/OLSUICameraManagerComponent.h @@ -0,0 +1,37 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "OLSUICameraManagerComponent.generated.h" + + +UCLASS( Transient, Within=OLSPlayerCameraManager ) +class UOLSUICameraManagerComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + static class UOLSUICameraManagerComponent* GetComponent(APlayerController* playerController); + +public: + UOLSUICameraManagerComponent(); + + bool IsSettingViewTarget() const; + AActor* GetViewTarget() const; + void SetViewTarget(AActor* viewTarget, FViewTargetTransitionParams transitionParams = FViewTargetTransitionParams()); + + bool NeedsToUpdateViewTarget() const; + void UpdateViewTarget(struct FTViewTarget& outVT, float deltaTime); + + void OnShowDebugInfo(AHUD* hud, UCanvas* canvas, const FDebugDisplayInfo& displayInfo, float& yl, float& yPos); + +private: + + UPROPERTY() + TObjectPtr ViewTarget = nullptr;; + + UPROPERTY(Transient) + bool bShouldUpdatingViewTarget = false; +}; diff --git a/Source/ols/Public/Camera/Interfaces/OLSCameraAssistInterface.h b/Source/ols/Public/Camera/Interfaces/OLSCameraAssistInterface.h new file mode 100644 index 0000000..858692d --- /dev/null +++ b/Source/ols/Public/Camera/Interfaces/OLSCameraAssistInterface.h @@ -0,0 +1,39 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "OLSCameraAssistInterface.generated.h" + +UINTERFACE(BlueprintType) +class UOLSCameraAssistInterface : public UInterface +{ + GENERATED_BODY() +}; + +/** + * + */ +class IOLSCameraAssistInterface +{ + GENERATED_BODY() + +public: + /** + * Get the list of actors that we're allowing the camera to penetrate. Useful in 3rd person cameras + * when you need the following camera to ignore things like the a collection of view targets, the pawn, + * a vehicle..etc. + */ + virtual void GetIgnoredActorsForCameraPenetration(TArray& outActorsAllowPenetration) const; + + /** + * The target actor to prevent penetration on. Normally, this is almost always the view target, which if + * unimplemented will remain true. However, sometimes the view target, isn't the same as the root actor + * you need to keep in frame. + */ + virtual TOptional GetCameraPreventPenetrationTarget() const; + + /** Called if the camera penetrates the focal target. Useful if you want to hide the target actor when being overlapped. */ + virtual void OnCameraPenetratingTarget(); +}; diff --git a/Source/ols/Public/Camera/OLSCameraMode.h b/Source/ols/Public/Camera/OLSCameraMode.h new file mode 100644 index 0000000..58b1612 --- /dev/null +++ b/Source/ols/Public/Camera/OLSCameraMode.h @@ -0,0 +1,191 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + +#pragma once + +#include "CoreMinimal.h" +#include "GameplayTagContainer.h" +#include "UObject/Object.h" +#include "OLSCameraMode.generated.h" + +/** + * EOLSCameraModeBlendFunction + * + * Blend function used for transitioning between camera modes. + */ +UENUM(BlueprintType) +enum class EOLSCameraModeBlendFunction : uint8 +{ + // Does a simple linear interpolation. + Linear, + + // Immediately accelerates, but smoothly decelerates into the target. Ease amount controlled by the exponent. + EaseIn, + + // Smoothly accelerates, but does not decelerate into the target. Ease amount controlled by the exponent. + EaseOut, + + // Smoothly accelerates and decelerates. Ease amount controlled by the exponent. + EaseInOut, + + COUNT UMETA(Hidden) +}; + +/** + * FOLSCameraModeView + * + * View data produced by the camera mode that is used to blend camera modes. + */ +struct FOLSCameraModeView +{ +public: + + FOLSCameraModeView(); + + void Blend(const FOLSCameraModeView& other, float otherWeight); + +public: + + FVector Location; + FRotator Rotation; + FRotator ControlRotation; + float FieldOfView; +}; + +/** + * UOLSCameraMode + * + * Base class for all camera modes. + */ +UCLASS(Abstract, NotBlueprintable) +class OLS_API UOLSCameraMode : public UObject +{ + GENERATED_BODY() + +public: + + UOLSCameraMode(); + + class UOLSCameraComponent* GetOLSCameraComponent() const; + + virtual UWorld* GetWorld() const override; + + AActor* GetTargetActor() const; + + const FOLSCameraModeView& GetCameraModeView() const; + + // Called when this camera mode is activated on the camera mode stack. + virtual void OnActivation(); + + // Called when this camera mode is deactivated on the camera mode stack. + virtual void OnDeactivation(); + + void UpdateCameraMode(float deltaTime); + + float GetBlendTime() const; + float GetBlendWeight() const; + void SetBlendWeight(float weight); + + FGameplayTag GetCameraTypeTag() const; + + virtual void DrawDebug(UCanvas* canvas) const; + +protected: + + virtual FVector GetPivotLocation() const; + virtual FRotator GetPivotRotation() const; + + virtual void UpdateView(float deltaTime); + virtual void UpdateBlending(float deltaTime); + +protected: + + // A tag that can be queried by gameplay code that cares when a kind of camera mode is active + // without having to ask about a specific mode (e.g., when aiming downsights to get more accuracy) + UPROPERTY(EditDefaultsOnly, Category = "Blending") + FGameplayTag CameraTypeTag = FGameplayTag::EmptyTag; + + // View output produced by the camera mode. + FOLSCameraModeView View; + + // The horizontal field of view (in degrees). + UPROPERTY(EditDefaultsOnly, Category = "View", Meta = (UIMin = "5.0", UIMax = "170", ClampMin = "5.0", ClampMax = "170.0")) + float FieldOfView = 0.0f; + + // Minimum view pitch (in degrees). + UPROPERTY(EditDefaultsOnly, Category = "View", Meta = (UIMin = "-89.9", UIMax = "89.9", ClampMin = "-89.9", ClampMax = "89.9")) + float ViewPitchMin = 0.0f; + + // Maximum view pitch (in degrees). + UPROPERTY(EditDefaultsOnly, Category = "View", Meta = (UIMin = "-89.9", UIMax = "89.9", ClampMin = "-89.9", ClampMax = "89.9")) + float ViewPitchMax = 0.0f; + + // How long it takes to blend in this mode. + UPROPERTY(EditDefaultsOnly, Category = "Blending") + float BlendTime = 0.0f; + + // Function used for blending. + UPROPERTY(EditDefaultsOnly, Category = "Blending") + EOLSCameraModeBlendFunction BlendFunction = EOLSCameraModeBlendFunction::Linear; + + // Exponent used by blend functions to control the shape of the curve. + UPROPERTY(EditDefaultsOnly, Category = "Blending") + float BlendExponent = 0.0f; + + // Linear blend alpha used to determine the blend weight. + float BlendAlpha = 0.0f; + + // Blend weight calculated using the blend alpha and function. + float BlendWeight = 0.0f; + +protected: + + /** If true, skips all interpolation and puts camera in ideal location. Automatically set to false next frame. */ + UPROPERTY(Transient) + uint32 bShouldResetInterpolation : 1 = false; +}; + +/** + * UOLSCameraModeStack + * + * Stack used for blending camera modes. + */ +UCLASS() +class UOLSCameraModeStack : public UObject +{ + GENERATED_BODY() + +public: + + UOLSCameraModeStack(); + + void ActivateStack(); + void DeactivateStack(); + + bool IsStackActivate() const; + + void PushCameraMode(TSubclassOf cameraModeClass); + + bool EvaluateStack(float deltaTime, FOLSCameraModeView& outCameraModeView); + + void DrawDebug(UCanvas* canvas) const; + + // Gets the tag associated with the top layer and the blend weight of it + void GetBlendInfo(float& outWeightOfTopLayer, FGameplayTag& outTagOfTopLayer) const; + +protected: + + UOLSCameraMode* GetCameraModeInstance(TSubclassOf cameraModeClass); + + void UpdateStack(float deltaTime); + void BlendStack(FOLSCameraModeView& outCameraModeView) const; + +protected: + + uint8 bIsStackActive : 1 = true; + + UPROPERTY() + TArray> CameraModeInstances; + + UPROPERTY() + TArray> CameraModeStack; +}; diff --git a/Source/ols/Public/Camera/OLSPlayerCameraManager.h b/Source/ols/Public/Camera/OLSPlayerCameraManager.h new file mode 100644 index 0000000..4cdcc16 --- /dev/null +++ b/Source/ols/Public/Camera/OLSPlayerCameraManager.h @@ -0,0 +1,40 @@ +// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. + +#pragma once + +#include "CoreMinimal.h" +#include "Camera/PlayerCameraManager.h" +#include "OLSPlayerCameraManager.generated.h" + +#define OLS_CAMERA_DEFAULT_FOV (80.0f) +#define OLS_CAMERA_DEFAULT_PITCH_MIN (-89.0f) +#define OLS_CAMERA_DEFAULT_PITCH_MAX (89.0f) + +/** + * AOLSPlayerCameraManager + * + * The base player camera manager class used by this project. + */ +UCLASS(NotPlaceable, MinimalAPI) +class AOLSPlayerCameraManager : public APlayerCameraManager +{ + GENERATED_BODY() + + +public: + + AOLSPlayerCameraManager(const FObjectInitializer& objectInitializer); + + class UOLSUICameraManagerComponent* GetUICameraComponent() const; + +protected: + + virtual void UpdateViewTarget(FTViewTarget& outVT, float deltaTime) override; + + virtual void DisplayDebug(UCanvas* canvas, const FDebugDisplayInfo& debugDisplay, float& yl, float& yPos) override; + +private: + /** The UI Camera Component, controls the camera when UI is doing something important that gameplay doesn't get priority over. */ + UPROPERTY() + TObjectPtr UICameraComponent = nullptr; +}; diff --git a/Source/ols/Public/Components/OLSHeroComponent.h b/Source/ols/Public/Components/OLSHeroComponent.h index dbe7e51..f7105df 100644 --- a/Source/ols/Public/Components/OLSHeroComponent.h +++ b/Source/ols/Public/Components/OLSHeroComponent.h @@ -45,8 +45,7 @@ public: /** Overrides the camera from an active gameplay ability */ - //@TODO: implement UOLSCameraMode. - // void SetAbilityCameraMode(TSubclassOf CameraMode, const FGameplayAbilitySpecHandle& OwningSpecHandle); + void SetAbilityCameraMode(TSubclassOf CameraMode, const FGameplayAbilitySpecHandle& OwningSpecHandle); /** Clears the camera override if it is set */ void ClearAbilityCameraMode(const FGameplayAbilitySpecHandle& owningSpecHandle); @@ -79,8 +78,7 @@ protected: void Input_Crouch(const struct FInputActionValue& InputActionValue); void Input_AutoRun(const struct FInputActionValue& inputActionValue); - //@TODO: implement UOLSCameraMode. - // TSubclassOf DetermineCameraMode() const; + TSubclassOf DetermineCameraMode() const; protected: @@ -88,8 +86,8 @@ protected: TArray DefaultInputMappings; /** Camera mode set by an ability. */ - // UPROPERTY() - // TSubclassOf AbilityCameraMode; + UPROPERTY() + TSubclassOf AbilityCameraMode; /** Spec handle for the last ability to set a camera mode. */ FGameplayAbilitySpecHandle AbilityCameraModeOwningSpecHandle; diff --git a/Source/ols/Public/DataAssets/OLSPawnDataAsset.h b/Source/ols/Public/DataAssets/OLSPawnDataAsset.h index 5e3d3c7..f70371c 100644 --- a/Source/ols/Public/DataAssets/OLSPawnDataAsset.h +++ b/Source/ols/Public/DataAssets/OLSPawnDataAsset.h @@ -43,8 +43,7 @@ public: UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "OLS|Input") TObjectPtr InputConfig = nullptr; - // @Todo: implement DefaultCamera mode here. // Default camera mode used by player controlled pawns. - // UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Lyra|Camera") - // TSubclassOf DefaultCameraMode = nullptr; + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Lyra|Camera") + TSubclassOf DefaultCameraMode = nullptr; }; diff --git a/Source/ols/Public/GameModes/OLSGameMode.h b/Source/ols/Public/GameModes/OLSGameMode.h index d68e89e..56e4d4b 100644 --- a/Source/ols/Public/GameModes/OLSGameMode.h +++ b/Source/ols/Public/GameModes/OLSGameMode.h @@ -7,6 +7,8 @@ #include "ModularGameplayActors/OLSModularGameMode.h" #include "OLSGameMode.generated.h" +DECLARE_LOG_CATEGORY_EXTERN(LogOLSGameMode, Verbose, All); + /** * Post login event, triggered when a player or bot joins the game as well as after seamless and non seamless travel * @@ -15,7 +17,6 @@ DECLARE_MULTICAST_DELEGATE_TwoParams(FOLSGameModePlayerInitializeNativeDelegate, class AGameModeBase* /*GameMode*/, class AController* /*NewPlayer*/); - /** * AOLSGameMode *