// Copyright Epic Games, Inc. All Rights Reserved. #include "LyraHeroComponent.h" #include "LyraLogChannels.h" #include "GameFramework/Pawn.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" #include "Player/LyraPlayerController.h" #include "Player/LyraPlayerState.h" #include "Character/LyraPawnExtensionComponent.h" #include "Character/LyraPawnData.h" #include "Character/LyraCharacter.h" #include "AbilitySystem/LyraAbilitySystemComponent.h" #include "Input/LyraInputConfig.h" #include "Input/LyraInputComponent.h" #include "Camera/LyraCameraComponent.h" #include "LyraGameplayTags.h" #include "Engine/LocalPlayer.h" #include "Components/GameFrameworkComponentManager.h" #include "Settings/LyraSettingsLocal.h" #include "System/LyraAssetManager.h" #include "PlayerMappableInputConfig.h" #if WITH_EDITOR #include "Misc/UObjectToken.h" #endif // WITH_EDITOR namespace LyraHero { static const float LookYawRate = 300.0f; static const float LookPitchRate = 165.0f; }; const FName ULyraHeroComponent::NAME_BindInputsNow("BindInputsNow"); ULyraHeroComponent::ULyraHeroComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { AbilityCameraMode = nullptr; bPawnHasInitialized = false; bReadyToBindInputs = false; } void ULyraHeroComponent::OnRegister() { Super::OnRegister(); if (const APawn* Pawn = GetPawn()) { if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) { PawnExtComp->OnPawnReadyToInitialize_RegisterAndCall(FSimpleMulticastDelegate::FDelegate::CreateUObject(this, &ThisClass::OnPawnReadyToInitialize)); } } else { UE_LOG(LogLyra, Error, TEXT("[ULyraHeroComponent::OnRegister] 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("LyraHeroComponent", "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("LyraHeroComponent"); FMessageLog(HeroMessageLogName).Error() ->AddToken(FUObjectToken::Create(this, FText::FromString(GetNameSafe(this)))) ->AddToken(FTextToken::Create(Message)); FMessageLog(HeroMessageLogName).Open(); } #endif } } bool ULyraHeroComponent::IsPawnComponentReadyToInitialize() const { // The player state is required. if (!GetPlayerState()) { return false; } const APawn* Pawn = GetPawn(); // A pawn is required. if (!Pawn) { 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(); const bool bHasControllerPairedWithPS = (Controller != nullptr) && \ (Controller->PlayerState != nullptr) && \ (Controller->PlayerState->GetOwner() == Controller); if (!bHasControllerPairedWithPS) { return false; } } const bool bIsLocallyControlled = Pawn->IsLocallyControlled(); const bool bIsBot = Pawn->IsBotControlled(); if (bIsLocallyControlled && !bIsBot) { // The input component is required when locally controlled. if (!Pawn->InputComponent) { return false; } } return true; } void ULyraHeroComponent::OnPawnReadyToInitialize() { if (!ensure(!bPawnHasInitialized)) { // Don't initialize twice return; } APawn* Pawn = GetPawn(); if (!Pawn) { return; } const bool bIsLocallyControlled = Pawn->IsLocallyControlled(); ALyraPlayerState* LyraPS = GetPlayerState(); check(LyraPS); const ULyraPawnData* PawnData = nullptr; if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) { PawnData = PawnExtComp->GetPawnData(); // 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(LyraPS->GetLyraAbilitySystemComponent(), LyraPS); } if (ALyraPlayerController* LyraPC = GetController()) { if (Pawn->InputComponent != nullptr) { InitializePlayerInput(Pawn->InputComponent); } } if (bIsLocallyControlled && PawnData) { if (ULyraCameraComponent* CameraComponent = ULyraCameraComponent::FindCameraComponent(Pawn)) { CameraComponent->DetermineCameraModeDelegate.BindUObject(this, &ThisClass::DetermineCameraMode); } } bPawnHasInitialized = true; } void ULyraHeroComponent::BeginPlay() { Super::BeginPlay(); } void ULyraHeroComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { if (const APawn* Pawn = GetPawn()) { if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) { PawnExtComp->UninitializeAbilitySystem(); } } Super::EndPlay(EndPlayReason); } void ULyraHeroComponent::InitializePlayerInput(UInputComponent* PlayerInputComponent) { check(PlayerInputComponent); const APawn* Pawn = GetPawn(); if (!Pawn) { return; } const APlayerController* PC = GetController(); check(PC); const ULocalPlayer* LP = PC->GetLocalPlayer(); check(LP); UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem(); check(Subsystem); Subsystem->ClearAllMappings(); if (const ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) { if (const ULyraPawnData* PawnData = PawnExtComp->GetPawnData()) { if (const ULyraInputConfig* InputConfig = PawnData->InputConfig) { const FLyraGameplayTags& GameplayTags = FLyraGameplayTags::Get(); // Register any default input configs with the settings so that they will be applied to the player during AddInputMappings for (const FMappableConfigPair& Pair : DefaultInputConfigs) { FMappableConfigPair::ActivatePair(Pair); } ULyraInputComponent* LyraIC = CastChecked(PlayerInputComponent); LyraIC->AddInputMappings(InputConfig, Subsystem); if (ULyraSettingsLocal* LocalSettings = ULyraSettingsLocal::Get()) { LocalSettings->OnInputConfigActivated.AddUObject(this, &ULyraHeroComponent::OnInputConfigActivated); LocalSettings->OnInputConfigDeactivated.AddUObject(this, &ULyraHeroComponent::OnInputConfigDeactivated); } TArray BindHandles; LyraIC->BindAbilityActions(InputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles); LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_Move, ETriggerEvent::Triggered, this, &ThisClass::Input_Move, /*bLogIfNotFound=*/ false); LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_Look_Mouse, ETriggerEvent::Triggered, this, &ThisClass::Input_LookMouse, /*bLogIfNotFound=*/ false); LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_Look_Stick, ETriggerEvent::Triggered, this, &ThisClass::Input_LookStick, /*bLogIfNotFound=*/ false); LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_Crouch, ETriggerEvent::Triggered, this, &ThisClass::Input_Crouch, /*bLogIfNotFound=*/ false); LyraIC->BindNativeAction(InputConfig, GameplayTags.InputTag_AutoRun, ETriggerEvent::Triggered, this, &ThisClass::Input_AutoRun, /*bLogIfNotFound=*/ false); } } } if (ensure(!bReadyToBindInputs)) { bReadyToBindInputs = true; } UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast(PC), NAME_BindInputsNow); UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast(Pawn), NAME_BindInputsNow); } void ULyraHeroComponent::OnInputConfigActivated(const FLoadedMappableConfigPair& ConfigPair) { if (ALyraPlayerController* LyraPC = GetController()) { if (APawn* Pawn = GetPawn()) { if (ULyraInputComponent* LyraIC = Cast(Pawn->InputComponent)) { if (const ULocalPlayer* LP = LyraPC->GetLocalPlayer()) { if (UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem()) { LyraIC->AddInputConfig(ConfigPair, Subsystem); } } } } } } void ULyraHeroComponent::OnInputConfigDeactivated(const FLoadedMappableConfigPair& ConfigPair) { if (ALyraPlayerController* LyraPC = GetController()) { if (APawn* Pawn = GetPawn()) { if (ULyraInputComponent* LyraIC = Cast(Pawn->InputComponent)) { if (const ULocalPlayer* LP = LyraPC->GetLocalPlayer()) { if (UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem()) { LyraIC->RemoveInputConfig(ConfigPair, Subsystem); } } } } } } void ULyraHeroComponent::AddAdditionalInputConfig(const ULyraInputConfig* InputConfig) { TArray BindHandles; const APawn* Pawn = GetPawn(); if (!Pawn) { return; } ULyraInputComponent* LyraIC = Pawn->FindComponentByClass(); check(LyraIC); const APlayerController* PC = GetController(); check(PC); const ULocalPlayer* LP = PC->GetLocalPlayer(); check(LP); UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem(); check(Subsystem); if (const ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) { LyraIC->BindAbilityActions(InputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles); } } void ULyraHeroComponent::RemoveAdditionalInputConfig(const ULyraInputConfig* InputConfig) { //@TODO: Implement me! } bool ULyraHeroComponent::HasPawnInitialized() const { return bPawnHasInitialized; } bool ULyraHeroComponent::IsReadyToBindInputs() const { return bReadyToBindInputs; } void ULyraHeroComponent::Input_AbilityInputTagPressed(FGameplayTag InputTag) { if (const APawn* Pawn = GetPawn()) { if (const ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) { if (ULyraAbilitySystemComponent* LyraASC = PawnExtComp->GetLyraAbilitySystemComponent()) { LyraASC->AbilityInputTagPressed(InputTag); } } } } void ULyraHeroComponent::Input_AbilityInputTagReleased(FGameplayTag InputTag) { const APawn* Pawn = GetPawn(); if (!Pawn) { return; } if (const ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) { if (ULyraAbilitySystemComponent* LyraASC = PawnExtComp->GetLyraAbilitySystemComponent()) { LyraASC->AbilityInputTagReleased(InputTag); } } } void ULyraHeroComponent::Input_Move(const FInputActionValue& InputActionValue) { APawn* Pawn = GetPawn(); AController* Controller = Pawn ? Pawn->GetController() : nullptr; // If the player has attempted to move again then cancel auto running if (ALyraPlayerController* LyraController = Cast(Controller)) { LyraController->SetIsAutoRunning(false); } if (Controller) { const FVector2D Value = InputActionValue.Get(); 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 ULyraHeroComponent::Input_LookMouse(const FInputActionValue& InputActionValue) { APawn* Pawn = GetPawn(); if (!Pawn) { return; } const FVector2D Value = InputActionValue.Get(); if (Value.X != 0.0f) { Pawn->AddControllerYawInput(Value.X); } if (Value.Y != 0.0f) { Pawn->AddControllerPitchInput(Value.Y); } } void ULyraHeroComponent::Input_LookStick(const FInputActionValue& InputActionValue) { APawn* Pawn = GetPawn(); if (!Pawn) { return; } const FVector2D Value = InputActionValue.Get(); const UWorld* World = GetWorld(); check(World); if (Value.X != 0.0f) { Pawn->AddControllerYawInput(Value.X * LyraHero::LookYawRate * World->GetDeltaSeconds()); } if (Value.Y != 0.0f) { Pawn->AddControllerPitchInput(Value.Y * LyraHero::LookPitchRate * World->GetDeltaSeconds()); } } void ULyraHeroComponent::Input_Crouch(const FInputActionValue& InputActionValue) { if (ALyraCharacter* Character = GetPawn()) { Character->ToggleCrouch(); } } void ULyraHeroComponent::Input_AutoRun(const FInputActionValue& InputActionValue) { if (APawn* Pawn = GetPawn()) { if (ALyraPlayerController* Controller = Cast(Pawn->GetController())) { // Toggle auto running Controller->SetIsAutoRunning(!Controller->GetIsAutoRunning()); } } } TSubclassOf ULyraHeroComponent::DetermineCameraMode() const { if (AbilityCameraMode) { return AbilityCameraMode; } const APawn* Pawn = GetPawn(); if (!Pawn) { return nullptr; } if (ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) { if (const ULyraPawnData* PawnData = PawnExtComp->GetPawnData()) { return PawnData->DefaultCameraMode; } } return nullptr; } void ULyraHeroComponent::SetAbilityCameraMode(TSubclassOf CameraMode, const FGameplayAbilitySpecHandle& OwningSpecHandle) { if (CameraMode) { AbilityCameraMode = CameraMode; AbilityCameraModeOwningSpecHandle = OwningSpecHandle; } } void ULyraHeroComponent::ClearAbilityCameraMode(const FGameplayAbilitySpecHandle& OwningSpecHandle) { if (AbilityCameraModeOwningSpecHandle == OwningSpecHandle) { AbilityCameraMode = nullptr; AbilityCameraModeOwningSpecHandle = FGameplayAbilitySpecHandle(); } }