552 lines
18 KiB
C++
552 lines
18 KiB
C++
// © 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 "Components/OLSHeroComponent.h"
|
|
|
|
#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"
|
|
#include "DataAssets/OLSPawnDataAsset.h"
|
|
#include "GameFeatures/OLSGameFeatureAction_AddInputContextMapping.h"
|
|
#include "Player/OLSPlayerController.h"
|
|
#include "Player/OLSPlayerState.h"
|
|
|
|
#if WITH_EDITOR
|
|
#include "Misc/UObjectToken.h"
|
|
#endif // WITH_EDITOR
|
|
|
|
DEFINE_LOG_CATEGORY(LogOLSHeroComponent);
|
|
|
|
namespace OLSHero
|
|
{
|
|
static const float LookYawRate = 300.0f;
|
|
static const float LookPitchRate = 165.0f;
|
|
};
|
|
|
|
const FName UOLSHeroComponent::NAME_BindInputsNow("BindInputsNow");
|
|
const FName UOLSHeroComponent::NAME_ActorFeatureName("Hero");
|
|
|
|
UOLSHeroComponent::UOLSHeroComponent(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
|
{
|
|
AbilityCameraMode = nullptr;
|
|
bIsReadyToBindInputs = false;
|
|
}
|
|
|
|
FName UOLSHeroComponent::GetFeatureName() const
|
|
{
|
|
// Don't call Super since it does not fit in this.
|
|
|
|
return NAME_ActorFeatureName;
|
|
}
|
|
|
|
bool UOLSHeroComponent::CanChangeInitState(UGameFrameworkComponentManager* manager,
|
|
FGameplayTag currentState,
|
|
FGameplayTag desiredState) const
|
|
{
|
|
// Don't call Super since it does not fit into this.
|
|
|
|
check(manager);
|
|
|
|
APawn* pawn = GetPawn<APawn>();
|
|
|
|
//@TODO: implement LyraGameplayTags::InitState_Spawned.
|
|
//@TODO: implement LyraGameplayTags::InitState_DataAvailable.
|
|
//@TODO: implement LyraGameplayTags::InitState_DataInitialized.
|
|
//@TODO: implement LyraGameplayTags::InitState_GameplayReady.
|
|
// if (!currentState.IsValid() && desiredState == LyraGameplayTags::InitState_Spawned)
|
|
// {
|
|
// // As long as we have a real pawn, let us transition
|
|
// if (pawn)
|
|
// {
|
|
// return true;
|
|
// }
|
|
// }
|
|
// else if (currentState == LyraGameplayTags::InitState_Spawned && desiredState == LyraGameplayTags::InitState_DataAvailable)
|
|
// {
|
|
// // The player state is required.
|
|
// if (!GetPlayerState<AOLSPlayerState>())
|
|
// {
|
|
// return false;
|
|
// }
|
|
//
|
|
// // If we're authority or autonomous, we need to wait for a controller with registered ownership of the player state.
|
|
// if (pawn->GetLocalRole() != ROLE_SimulatedProxy)
|
|
// {
|
|
// AController* controller = GetController<AController>();
|
|
//
|
|
// const bool hasControllerPairedWithPS = (controller != nullptr) && \
|
|
// (controller->PlayerState != nullptr) && \
|
|
// (controller->PlayerState->GetOwner() == controller);
|
|
//
|
|
// if (!hasControllerPairedWithPS)
|
|
// {
|
|
// return false;
|
|
// }
|
|
// }
|
|
//
|
|
// const bool isLocallyControlled = pawn->IsLocallyControlled();
|
|
// const bool isBot = pawn->IsBotControlled();
|
|
//
|
|
// if (isLocallyControlled && !isBot)
|
|
// {
|
|
// AOLSPlayerController* playerController = GetController<AOLSPlayerController>();
|
|
//
|
|
// // The input component and local player is required when locally controlled.
|
|
// if (!pawn->InputComponent || !playerController || !playerController->GetLocalPlayer())
|
|
// {
|
|
// return false;
|
|
// }
|
|
// }
|
|
//
|
|
// return true;
|
|
// }
|
|
// else if (currentState == LyraGameplayTags::InitState_DataAvailable && desiredState == LyraGameplayTags::InitState_DataInitialized)
|
|
// {
|
|
// // Wait for player state and extension component
|
|
// AOLSPlayerState* playerState = GetPlayerState<AOLSPlayerState>();
|
|
//
|
|
// return playerState && manager->HasFeatureReachedInitState(pawn, UOLSPawnExtensionComponent::NAME_ActorFeatureName, LyraGameplayTags::InitState_DataInitialized);
|
|
// }
|
|
// else if (currentState == LyraGameplayTags::InitState_DataInitialized && desiredState == LyraGameplayTags::InitState_GameplayReady)
|
|
// {
|
|
// // TODO add ability initialization checks?
|
|
// return true;
|
|
// }
|
|
|
|
return false;
|
|
}
|
|
|
|
void UOLSHeroComponent::HandleChangeInitState(UGameFrameworkComponentManager* manager,
|
|
FGameplayTag currentState,
|
|
FGameplayTag desiredState)
|
|
{
|
|
//@TODO: implement LyraGameplayTags::InitState_DataAvailable.
|
|
//@TODO: implement LyraGameplayTags::InitState_DataInitialized.
|
|
// if (currentState == LyraGameplayTags::InitState_DataAvailable && desiredState == LyraGameplayTags::InitState_DataInitialized)
|
|
// {
|
|
// APawn* pawn = GetPawn<APawn>();
|
|
// AOLSPlayerState* playerState = GetPlayerState<AOLSPlayerState>();
|
|
// if (!ensure(pawn && playerState))
|
|
// {
|
|
// return;
|
|
// }
|
|
//
|
|
// const UOLSPawnDataAsset* pawnData = nullptr;
|
|
//
|
|
// if (UOLSPawnExtensionComponent* PawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(pawn))
|
|
// {
|
|
// pawnData = PawnExtComp->GetPawnData<UOLSPawnDataAsset>();
|
|
//
|
|
// // The player state holds the persistent data for this player (state that persists across deaths and multiple pawns).
|
|
// // The ability system component and attribute sets live on the player state.
|
|
// PawnExtComp->InitializeAbilitySystem(playerState->GetOLSAbilitySystemComponent(), playerState);
|
|
// }
|
|
//
|
|
// if (AOLSPlayerController* playerController = GetController<AOLSPlayerController>())
|
|
// {
|
|
// if (pawn->InputComponent)
|
|
// {
|
|
// InitializePlayerInput(pawn->InputComponent);
|
|
// }
|
|
// }
|
|
//
|
|
//
|
|
// // Hook up the delegate for all pawns, in case we spectate later
|
|
// // if (pawnData)
|
|
// // {
|
|
// // if (UOLSCameraComponent* CameraComponent = UOLSCameraComponent::FindCameraComponent(pawn))
|
|
// // {
|
|
// // CameraComponent->DetermineCameraModeDelegate.BindUObject(this, &ThisClass::DetermineCameraMode);
|
|
// // }
|
|
// // }
|
|
// }
|
|
}
|
|
|
|
void UOLSHeroComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& params)
|
|
{
|
|
// if (params.FeatureName == UOLSPawnExtensionComponent::NAME_ActorFeatureName)
|
|
// {
|
|
// //@TODO: implement LyraGameplayTags::InitState_DataAvailable.
|
|
// if (params.FeatureState == LyraGameplayTags::InitState_DataInitialized)
|
|
// {
|
|
// // If the extension component says all other components are initialized, try to progress to next state
|
|
// CheckDefaultInitialization();
|
|
// }
|
|
// }
|
|
}
|
|
|
|
void UOLSHeroComponent::CheckDefaultInitialization()
|
|
{
|
|
//@TODO: implement LyraGameplayTags::InitState_Spawned.
|
|
//@TODO: implement LyraGameplayTags::InitState_DataAvailable.
|
|
//@TODO: implement LyraGameplayTags::InitState_DataInitialized.
|
|
//@TODO: implement LyraGameplayTags::InitState_GameplayReady.
|
|
// static const TArray<FGameplayTag> stateChain = {
|
|
// LyraGameplayTags::InitState_Spawned, LyraGameplayTags::InitState_DataAvailable,
|
|
// LyraGameplayTags::InitState_DataInitialized, LyraGameplayTags::InitState_GameplayReady
|
|
// };
|
|
//
|
|
// // This will try to progress from spawned (which is only set in BeginPlay) through the data initialization stages until it gets to gameplay ready
|
|
// ContinueInitStateChain(stateChain);
|
|
}
|
|
|
|
UOLSHeroComponent* UOLSHeroComponent::FindHeroComponent(const AActor* actor)
|
|
{
|
|
return (actor ? actor->FindComponentByClass<UOLSHeroComponent>() : nullptr);
|
|
}
|
|
|
|
void UOLSHeroComponent::SetAbilityCameraMode(TSubclassOf<UOLSCameraMode> CameraMode,
|
|
const FGameplayAbilitySpecHandle& OwningSpecHandle)
|
|
{
|
|
}
|
|
|
|
void UOLSHeroComponent::ClearAbilityCameraMode(const FGameplayAbilitySpecHandle& owningSpecHandle)
|
|
{
|
|
if (AbilityCameraModeOwningSpecHandle == owningSpecHandle)
|
|
{
|
|
//@TODO: implement UOLSCameraMode.
|
|
// AbilityCameraMode = nullptr;
|
|
AbilityCameraModeOwningSpecHandle = FGameplayAbilitySpecHandle();
|
|
}
|
|
}
|
|
|
|
void UOLSHeroComponent::AddAdditionalInputConfig(const UOLSInputConfigDataAsset* inputConfig)
|
|
{
|
|
TArray<uint32> bindHandles;
|
|
|
|
const APawn* pawn = GetPawn<APawn>();
|
|
if (!pawn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const APlayerController* playerController = GetController<APlayerController>();
|
|
check(playerController);
|
|
|
|
const ULocalPlayer* localPlayer = playerController->GetLocalPlayer();
|
|
check(localPlayer);
|
|
|
|
UEnhancedInputLocalPlayerSubsystem* subsystem = localPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
|
|
check(subsystem);
|
|
|
|
if (const UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(pawn))
|
|
{
|
|
//@TODO: Implement UOLSInputComponent.
|
|
// UOLSInputComponent* inputComponent = pawn->FindComponentByClass<UOLSInputComponent>();
|
|
// if (ensureMsgf(inputComponent,
|
|
// TEXT(
|
|
// "Unexpected Input Component class! The Gameplay Abilities will not be bound to their inputs. Change the input component to ULyraInputComponent or a subclass of it."
|
|
// )))
|
|
// {
|
|
// inputComponent->BindAbilityActions(inputConfig, this, &ThisClass::Input_AbilityInputTagPressed,
|
|
// &ThisClass::Input_AbilityInputTagReleased, /*out*/ bindHandles);
|
|
// }
|
|
}
|
|
}
|
|
|
|
void UOLSHeroComponent::RemoveAdditionalInputConfig(const UOLSInputConfigDataAsset* inputConfig)
|
|
{
|
|
//@TODO: Implement me!
|
|
}
|
|
|
|
bool UOLSHeroComponent::IsReadyToBindInputs() const
|
|
{
|
|
return bIsReadyToBindInputs;
|
|
}
|
|
|
|
void UOLSHeroComponent::OnRegister()
|
|
{
|
|
Super::OnRegister();
|
|
|
|
if (!GetPawn<APawn>())
|
|
{
|
|
OLS_LOG(LogOLSHeroComponent, Error,
|
|
TEXT(
|
|
"This component has been added to a blueprint whose base class is not a Pawn. To use this component, it MUST be placed on a Pawn Blueprint."
|
|
));
|
|
|
|
#if WITH_EDITOR
|
|
if (GIsEditor)
|
|
{
|
|
static const FText message = NSLOCTEXT("OLSHeroComponent", "NotOnPawnError",
|
|
"has been added to a blueprint whose base class is not a Pawn. To use this component, it MUST be placed on a Pawn Blueprint. This will cause a crash if you PIE!");
|
|
static const FName heroMessageLogName = TEXT("OLSHeroComponent");
|
|
|
|
FMessageLog(heroMessageLogName).Error()
|
|
->AddToken(FUObjectToken::Create(this, FText::FromString(GetNameSafe(this))))
|
|
->AddToken(FTextToken::Create(message));
|
|
|
|
FMessageLog(heroMessageLogName).Open();
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// Register with the init state system early, this will only work if this is a game world
|
|
RegisterInitStateFeature();
|
|
}
|
|
}
|
|
|
|
void UOLSHeroComponent::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
// Listen for when the pawn extension component changes init state
|
|
BindOnActorInitStateChanged(UOLSPawnExtensionComponent::NAME_ActorFeatureName, FGameplayTag(), false);
|
|
|
|
// Notifies that we are done spawning, then try the rest of initialization
|
|
//@TODO: implement LyraGameplayTags::InitState_Spawned.
|
|
// ensure(TryToChangeInitState(LyraGameplayTags::InitState_Spawned));
|
|
CheckDefaultInitialization();
|
|
}
|
|
|
|
void UOLSHeroComponent::EndPlay(const EEndPlayReason::Type endPlayReason)
|
|
{
|
|
UnregisterInitStateFeature();
|
|
|
|
Super::EndPlay(endPlayReason);
|
|
}
|
|
|
|
void UOLSHeroComponent::InitializePlayerInput(UInputComponent* playerInputComponent)
|
|
{
|
|
check(playerInputComponent);
|
|
|
|
const APawn* Pawn = GetPawn<APawn>();
|
|
if (!Pawn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const APlayerController* playerController = GetController<APlayerController>();
|
|
check(playerController);
|
|
|
|
//@TODO implement UOLSLocalPlayer.
|
|
// const UOLSLocalPlayer* localPlayer = Cast<UOLSLocalPlayer>(playerController->GetLocalPlayer());
|
|
// check(localPlayer);
|
|
|
|
// UEnhancedInputLocalPlayerSubsystem* subsystem = localPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
|
|
// check(subsystem);
|
|
//
|
|
// subsystem->ClearAllMappings();
|
|
|
|
// if (const UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
|
|
// {
|
|
// if (const UOLSPawnDataAsset* pawnData = pawnExtComp->GetPawnData<UOLSPawnDataAsset>())
|
|
// {
|
|
// //@TODO: Implement UOLSInputConfig
|
|
// if (const UOLSInputConfig* inputConfig = pawnData->InputConfig)
|
|
// {
|
|
// for (const FOLSInputMappingContextAndPriority& mapping : DefaultInputMappings)
|
|
// {
|
|
// if (UInputMappingContext* imc = mapping.InputMapping.Get())
|
|
// {
|
|
// if (mapping.bShouldRegisterWithSettings)
|
|
// {
|
|
// if (UEnhancedInputUserSettings* settings = subsystem->GetUserSettings())
|
|
// {
|
|
// settings->RegisterInputMappingContext(imc);
|
|
// }
|
|
//
|
|
// FModifyContextOptions options = {};
|
|
// options.bIgnoreAllPressedKeysUntilRelease = false;
|
|
// // Actually add the config to the local player
|
|
// subsystem->AddMappingContext(imc, mapping.Priority, options);
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// // The Lyra Input Component has some additional functions to map Gameplay Tags to an Input Action.
|
|
// // If you want this functionality but still want to change your input component class, make it a subclass
|
|
// // of the ULyraInputComponent or modify this component accordingly.
|
|
// //@TODO: Implement UOLSInputComponent.
|
|
// // UOLSInputComponent* inputComponent = Cast<UOLSInputComponent>(playerInputComponent);
|
|
// if (ensureMsgf(inputComponent,
|
|
// TEXT(
|
|
// "Unexpected Input Component class! The Gameplay Abilities will not be bound to their inputs. Change the input component to ULyraInputComponent or a subclass of it."
|
|
// )))
|
|
// {
|
|
// // Add the key mappings that may have been set by the player
|
|
// inputComponent->AddInputMappings(inputConfig, subsystem);
|
|
//
|
|
// // This is where we actually bind and input action to a gameplay tag, which means that Gameplay Ability Blueprints will
|
|
// // be triggered directly by these input actions Triggered events.
|
|
// TArray<uint32> BindHandles;
|
|
// inputComponent->BindAbilityActions(inputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles);
|
|
//
|
|
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_Move, ETriggerEvent::Triggered, this, &ThisClass::Input_Move, /*bLogIfNotFound=*/ false);
|
|
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_Look_Mouse, ETriggerEvent::Triggered, this, &ThisClass::Input_LookMouse, /*bLogIfNotFound=*/ false);
|
|
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_Look_Stick, ETriggerEvent::Triggered, this, &ThisClass::Input_LookStick, /*bLogIfNotFound=*/ false);
|
|
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_Crouch, ETriggerEvent::Triggered, this, &ThisClass::Input_Crouch, /*bLogIfNotFound=*/ false);
|
|
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_AutoRun, ETriggerEvent::Triggered, this, &ThisClass::Input_AutoRun, /*bLogIfNotFound=*/ false);
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
if (ensure(!bIsReadyToBindInputs))
|
|
{
|
|
bIsReadyToBindInputs = true;
|
|
}
|
|
|
|
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast<APlayerController*>(playerController), NAME_BindInputsNow);
|
|
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast<APawn*>(Pawn), NAME_BindInputsNow);
|
|
}
|
|
|
|
void UOLSHeroComponent::Input_AbilityInputTagPressed(FGameplayTag inputTag)
|
|
{
|
|
if (const APawn* pawn = GetPawn<APawn>())
|
|
{
|
|
if (const UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(pawn))
|
|
{
|
|
if (UOLSAbilitySystemComponent* asc = pawnExtComp->GetOLSAbilitySystemComponent())
|
|
{
|
|
asc->AbilityInputTagPressed(inputTag);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UOLSHeroComponent::Input_AbilityInputTagReleased(FGameplayTag inputTag)
|
|
{
|
|
const APawn* pawn = GetPawn<APawn>();
|
|
if (!pawn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (const UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(pawn))
|
|
{
|
|
if (UOLSAbilitySystemComponent* asc = pawnExtComp->GetOLSAbilitySystemComponent())
|
|
{
|
|
asc->AbilityInputTagReleased(inputTag);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UOLSHeroComponent::Input_Move(const FInputActionValue& inputActionValue)
|
|
{
|
|
APawn* pawn = GetPawn<APawn>();
|
|
AController* controller = pawn ? pawn->GetController() : nullptr;
|
|
|
|
// If the player has attempted to move again then cancel auto running
|
|
if (AOLSPlayerController* playerController = Cast<AOLSPlayerController>(controller))
|
|
{
|
|
//@TODO: Should we have this?
|
|
// playerController->SetIsAutoRunning(false);
|
|
}
|
|
|
|
if (controller)
|
|
{
|
|
const FVector2D value = inputActionValue.Get<FVector2D>();
|
|
const FRotator movementRotation(0.0f, controller->GetControlRotation().Yaw, 0.0f);
|
|
|
|
if (value.X != 0.0f)
|
|
{
|
|
const FVector movementDirection = movementRotation.RotateVector(FVector::RightVector);
|
|
pawn->AddMovementInput(movementDirection, value.X);
|
|
}
|
|
|
|
if (value.Y != 0.0f)
|
|
{
|
|
const FVector movementDirection = movementRotation.RotateVector(FVector::ForwardVector);
|
|
pawn->AddMovementInput(movementDirection, value.Y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UOLSHeroComponent::Input_LookMouse(const FInputActionValue& inputActionValue)
|
|
{
|
|
APawn* pawn = GetPawn<APawn>();
|
|
|
|
if (!pawn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FVector2D value = inputActionValue.Get<FVector2D>();
|
|
|
|
if (value.X != 0.0f)
|
|
{
|
|
pawn->AddControllerYawInput(value.X);
|
|
}
|
|
|
|
if (value.Y != 0.0f)
|
|
{
|
|
pawn->AddControllerPitchInput(value.Y);
|
|
}
|
|
}
|
|
|
|
void UOLSHeroComponent::Input_LookStick(const FInputActionValue& inputActionValue)
|
|
{
|
|
APawn* pawn = GetPawn<APawn>();
|
|
|
|
if (!pawn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FVector2D value = inputActionValue.Get<FVector2D>();
|
|
|
|
const UWorld* world = GetWorld();
|
|
check(world);
|
|
|
|
if (value.X != 0.0f)
|
|
{
|
|
pawn->AddControllerYawInput(value.X * OLSHero::LookYawRate * world->GetDeltaSeconds());
|
|
}
|
|
|
|
if (value.Y != 0.0f)
|
|
{
|
|
pawn->AddControllerPitchInput(value.Y * OLSHero::LookPitchRate * world->GetDeltaSeconds());
|
|
}
|
|
}
|
|
|
|
void UOLSHeroComponent::Input_Crouch(const FInputActionValue& inputActionValue)
|
|
{
|
|
if (AOLSCharacter* character = GetPawn<AOLSCharacter>())
|
|
{
|
|
character->ToggleCrouch();
|
|
}
|
|
}
|
|
|
|
void UOLSHeroComponent::Input_AutoRun(const FInputActionValue& inputActionValue)
|
|
{
|
|
if (APawn* Pawp = GetPawn<APawn>())
|
|
{
|
|
if (AOLSPlayerController* controller = Cast<AOLSPlayerController>(Pawp->GetController()))
|
|
{
|
|
//@TODO: Should we have this?
|
|
// Toggle auto running
|
|
// controller->SetIsAutoRunning(!controller->GetIsAutoRunning());
|
|
}
|
|
}
|
|
}
|
|
|
|
TSubclassOf<UOLSCameraMode> UOLSHeroComponent::DetermineCameraMode() const
|
|
{
|
|
if (AbilityCameraMode)
|
|
{
|
|
return AbilityCameraMode;
|
|
}
|
|
|
|
const APawn* Pawn = GetPawn<APawn>();
|
|
if (!Pawn)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
|
|
{
|
|
if (const UOLSPawnDataAsset* pawnData = pawnExtComp->GetPawnData<UOLSPawnDataAsset>())
|
|
{
|
|
return pawnData->DefaultCameraMode;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|