[이득우의 언리얼 C++ 게임 개발의 정석] Chapter 6. 캐릭터의 제작과 컨트롤

수민·2023년 3월 15일
0
post-thumbnail

이득우의 언리얼 C++ 게임 개발의 정석을 읽고 개인 공부 목적으로 요약 정리한 글입니다!


👀 캐릭터 모델

캐릭터 (Character)

: 인간형 폰을 효과적으로 제작하기 위한 모델

ACharacter 클래스는 APawn 클래스를 상속받는다
-Capsule, SkeletalMesh 컴포넌트 사용
CharacterMovement 컴포넌트를 사용하여 움직임 관리

Character vs Pawn

CharacterMovement Component를 제공한다

CharacterMovement 가 FloatingPawnMovement보다 나은 점

  • 중력을 반영한 움직임 (ex. 점프) 제공
  • 다양한 움직임 (기어가기, 날아가기 등) 설정 가능
  • 멀티 플레이 네트워크 환경에서 캐릭터들의 움직임을 자동으로 동기화

플레이어 컨트롤러 vs 폰

그냥 플레이어 컨트롤러 : 알빠임? 내가 하고 싶은 대로 할거임
폰 : ㅠㅠ제발.... 나는 물리적으로그렇게못한다니까?ㅠㅠ


👀 컨트롤 회전의 활용

플레이어 컨트롤러는 컨트롤 회전을 제공한다

입력에 따라 캐릭터가 Yaw, Pitch, Roll 회전

Raw : 도리도리
Pitch : 끄덕끄덕
Roll : 갸우뚱갸우뚱

참고 링크

아 진짜 DX하다가 축 바뀌니까 죽겠따.. 너무 헷갈린다 ㅠㅠ


👀 GTA vs DIABLO 컨트롤 구현

GTA

== 언리얼 3인칭 템플릿!!

설정

분류내용
캐릭터의 이동현재 보는 시점 기준 상하, 좌우 방향으로 이동, 카메라는 회전X
캐릭터의 회전캐릭터가 이동하는 방향으로 회전
카메라 지지대 길이 450cm
카메라 회전마우스 상하좌우 이동에 따라 카메라 지지대가 상하좌우로 회전
카메라줌장애물 감지되면 캐릭터가 보이도록 카메라를 장애물 앞으로 줌인

DIABLO

고정된 3인칭 시점에서 캐릭터를 따라다닌다.

설정

분류내용
캐릭터의 이동상하좌우 키를 조합해 이동할 방향 결정
캐릭터의 회전입력한 방향으로 회전
카메라 지지대 길이 조금 멀리 떨어진 800cm
카메라 회전회전없이 항상 고정. 45도로 내려다봄
카메라줌X 장애물이 있을 경우 외곽선 처리

구현

AMyCharacter 클래스에서 ControlMode 를 변경하여 조작할 수 있도록 함

enum class EControlMode {
		GTA,
		DIABLO
	};

열거형 선언 시 enum class로 열거형을 선언하자.
enum은 int로 변환될 수 있기 때문에 다양한 문제를 초래한다!!
자세한건 나중에 포스팅하겟슴!

그리구
UPROPERTY를 사용하지 않는 값 타입 변수들은 반드시 초기화해주자!~!!!!

AMyCharacter::AMyCharacter()
{
    ...
	SetControlMode(EControlMode::GTA);

	ArmLengthSpeed = 3.0f;
	ArmRotationSpeed = 10.0f;
}

void AMyCharacter::SetControlMode(EControlMode NewControlMode)
{
	CurrentControlMode = NewControlMode;

	switch (CurrentControlMode) {
	case EControlMode::GTA:
	{
		// 관련 설정
	}
	case EControlMode::DIABLO:
	{
		// 관련 설정
	}
	}
}

void AMyCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	// 조작모드 변경 시 부드럽게 카메라가 움직일 수 있도록 SpringArm을 선형보간! 
	SpringArm->TargetArmLength = FMath::FInterpTo(SpringArm->TargetArmLength, ArmLengthTo, DeltaTime, ArmLengthSpeed);

	switch (CurrentControlMode) {
	case EControlMode::DIABLO:
	{
		SpringArm->SetRelativeRotation(FMath::RInterpTo(SpringArm->GetRelativeRotation(), ArmRotationTo, DeltaTime, ArmRotationSpeed)); 
		break;
	}
	}

	switch (CurrentControlMode) {
	case EControlMode::DIABLO:
	{
    	// 디아블로 방식에서는 DirectToMove (방향벡터)를 통해 회전행렬을 구해서 냅다 넣는다.
		if (DirectionToMove.SizeSquared() > 0.0f) {
			GetController()->SetControlRotation(FRotationMatrix::MakeFromX(DirectionToMove).Rotator());
			AddMovementInput(DirectionToMove);
		}
		break;
	}
	}
}

// 시점변환
void AMyCharacter::ViewChange()
{
	switch (CurrentControlMode) {
	case EControlMode::GTA:
		GetController()->SetControlRotation(GetActorRotation());
		SetControlMode(EControlMode::DIABLO);
		break;
	case EControlMode::DIABLO:
		GetController()->SetControlRotation(SpringArm->GetRelativeRotation());
		SetControlMode(EControlMode::GTA);
		break;
	}
}

void AMyCharacter::MoveForward(float AxisValue)
{
	// 그냥 앞뒤로 움직이기
	// AddMovementInput(GetActorForwardVector(), AxisValue);


	switch (CurrentControlMode) {
	case EControlMode::GTA:
	{
		// 카메라가 바라보는 방향대로 움직이기
		AddMovementInput(FRotationMatrix(GetControlRotation()).GetUnitAxis(EAxis::X), AxisValue);
		break;
	}
	case EControlMode::DIABLO:
	{
    	// 방향벡터 만들기 (실제로 움직이는 명령은 Tick에서 해줌)
		DirectionToMove.X = AxisValue;
		break;
	}
	}
}

void AMyCharacter::MoveRight(float AxisValue)
{
	// 그냥 앞뒤로 움직이기
	// AddMovementInput(GetActorRightVector(), AxisValue);

	switch (CurrentControlMode) {
	case EControlMode::GTA:
	{
		// 카메라가 바라보는 방향대로 움직이기
		AddMovementInput(FRotationMatrix(GetControlRotation()).GetUnitAxis(EAxis::Y), AxisValue);
		break;
	}
	case EControlMode::DIABLO:
	{
    // 방향벡터 만들기 (실제로 움직이는 명령은 Tick에서 해줌)
		DirectionToMove.Y = AxisValue;
		break;
	}
	}
}

👀 그리고,,

FRotationMatrix : 회전된 좌표계 정보를 저장하는 행렬
MakeFromX,Y,Z : 하나의 벡터로부터 회전 행렬을 구축하는 명령어

FMath::InterpTo
선형보간해주는 명렁어
(지정한 속력으로 목표 지점까지 진행하되, 목표 지점까지 도달하면 그 자리에서 멈춤)
float : FInterpTo
Vector : VInterpTo
Rotator : RInterpTo

profile
우하하

0개의 댓글