Files
utad-ui/Source/UTAD_UI/UTAD_UIPlayerController.cpp
2025-10-12 15:51:44 +02:00

281 lines
7.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "UTAD_UIPlayerController.h"
#include "GameFramework/Pawn.h"
#include "Blueprint/AIBlueprintHelperLibrary.h"
#include "NiagaraSystem.h"
#include "NiagaraFunctionLibrary.h"
#include "UTAD_UICharacter.h"
#include "Engine/World.h"
#include "EnhancedInputComponent.h"
#include "InputActionValue.h"
#include "EnhancedInputSubsystems.h"
#include "Engine/LocalPlayer.h"
#include "Blueprint/UserWidget.h"
#include "SkillTree/SkillTreeComponent.h"
DEFINE_LOG_CATEGORY(LogTemplateCharacter);
AUTAD_UIPlayerController::AUTAD_UIPlayerController()
{
bShowMouseCursor = true;
DefaultMouseCursor = EMouseCursor::Default;
CachedDestination = FVector::ZeroVector;
FollowTime = 0.f;
}
void AUTAD_UIPlayerController::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Create and show the main HUD
ShowMainHUD();
}
void AUTAD_UIPlayerController::SetupInputComponent()
{
// set up gameplay key bindings
Super::SetupInputComponent();
// Add Input Mapping Context
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(
GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent =
Cast<UEnhancedInputComponent>(InputComponent))
{
// Setup mouse input events
EnhancedInputComponent->BindAction(
SetDestinationClickAction, ETriggerEvent::Started, this,
&AUTAD_UIPlayerController::OnInputStarted);
EnhancedInputComponent->BindAction(
SetDestinationClickAction, ETriggerEvent::Triggered, this,
&AUTAD_UIPlayerController::OnSetDestinationTriggered);
EnhancedInputComponent->BindAction(
SetDestinationClickAction, ETriggerEvent::Completed, this,
&AUTAD_UIPlayerController::OnSetDestinationReleased);
EnhancedInputComponent->BindAction(
SetDestinationClickAction, ETriggerEvent::Canceled, this,
&AUTAD_UIPlayerController::OnSetDestinationReleased);
// Setup touch input events
EnhancedInputComponent->BindAction(
SetDestinationTouchAction, ETriggerEvent::Started, this,
&AUTAD_UIPlayerController::OnInputStarted);
EnhancedInputComponent->BindAction(
SetDestinationTouchAction, ETriggerEvent::Triggered, this,
&AUTAD_UIPlayerController::OnTouchTriggered);
EnhancedInputComponent->BindAction(
SetDestinationTouchAction, ETriggerEvent::Completed, this,
&AUTAD_UIPlayerController::OnTouchReleased);
EnhancedInputComponent->BindAction(
SetDestinationTouchAction, ETriggerEvent::Canceled, this,
&AUTAD_UIPlayerController::OnTouchReleased);
// Setup skill tree input
if (OpenSkillTreeAction)
{
EnhancedInputComponent->BindAction(
OpenSkillTreeAction, ETriggerEvent::Started, this,
&AUTAD_UIPlayerController::OnOpenSkillTree);
}
}
else
{
UE_LOG(
LogTemplateCharacter, Error,
TEXT(
"'%s' Failed to find an Enhanced Input Component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."),
*GetNameSafe(this));
}
}
void AUTAD_UIPlayerController::OnInputStarted()
{
StopMovement();
}
// Triggered every frame when the input is held down
void AUTAD_UIPlayerController::OnSetDestinationTriggered()
{
// We flag that the input is being pressed
FollowTime += GetWorld()->GetDeltaSeconds();
// We look for the location in the world where the player has pressed the
// input
FHitResult Hit;
bool bHitSuccessful = false;
if (bIsTouch)
{
bHitSuccessful = GetHitResultUnderFinger(
ETouchIndex::Touch1, ECollisionChannel::ECC_Visibility, true, Hit);
}
else
{
bHitSuccessful = GetHitResultUnderCursor(
ECollisionChannel::ECC_Visibility, true, Hit);
}
// If we hit a surface, cache the location
if (bHitSuccessful)
{
CachedDestination = Hit.Location;
}
// Move towards mouse pointer or touch
APawn* ControlledPawn = GetPawn();
if (ControlledPawn != nullptr)
{
FVector WorldDirection =
(CachedDestination - ControlledPawn->GetActorLocation())
.GetSafeNormal();
ControlledPawn->AddMovementInput(WorldDirection, 1.0, false);
}
}
void AUTAD_UIPlayerController::OnSetDestinationReleased()
{
// If it was a short press
if (FollowTime <= ShortPressThreshold)
{
// We move there and spawn some particles
UAIBlueprintHelperLibrary::SimpleMoveToLocation(this,
CachedDestination);
UNiagaraFunctionLibrary::SpawnSystemAtLocation(
this, FXCursor, CachedDestination, FRotator::ZeroRotator,
FVector(1.f, 1.f, 1.f), true, true, ENCPoolMethod::None, true);
}
FollowTime = 0.f;
}
// Triggered every frame when the input is held down
void AUTAD_UIPlayerController::OnTouchTriggered()
{
bIsTouch = true;
OnSetDestinationTriggered();
}
void AUTAD_UIPlayerController::OnTouchReleased()
{
bIsTouch = false;
OnSetDestinationReleased();
}
void AUTAD_UIPlayerController::OnOpenSkillTree()
{
ToggleSkillTree();
}
void AUTAD_UIPlayerController::ShowMainHUD()
{
if (MainHUDClass && !MainHUDWidget)
{
MainHUDWidget = CreateWidget<UUserWidget>(this, MainHUDClass);
if (MainHUDWidget)
{
MainHUDWidget->AddToViewport(0);
}
}
}
void AUTAD_UIPlayerController::ShowSkillTree()
{
if (!SkillTreeWidget && SkillTreeWidgetClass)
{
SkillTreeWidget = CreateWidget<UUserWidget>(this, SkillTreeWidgetClass);
}
if (SkillTreeWidget && !SkillTreeWidget->IsInViewport())
{
SkillTreeWidget->AddToViewport(1);
// Use GameAndUI mode - allows Enhanced Input to work while UI is open
// Mouse clicks will be consumed by UI widgets automatically
FInputModeGameAndUI InputMode;
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
InputMode.SetHideCursorDuringCapture(false);
SetInputMode(InputMode);
bShowMouseCursor = true;
// Switch input mapping context from gameplay to UI
// This allows UI-specific controls (like Tab to close) to work
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(
GetLocalPlayer()))
{
// Remove gameplay context to prevent movement actions
if (DefaultMappingContext)
{
Subsystem->RemoveMappingContext(DefaultMappingContext);
}
// Add UI context with higher priority
if (UIMappingContext)
{
Subsystem->AddMappingContext(UIMappingContext, 1);
}
}
}
}
void AUTAD_UIPlayerController::HideSkillTree()
{
if (SkillTreeWidget && SkillTreeWidget->IsInViewport())
{
// Clear any unconfirmed skill selections
if (const AUTAD_UICharacter* PlayerCharacter =
Cast<AUTAD_UICharacter>(GetPawn()))
{
if (USkillTreeComponent* SkillTreeComp =
PlayerCharacter->GetSkillTreeComponent())
{
SkillTreeComp->ClearSelection();
}
}
SkillTreeWidget->RemoveFromParent();
// Restore game and UI input mode for top-down gameplay
FInputModeGameAndUI InputMode;
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
InputMode.SetHideCursorDuringCapture(false);
SetInputMode(InputMode);
bShowMouseCursor = true;
// Switch input mapping context back from UI to gameplay
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(
GetLocalPlayer()))
{
// Remove UI context
if (UIMappingContext)
{
Subsystem->RemoveMappingContext(UIMappingContext);
}
// Restore gameplay context
if (DefaultMappingContext)
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
}
void AUTAD_UIPlayerController::ToggleSkillTree()
{
if (SkillTreeWidget && SkillTreeWidget->IsInViewport())
{
HideSkillTree();
}
else
{
ShowSkillTree();
}
}