본문 바로가기

언리얼러닝/C++스크립트게임개발

EnhancedInput vs LegacyInput, Input Error

연습하면서 출판사 사이트의 소스코드를 죄다 카피해서 복붙했더니 캐릭터가 안움직였다.

안그래도 TopDownProject의 Input이 Enhanced인데 왜 Legacy방식의 Attack Actoion을 만드는지 이상했는데 여기가 문제가 있다. 코드가 다르다. 교재의 소스는 엤날거랑 엤날 인풋액션을 만들어주던지 소스를 뜯어고치던지 해야한다.

그냥 옜날방식 input Action을 ProjectSetting에 추가하기로 했다. 잘된다.

하여간 새로운 TopDownProject의 소스를 보면 Tick()이 없다.

대신 Input Event을 4단계로 구분해 TriggerEvent

EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Started, this, &APangeaPlayerController::OnInputStarted);
EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Triggered, this, &APangeaPlayerController::OnSetDestinationTriggered);
EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Completed, this, &APangeaPlayerController::OnSetDestinationReleased);
EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Canceled, this, &APangeaPlayerController::OnSetDestinationReleased);

Tick()을 사용하지 않고 Left 클릭이 된경우만 AddMovementInput으로 이동시키고 있다.

// Triggered every frame when the input is held down
void APangeaPlayerController::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);
	}
}
EnhanceInput  LegacyInput
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Templates/SubclassOf.h"
#include "GameFramework/PlayerController.h"
#include "PangeaPlayerController.generated.h"

/** Forward declaration to improve compiling times */
class UNiagaraSystem;
class UInputMappingContext;
class UInputAction;

DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);

UCLASS()
class APangeaPlayerController : public APlayerController
{
GENERATED_BODY()

public:
APangeaPlayerController();

/** Time Threshold to know if it was a short press */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
float ShortPressThreshold;

/** FX Class that we will spawn when clicking */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UNiagaraSystem* FXCursor;

/** MappingContext */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputMappingContext* DefaultMappingContext;

/** Jump Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputAction* SetDestinationClickAction;

/** Jump Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputAction* SetDestinationTouchAction;

protected:
/** True if the controlled character should navigate to the mouse cursor. */
uint32 bMoveToMouseCursor : 1;

virtual void SetupInputComponent() override;

// To add mapping context
virtual void BeginPlay();

/** Input handlers for SetDestination action. */
void OnInputStarted();
void OnSetDestinationTriggered();
void OnSetDestinationReleased();
void OnTouchTriggered();
void OnTouchReleased();

private:
FVector CachedDestination;

bool bIsTouch; // Is it a touch device
float FollowTime; // For how long it has been pressed
};
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Templates/SubclassOf.h"
#include "GameFramework/PlayerController.h"
#include "PangaeaPlayerController.generated.h"

/** Forward declaration to improve compiling times */
class UNiagaraSystem;

UCLASS()
class APangaeaPlayerController : public APlayerController
{
GENERATED_BODY()

public:
APangaeaPlayerController();

/** Time Threshold to know if it was a short press */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
float ShortPressThreshold;

/** FX Class that we will spawn when clicking */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UNiagaraSystem* FXCursor;

protected:
/** True if the controlled character should navigate to the mouse cursor. */
uint32 bMoveToMouseCursor : 1;

// Begin PlayerController interface
virtual void PlayerTick(float DeltaTime) override;
virtual void SetupInputComponent() override;
// End PlayerController interface

/** Input handlers for SetDestination action. */
void OnSetDestinationPressed();
void OnSetDestinationReleased();
void OnTouchPressed(const ETouchIndex::Type FingerIndex, const FVector Location);
void OnTouchReleased(const ETouchIndex::Type FingerIndex, const FVector Location);

/** Input Handlers for Attack action. */
void OnAttackPressed();

private:
bool bInputPressed; // Input is bring pressed
bool bIsTouch; // Is it a touch device
float FollowTime; // For how long it has been pressed
};


// Copyright Epic Games, Inc. All Rights Reserved.

#include "PangeaPlayerController.h"
#include "GameFramework/Pawn.h"
#include "Blueprint/AIBlueprintHelperLibrary.h"
#include "NiagaraSystem.h"
#include "NiagaraFunctionLibrary.h"
#include "PangeaCharacter.h"
#include "Engine/World.h"
#include "EnhancedInputComponent.h"
#include "InputActionValue.h"
#include "EnhancedInputSubsystems.h"
#include "Engine/LocalPlayer.h"

DEFINE_LOG_CATEGORY(LogTemplateCharacter);

APangeaPlayerController::APangeaPlayerController()
{
bShowMouseCursor = true;
DefaultMouseCursor = EMouseCursor::Default;
CachedDestination = FVector::ZeroVector;
FollowTime = 0.f;
}

void APangeaPlayerController::BeginPlay()
{
// Call the base class  
Super::BeginPlay();

//Add Input Mapping Context
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}

void APangeaPlayerController::SetupInputComponent()
{
// set up gameplay key bindings
Super::SetupInputComponent();

// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(InputComponent))
{
// Setup mouse input events
EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Started, this, &APangeaPlayerController::OnInputStarted);
EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Triggered, this, &APangeaPlayerController::OnSetDestinationTriggered);
EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Completed, this, &APangeaPlayerController::OnSetDestinationReleased);
EnhancedInputComponent->BindAction(SetDestinationClickAction, ETriggerEvent::Canceled, this, &APangeaPlayerController::OnSetDestinationReleased);

// Setup touch input events
EnhancedInputComponent->BindAction(SetDestinationTouchAction, ETriggerEvent::Started, this, &APangeaPlayerController::OnInputStarted);
EnhancedInputComponent->BindAction(SetDestinationTouchAction, ETriggerEvent::Triggered, this, &APangeaPlayerController::OnTouchTriggered);
EnhancedInputComponent->BindAction(SetDestinationTouchAction, ETriggerEvent::Completed, this, &APangeaPlayerController::OnTouchReleased);
EnhancedInputComponent->BindAction(SetDestinationTouchAction, ETriggerEvent::Canceled, this, &APangeaPlayerController::OnTouchReleased);
}
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 APangeaPlayerController::OnInputStarted()
{
StopMovement();
}

// Triggered every frame when the input is held down
void APangeaPlayerController::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 APangeaPlayerController::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 APangeaPlayerController::OnTouchTriggered()
{
bIsTouch = true;
OnSetDestinationTriggered();
}

void APangeaPlayerController::OnTouchReleased()
{
bIsTouch = false;
OnSetDestinationReleased();
}
// Copyright Epic Games, Inc. All Rights Reserved.

#include "PangaeaPlayerController.h"
#include "GameFramework/Pawn.h"
#include "Blueprint/AIBlueprintHelperLibrary.h"
#include "NiagaraSystem.h"
#include "NiagaraFunctionLibrary.h"
#include "PangaeaCharacter.h"
#include "Engine/World.h"
#include "PlayerAvatar.h"

APangaeaPlayerController::APangaeaPlayerController()
{
bShowMouseCursor = true;
DefaultMouseCursor = EMouseCursor::Default;
}

void APangaeaPlayerController::PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);


if(bInputPressed)
{
FollowTime += DeltaTime;

// Look for the touch location
FVector HitLocation = FVector::ZeroVector;
FHitResult Hit;
if(bIsTouch)
{
GetHitResultUnderFinger(ETouchIndex::Touch1, ECC_Visibility, true, Hit);
}
else
{
GetHitResultUnderCursor(ECC_Visibility, true, Hit);
}
HitLocation = Hit.Location;

// Direct the Pawn towards that location
APawn* const MyPawn = GetPawn();
if(MyPawn)
{
FVector WorldDirection = (HitLocation - MyPawn->GetActorLocation()).GetSafeNormal();
MyPawn->AddMovementInput(WorldDirection, 1.f, false);
}
}
else
{
FollowTime = 0.f;
}
}

void APangaeaPlayerController::SetupInputComponent()
{
// set up gameplay key bindings
Super::SetupInputComponent();

InputComponent->BindAction("SetDestination", IE_Pressed, this, &APangaeaPlayerController::OnSetDestinationPressed);
InputComponent->BindAction("SetDestination", IE_Released, this, &APangaeaPlayerController::OnSetDestinationReleased);

InputComponent->BindAction("Attack", IE_Pressed, this, &APangaeaPlayerController::OnAttackPressed);

// support touch devices 
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &APangaeaPlayerController::OnTouchPressed);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &APangaeaPlayerController::OnTouchReleased);

}

void APangaeaPlayerController::OnSetDestinationPressed()
{
// We flag that the input is being pressed
bInputPressed = true;
// Just in case the character was moving because of a previous short press we stop it
StopMovement();
}

void APangaeaPlayerController::OnSetDestinationReleased()
{
// Player is no longer pressing the input
bInputPressed = false;

// If it was a short press
if(FollowTime <= ShortPressThreshold)
{
// We look for the location in the world where the player has pressed the input
FVector HitLocation = FVector::ZeroVector;
FHitResult Hit;
GetHitResultUnderCursor(ECC_Visibility, true, Hit);
HitLocation = Hit.Location;

// We move there and spawn some particles
UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, HitLocation);
UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, FXCursor, HitLocation, FRotator::ZeroRotator, FVector(1.f, 1.f, 1.f), true, true, ENCPoolMethod::None, true);
}
}

void APangaeaPlayerController::OnTouchPressed(const ETouchIndex::Type FingerIndex, const FVector Location)
{
bIsTouch = true;
OnSetDestinationPressed();
}

void APangaeaPlayerController::OnTouchReleased(const ETouchIndex::Type FingerIndex, const FVector Location)
{
bIsTouch = false;
OnSetDestinationReleased();
}

void APangaeaPlayerController::OnAttackPressed()
{
auto playerAvatar = Cast<APlayerAvatar>(GetPawn());
if (playerAvatar->CanAttack())
{
playerAvatar->Attack();
}
}