277 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			7.4 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);
 | |
| 
 | |
| 		// Switch to UI-only input mode to block gameplay actions (like click-to-move)
 | |
| 		FInputModeUIOnly InputMode;
 | |
| 		InputMode.SetWidgetToFocus(SkillTreeWidget->TakeWidget());
 | |
| 		InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
 | |
| 		SetInputMode(InputMode);
 | |
| 		bShowMouseCursor = true;
 | |
| 
 | |
| 		// Switch input mapping context from gameplay to UI
 | |
| 		if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
 | |
| 				ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(
 | |
| 					GetLocalPlayer()))
 | |
| 		{
 | |
| 			// Remove gameplay context
 | |
| 			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 (AUTAD_UICharacter* Character = Cast<AUTAD_UICharacter>(GetPawn()))
 | |
| 		{
 | |
| 			if (USkillTreeComponent* SkillTreeComp = Character->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();
 | |
| 	}
 | |
| }
 |