04. 캐릭터 이동하기 - Elysia-ff/UE4-Custom_Stencil_Tutorial GitHub Wiki

Nav Mesh Volume 추가

Place Actors -> Volumes 에서 Nav Mesh Bounds Volume을 Level에 끌어다 놓고 크기를 적당히 맞춰준다. image

이제 Viewport 에서 P를 누르거나 Show -> Navigation을 체크하면 액터가 이동할 수 있는 지역이 초록색으로 표시된다. image


AIController 추가

캐릭터가 Nav Mesh상에서 움직이려면 UE에서 제공하는 네비게이션 시스템을 이용하면 된다.
StealthTutorial.Build.cs를 다음과 같이 수정한다.

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "AIModule" });

AAIController를 확장해서 AHitmanAIController를 생성하고 Hitman.h에 아래 코드를 추가한다.

public:
	AHitman(const FObjectInitializer& ObjectInitializer);

Hitman.cpp에 방금 추가한 생성자를 정의하고 AHitmanAIController를 기본 컨트롤러로 등록한다.

#include "Character/Controller/HitmanAIController.h"

AHitman::AHitman(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	AIControllerClass = AHitmanAIController::StaticClass();
}

그다음 StealthPlayerController.cppBeginPlay를 다음과 같이 수정한다.

Hitman = GetWorld()->SpawnActor<AHitman>(HitmanBlueprint, FVector(0.0f, 0.0f, 90.0f), FRotator::ZeroRotator, Param);
check(Hitman);
Hitman->SpawnDefaultController();

이제 게임을 시작하면 AHitman과 함께 AHitmanAIController가 Level에 생성된다.
image


캐릭터 이동

AIControllerMoveToLocation이나 MoveToActor를 호출하면 빙의된 Pawn이 해당 지점으로 이동한다.
MoveToLocation은 목적지 좌표로 이동하고, MoveToActor는 목표 Actor의 좌표로 이동하되 Actor가 이동하면 따라간다.

여기서는 마우스 오른클릭을 누르고 있으면 마우스를 따라 이동하게 하자.
오른클릭 버튼을 Input에 바인드 하고 StealthPlayerController.h에 다음 코드를 추가한다.

protected:
	virtual void SetupInputComponent() override;

private:
	void OnRMBPressed();

	void OnRMBReleased();

	void MoveHitmanToMouseCursor();

private:
	bool bOnRMB;

StealthPlayerController.cpp에 각 함수를 정의한다.

#include "Character/Controller/HitmanAIController.h"

void AStealthPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();

	InputComponent->BindAction("RMB", IE_Pressed, this, &AStealthPlayerController::OnRMBPressed);
	InputComponent->BindAction("RMB", IE_Released, this, &AStealthPlayerController::OnRMBReleased);
}

void AStealthPlayerController::OnRMBPressed()
{
	bOnRMB = true;
}

void AStealthPlayerController::OnRMBReleased()
{
	bOnRMB = false;
}

void AStealthPlayerController::MoveHitmanToMouseCursor()
{
	FHitResult HitResult;
	if (GetHitResultUnderCursor(ECollisionChannel::ECC_Visibility, false, HitResult))
	{
		FVector TargetLocation = HitResult.ImpactPoint;

		check(Hitman && Hitman->GetController<AHitmanAIController>());
		Hitman->GetController<AHitmanAIController>()->MoveToLocation(TargetLocation);
	}
}

이제 StealthPlayerController.cppPlayerTick하단에 다음 코드를 추가하고 실행하면 캐릭터가 마우스를 따라 움직인다.

if (bOnRMB)
{
	MoveHitmanToMouseCursor();
}

mouse_input


완성된 코드

/Character/Hitman.h
// Copyright 2021. Elysia-ff

#pragma once

#include "CoreMinimal.h"
#include "Character/StealthCharacter.h"
#include "Hitman.generated.h"

/**
 * 
 */
UCLASS()
class STEALTHTUTORIAL_API AHitman : public AStealthCharacter
{
	GENERATED_BODY()
	
public:
	AHitman(const FObjectInitializer& ObjectInitializer);
};
/Character/Hitman.cpp
// Copyright 2021. Elysia-ff


#include "Hitman.h"

#include "Character/Controller/HitmanAIController.h"

AHitman::AHitman(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	AIControllerClass = AHitmanAIController::StaticClass();
}
/Character/Controller/HitmanAIController.h
// Copyright 2021. Elysia-ff

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "HitmanAIController.generated.h"

/**
 * 
 */
UCLASS()
class STEALTHTUTORIAL_API AHitmanAIController : public AAIController
{
	GENERATED_BODY()
	
};
/Player/StealthPlayerController.h
// Copyright 2021. Elysia-ff

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "StealthPlayerController.generated.h"

class AHitman;

/**
 * 
 */
UCLASS()
class STEALTHTUTORIAL_API AStealthPlayerController : public APlayerController
{
	GENERATED_BODY()
	
public:
	AStealthPlayerController(const FObjectInitializer& ObjectInitializer);

	virtual void PlayerTick(float DeltaTime) override;

protected:
	virtual void BeginPlay() override;

	virtual void SetupInputComponent() override;

private:
	void OnRMBPressed();

	void OnRMBReleased();

	void MoveHitmanToMouseCursor();

private:
	UPROPERTY()
	UClass* HitmanBlueprint;

	UPROPERTY()
	AHitman* Hitman;

	bool bOnRMB;
};
/Player/StealthPlayerController.cpp
// Copyright 2021. Elysia-ff


#include "StealthPlayerController.h"

#include "Character/Controller/HitmanAIController.h"
#include "Character/Hitman.h"
#include "Player/StealthPlayerCamera.h"

AStealthPlayerController::AStealthPlayerController(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	PrimaryActorTick.bCanEverTick = true;
	bShowMouseCursor = true;

	static ConstructorHelpers::FObjectFinder<UClass> HitmanObjectFinder(TEXT("Blueprint'/Game/Blueprint/Character/BP_Hitman.BP_Hitman_C'"));
	check(HitmanObjectFinder.Succeeded());
	HitmanBlueprint = HitmanObjectFinder.Object;
}

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

	AStealthPlayerCamera* PossessedPawn = GetPawn<AStealthPlayerCamera>();
	if (PossessedPawn != nullptr && GEngine != nullptr)
	{
		FVector2D MouseInput;
		if (GetMousePosition(MouseInput.X, MouseInput.Y))
		{
			FVector2D MoveInput;
			const FIntPoint ViewportSize = GEngine->GameViewport->Viewport->GetSizeXY();
			
			if (MouseInput.X <= PossessedPawn->EdgePixel)
			{
				MoveInput.Y = -1.0f;
			}
			else if (MouseInput.X >= ViewportSize.X - PossessedPawn->EdgePixel)
			{
				MoveInput.Y = 1.0f;
			}

			if (MouseInput.Y <= PossessedPawn->EdgePixel)
			{
				MoveInput.X = 1.0f;
			}
			else if (MouseInput.Y >= ViewportSize.Y - PossessedPawn->EdgePixel)
			{
				MoveInput.X = -1.0f;
			}

			if (!MoveInput.IsZero())
			{
				PossessedPawn->AddMoveInput(MoveInput);
			}
		}
	}

	if (bOnRMB)
	{
		MoveHitmanToMouseCursor();
	}
}

void AStealthPlayerController::BeginPlay()
{
	Super::BeginPlay();

	check(HitmanBlueprint);

	FActorSpawnParameters Param;
	Param.Owner = this;
	Param.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

	Hitman = GetWorld()->SpawnActor<AHitman>(HitmanBlueprint, FVector(0.0f, 0.0f, 90.0f), FRotator::ZeroRotator, Param);
	check(Hitman);
	Hitman->SpawnDefaultController();
}

void AStealthPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();

	InputComponent->BindAction("RMB", IE_Pressed, this, &AStealthPlayerController::OnRMBPressed);
	InputComponent->BindAction("RMB", IE_Released, this, &AStealthPlayerController::OnRMBReleased);
}

void AStealthPlayerController::OnRMBPressed()
{
	bOnRMB = true;
}

void AStealthPlayerController::OnRMBReleased()
{
	bOnRMB = false;
}

void AStealthPlayerController::MoveHitmanToMouseCursor()
{
	FHitResult HitResult;
	if (GetHitResultUnderCursor(ECollisionChannel::ECC_Visibility, false, HitResult))
	{
		FVector TargetLocation = HitResult.ImpactPoint;

		check(Hitman && Hitman->GetController<AHitmanAIController>());
		Hitman->GetController<AHitmanAIController>()->MoveToLocation(TargetLocation);
	}
}
⚠️ **GitHub.com Fallback** ⚠️