로스트아크는 Top-Down 뷰로 캐릭터를 조작하기에 탑 다운 템플릿으로 프로젝트를 만들었다
본격적으로 게임을 만들기 전에 먼저 캐릭터 컨트롤을 완성하고 콘텐츠 제작을 하려고 한다

템플릿의 처음 시작화면이다. 여기서 왼쪽 마우스 클릭으로 맵을 클릭하면 커서가 찍히며 캐릭터가 해당 방향으로 움직인다

먼저 탑다운 템플릿을 분석해보자
C++ 클래스로 GameMode, Character, PlayerController가 만들어져 있다

그리고 C++ 클래스들을 상속받은 블루프린트 클래스들이 있어 이 클래스들이 게임 모드에 설정되어 있어 에디터에서 손 쉽게 Input 같은 객체의 값을 바꿀 수 있게 해 놓은 것 같다
ALostKingdomGameMode::ALostKingdomGameMode()
{
// use our custom PlayerController class
PlayerControllerClass = ALostKingdomPlayerController::StaticClass();
// set default pawn class to our Blueprinted character
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/TopDown/Blueprints/BP_TopDownCharacter"));
if (PlayerPawnBPClass.Class != nullptr)
{
DefaultPawnClass = PlayerPawnBPClass.Class;
}
// set default controller to our Blueprinted controller
static ConstructorHelpers::FClassFinder<APlayerController> PlayerControllerBPClass(TEXT("/Game/TopDown/Blueprints/BP_TopDownPlayerController"));
if(PlayerControllerBPClass.Class != NULL)
{
PlayerControllerClass = PlayerControllerBPClass.Class;
}
}

BP_TopDownPlayerController 블루프린트 클래스를 열어보면 다음과 같이 입력 시스템을 넣어줄 수 있다
언리얼 5에서는 향상된 입력 시스템을 사용하기에 Mapping Context를 수정하면 왼쪽 클릭해서 이동하던 것을 쉽게 오른쪽 클릭으로 이동할 수 있게 된다

코드를 다 업로드하고 설명하기에는 너무 길어질 것 같아 요약하자면 게임 모드에서는 플레이어 컨트롤러와 플레이어가 조종할 폰 클래스를 할당해서 게임을 시작하면 플레이어 컨트롤러는 InputMappingContext를 설정하고 입력을 함수와 연결한다.
캐릭터에서는 단순히 컨트롤 값만 설정한다
지금 템플릿에 있는 기능들로도 충분히 게임을 만들기 좋지만 아무래도 내가 작성한 코드가 아니기 때문에 성장에는 도움이 안 될 것 같기도하고 내가 만들려 하는 게임과 조금 다른 점들도 있기 때문에 하나씩 바꾸어 보려고 한다
GameMode, PlayerController, Character 부터 다시 C++ 클래스로 만들겠다
새로 LKGameModeBase라는 클래스를 만들었다
아 그런데 이게 무슨 일, 시작하자마자 빌드 실패뜨는데

아 한 시간의 사투 끝에 해결법을 찾았다. LostKingdom.Build.cs에서 한 줄을 추가했더니 빌드가 됐다!!!!
이게 Source 폴더 바로 아래에 있는 {내 프로젝트} 폴더인데 이 폴더를 Public으로 만들어 어디에서든 접근하게 만드는 것 같다... 애초에 클래스를 만들 때 Public과 Private을 설정할 수 있었는데 그 방법을 하면 Public 폴더, Private 폴더를 따로 만들어야 해서 번거로웠던 것 같다... 이제 알았으니 됐다
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class LostKingdom : ModuleRules
{
public LostKingdom(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(new string[] { "LostKingdom" }); // 이 줄 추가
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "NavigationSystem", "AIModule", "Niagara", "EnhancedInput" });
}
}
LKGameModeBase 클래스를 만들고 폰은 아직 안 만들어 기존 템플릿에 있는 폰을 사용했고 컨트롤러는 LKPlayerController 클래스를 만들어서 할당해주었다
ALKGameModeBase::ALKGameModeBase()
{
static ConstructorHelpers::FClassFinder<APawn> DefaultPawnClassRef(TEXT("/Game/TopDown/Blueprints/BP_TopDownCharacter"));
if (DefaultPawnClassRef.Class)
{
DefaultPawnClass = DefaultPawnClassRef.Class;
}
PlayerControllerClass = ALKPlayerController::StaticClass();
}
빌드하고 월드세팅을 보면 다음과 같이 바뀌어 있는 것을 볼 수 있다

이제 게임을 실행해보면 당연히 컨트롤러에는 아무 기능이 없기에 캐릭터가 전혀 움직이지 않는다
이제 플레이어 컨트롤러를 설정해보자
플레이어의 입력을 설정해야 한다
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "InputActionValue.h"
#include "LKPlayerController.generated.h"
/**
*
*/
class UNiagaraSystem;
UCLASS()
class LOSTKINGDOM_API ALKPlayerController : public APlayerController
{
GENERATED_BODY()
public:
ALKPlayerController();
/** FX Class that we will spawn when clicking */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
UNiagaraSystem* FXCursor;
/** MappingContext */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputMappingContext* DefaultMappingContext;
/** Click Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* SetDestinationClickAction;
protected:
virtual void BeginPlay() override;
virtual void SetupInputComponent() override;
/** Input handlers for SetDestination action. */
void OnInputStarted();
void OnSetDestinationTriggered();
void OnSetDestinationReleased();
private:
FVector Destination;
};
위에서 부터 보면
UNiagaraSystem이라는 언리얼의 파티클?과 관련이 있는 시스템같은데 일단 여기서는 마우스 입력한 곳의 커서 이펙트가 표시 된다. UInputMappingContext은 입력 매핑 컨텍스트UInputAction은 키 입력Destination은 사용자가 클릭한 곳의 위치를 저장할 것BeginPlay에서 로컬플레이어 서브시스템을 가져와 헤더 파일에 있던 입력 매핑 컨텍스트를 추가하고SetupInputComponent에서 입력 컴포넌트를 향상된 입력 컴포넌트로 캐스팅한 후 클릭, 길게 클릭, 뗄 때의 이벤트에 아까 On 함수들을 등록해주면 이벤트가 발생 시 해당 함수가 실행된다OnInputStarted 함수가 호출되면 부모 클래스인 APlayerController의 StopMovement가 호출되어 이동을 중지함OnSetDestinationTriggered 함수가 호출되면 마우스 커서의 충돌 결과인 FHitResult 구조체를 얻어와 충돌한 곳이 캐릭터가 성공적으로 갈 수 있으면 Destination을 업데이트 해 이동 방향을 계산해서 움직임을 시도한다(여기서 움직임을 구현하는 이유는 꾹 누르는 경우가 있기 때문에)OnSetDestinationReleased 함수는 마우스를 뗄 때 호출되니까 목적지로 이동하고 커서 이펙트를 생성한다참고로 입력 매핑 컨텍스트나 InputAction을 코드에서 할당해주기 조금 번거로우니까 플레이어 컨트롤러 블루프린트 클래스를 만들고 C++ 클래스를 상속받아 에디터에서 할당하는 편한 방법을 선택하자! -> 이러면 게임 모드 코드도 바꿔줘야 함
// Set Player Controller
static ConstructorHelpers::FClassFinder<APlayerController> PlayerControllerClassRef(TEXT("/Script/Engine.Blueprint'/Game/LostKingdom/Blueprint/BP_LKPlayerController.BP_LKPlayerController_C'"));
if (PlayerControllerClassRef.Class)
{
PlayerControllerClass = PlayerControllerClassRef.Class;
}
// PlayerControllerClass = ALKPlayerController::StaticClass();
이제 정상적으로 나만의 C++ 클래스를 만들고 블루프린트를 상속받아 기본적인 움직임만 구현해봤다. 이제 다음으로는 움직임을 더 자세히 구현하고 캐릭터에 대한 것도 자세히 다뤄보자