언리얼 5

230829 캐릭터 이동 및 카메라 회전

슬뷔 2023. 8. 29. 11:42

ACharacter_Base 의 부모 클래스가 ACharacter인데 Super로 type 재정의를 한 것이다.

(generate_body 에 정의되어 있다.)

 

#include "EngineMinimal.h" => 자주 사용할 것 같은 기능들, component 를 묶어놓은 헤더 

class UNREAL_3TH_API ACharacter_Base : public ACharacter
{
private:
	UPROPERTY(EditAnywhere, Category = "Component")
    	UCameraComponent*				m_Cam;
        
        UPROPERTY(EditAnywhere, Category = "Component")
	USpringArmComponent*				m_Arm;
    
    	UPROPERTY(EditAnywhere, Category = "Input")
	TSoftObjectPtr<UInputMappingContext>		InputMapping;

	UPROPERTY(EditAnywhere, Category = "Input")
	TSoftObjectPtr<UInputAction>			MoveAction;

	UPROPERTY(EditAnywhere, Category = "Input")
	TSoftObjectPtr<UInputAction>			RotationAction;
}

본인쪽 멤버가 주소를 받아서 바로 접근이 가능하게 한다.

 

* UPROPERTY 사용시 이점

1. EditAnywhere 가 없으면 언리얼에서 디테일창이 뜨지 않는다.

즉, 있어야 디테일 창이 뜬다!

 

2. 컴포넌트창이 생긴다 

ACharacter_Base::ACharacter_Base()
	: m_Cam(nullptr)
	, m_Arm(nullptr)
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// TEXT 안에는 이름 부여
	// Camera Component 와 SpringArm Component 를 생성해서 ACharacter_Base 에게 추가
    	// 둘 다 SceneComponent 이다.
	m_Cam = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
	m_Arm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
    
    	// SceneComponent 사이의 계층관계 구조 설정
	// 캡슐컴포넌트 밑에 달리게 된다
	m_Arm->SetupAttachment(GetCapsuleComponent());
	m_Cam->SetupAttachment(m_Arm);
}

CreateDefaultSubobject => 이 안에서 생성하고 Actor에 넣는 기능까지 들어가 있다.

 

CDO ( Class Default Object ) => 예제 객체가 하나 만들어진다.

 

 

블루프린트 스크립트 노드

시퀀스 ==> 앞 뒤 말고 좌우도 있어서 둠

branch ==> true / false 

캐릭터 무브먼트 -> add input vector 호출 ( 더블클릭해보면 c++ 함수로 볼 수 있다 )

get actor forward vector -> 앞의 방향 벡터를 가져와서 break vector 2d 의 x와 곱하고 그 값을 add input vector 의 world vector 에 준다.

( 곱한 이유 : w 키를 누르면 앞으로 가고 s 를 누르면 뒤로 가야하기 때문에, 곱해서 -1 이 나오게 하기 위해 )

 

블루프린트 스크립트 노드를 c++ 로 구현

Build.cs 에 PublicDependencyModuleNames.AddRange(new string[] { "EnhancedInput" }) 작성

위를 적으면 #include "InputMappingContext.h" 을 사용할 수 있다

그리고 비쥬얼스튜디오를 끄고, uproject 오른쪽 마우스 눌러서 generate visual studio project file 누르면 다시 생성된다.

Character_Bace.h

class UNREAL_3TH_API ACharacter_Base : public ACharacter
{
public:
	// 에디터상에 공개
	// EditAnywhere 가 없으면 언리얼에서 디테일창이 뜨지 않는다
	UPROPERTY(EditAnywhere, BlueprintReadWrithe, Category = "Component")
	UCameraComponent*						m_Cam;

	UPROPERTY(EditAnywhere, BlueprintReadWrithe, Category = "Component")
	USpringArmComponent*					m_Arm;

private:
	UPROPERTY(EditAnywhere, Category = "Input")
	TSoftObjectPtr<UInputMappingContext>	InputMapping;

	UPROPERTY(EditAnywhere, Category = "Input")
	TSoftObjectPtr<UInputAction>			MoveAction;

	UPROPERTY(EditAnywhere, Category = "Input")
	TSoftObjectPtr<UInputAction>			RotationAction;
}

TSoftObjectPtr ==> 

Character_Base.cpp

ACharacter_Base::ACharacter_Base()
	: m_Cam(nullptr)
	, m_Arm(nullptr)
{
	// 생성자 시점에서는 아직 값이 안채워져 있다
	InputMapping;
	MoveAction;
	RotationAction;
}

//#include "EnhancedInputSubsystems.h"
//#include "EnhancedInputComponent.h"

// 객체가 만들어지고 나서 레벨이 시작되는 순간 
// InputMapping, MoveAction, RotationAction 가 생성이 이미 셋팅이 된 이후
void ACharacter_Base::BeginPlay()
{
	// 부모클래스의 BeginPlay 에서 할 일은 부모가 해야되니까
	// 블루프린트의 부모인 character 의 BeginPlay 를 실행하게 한다.
	Super::BeginPlay();
	
	// APlayerController -> 폰을 상속받은 캐릭터에게 빙의하는 컨트롤러
	// Cast -> 다운 캐스팅 (c++의 다이나믹 케스트랑 비슷)
	// GetController -> pawn 에서 제공하는 함수
	// 현재 컨트롤러를 가져와서 누가 빙의했는지 알려주는 함수 ( player 인지 AI 인지 모르니까 )
	// 현재 컨트롤러를 가져와서 APlayerController 로 다운캐스팅하여 
	// APlayerController 인지 판단하는데, nullptr 이 나오면 AI 이다.
	APlayerController* pController = Cast<APlayerController>(GetController());

	if (pController)
	{
		// 우리가 실행하고 있는 프로젝트를 서버적으로 바라봤을 때의 플레이어
		ULocalPlayer* pLocalPlayer = pController->GetLocalPlayer();
		if (pLocalPlayer)
		{
			UEnhancedInputLocalPlayerSubsystem* pSubsystem = pLocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
			pSubsystem->AddMappingContext(InputMapping.LoadSynchronous(), 0);
		}
	}
}

2.

Character_Bace.h

class UNREAL_3TH_API ACharacter_Base : public ACharacter
{
private:
	void Move(const FInputActionInstance& _Instance);
	void Rotation(const FInputActionInstance& _Instance);
}
// 컨트롤러가 빙의되는 순간에 입력관련된 처리를 어떻게 할 것인지 actor 에게 호출해주는 함수
// 플레이어 == 컨트롤러
void ACharacter_Base::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	// 입력 액션
	// 다운캐스팅해서 실제 클래스타입으로 바꾼다
	UEnhancedInputComponent* InputCom = Cast<UEnhancedInputComponent>(PlayerInputComponent);
	
	if (nullptr == InputCom)
		return;

	InputCom->BindAction(MoveAction.LoadSynchronous(), ETriggerEvent::Triggered, this, &ACharacter_Base::Move);
	InputCom->BindAction(RotationAction.LoadSynchronous(), ETriggerEvent::Triggered, this, &ACharacter_Base::Rotation);
	
    
}

void ACharacter_Base::Move(const FInputActionInstance& _Instance)
{
	// Log-> 하얀색
	// Warning -> 노란색
	// Error -> 빨간색
   	// 잘 호출되고 있는지 로그를 찍어본다
	//UE_LOG(LogTemp, Warning, TEXT("Call Move Function!!!"));

	// FInputActionInstance 를 통해서
	FVector2D vInput = _Instance.GetValue().Get<FVector2D>();

	// 전방방향 벡터 구해와서 곱한다
	if(vInput.X != 0.f)
		GetCharacterMovement()->AddInputVector(GetActorForwardVector() * vInput.X);
	
	// 우측방향 벡터 구해와서 곱한다
	if (vInput.Y != 0.f)
		GetCharacterMovement()->AddInputVector(GetActorRightVector() * vInput.Y);
}

void ACharacter_Base::Rotation(const FInputActionInstance& _Instance)
{
	FVector2D vInput = _Instance.GetValue().Get<FVector2D>();

	// roll = x 축 회전
	// Pitch = 본인의 우측 방향 y 축 회전 
	// Yaw = z축을 이용해서 

	// 마우스의 가로 움직임
	AddControllerYawInput(vInput.X);

	// 마우스의 세로 움직임
	AddControllerPitchInput(vInput.Y);
}

 

* Roll, Pitch, Yaw

https://wise-eun.tistory.com/entry/Unreal-Yaw-Pitch-Roll-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0

 

[Unreal] Yaw, Pitch, Roll 이해하기 쉽게 알아보기

언리얼 강의를 보게 되면서 코드를 작성하였는데 보이는 의문의 단어들 회전관련되어서 그냥 지나치면서는 언뜻 본것같긴해도 이렇게 얘네 3개가 총 출동하는건 개발하면서 본적이 없어서 따

wise-eun.tistory.com