언리얼 엔진4 - Spring Arm

박동현·2021년 3월 25일
0

언리얼 공부

목록 보기
1/1

이득우의 언리얼 C++ 책을 보며 공부하던 중 6장 캐릭터의 제작과 컨트롤 부분에서 Spring Arm 컴포넌트를 사용하여 디아블로와 GTA의 카메라를 구현하는 부분이 있는데 이 부분을 정리해봤다.

언리얼 엔진 스프링 암 컴포넌트 사용법 공식 문서 https://docs.unrealengine.com/ko/InteractiveExperiences/UsingCameras/SpringArmComponents/index.html

1. 기본 사용법

클래스에 추가

UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
	GENERATED_BODY()
    
    	...
       
	UPROPERTY(VisibleAnywhere, Category = Camera)
	USpringArmComponent* SpringArm;

	UPROPERTY(VisibleAnywhere, Category = Camera)
	UCameraComponent* Camera;
    
private:
	void UpDown(float NewAxisValue);
	void LeftRight(float NewAxisValue);
};

AABCharacter::AABCharacter //생성자
{
	SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SPRINGARM"));
	Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("CAMERA"));
	SpringArm->TargetArmLength = 400.0f;
	SpringArm->SetRelativeRotation(FRotator(-15.0f, 0.0f, 0.0f));
}

2. GTA(3인칭 카메라)

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

	switch (CurrentControlMode)
	{
	case EControlMode::GTA:
		SpringArm->TargetArmLength = 450.0f;
		SpringArm->SetRelativeRotation(FRotator::ZeroRotator);
		SpringArm->bUsePawnControlRotation = true;
		SpringArm->bInheritPitch = true;
		SpringArm->bInheritRoll = true;
		SpringArm->bInheritYaw = true;
		SpringArm->bDoCollisionTest = true;
		bUseControllerRotationYaw = false;
        }
        ...

위의 코드만으로는 캐릭터와 카메라의 방향이 같지 않기 때문에 우리가 원하는대로 구현되지 않는다. 캐릭터의 방향을 변경하기 위해 컨트롤의 회전 값으로부터 회전 행렬을 생성한 후, 원하는 방향 축을 대입해서 캐릭터가 움직일 방향 값을 가져올수 있다.

void AABCharacter::UpDown(float NewAxisValue)
{
	AddMovementInput(FRotationMatrix(FRotator(0.0f, 
    GetControlRotation().Yaw, 0.0f)).GetUnitAxis(EAxis::X), NewAxisValue);
}

void AABCharacter::LeftRight(float NewAxisValue)
{
	AddMovementInput(FRotationMatrix(FRotator(0.0f, 
    GetControlRotation().Yaw, 0.0f)).GetUnitAxis(EAxis::Y), NewAxisValue);
}

이 후 캐릭터가 움직이는 방향으로 캐릭터를 자동으로 회전시켜주는 Movement 컴포넌트의 OrientRotationToMovement 기능을 통해 이동 방향을 향해 캐릭터가 부드럽게 회전할 수 있도록 한다.

void AABCharacter::SetControlMode(EControlMode NewControlMode)
{
	...
    
	GetCharacterMovement()->bOrientRotationToMovement = true;
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 720.0f, 0.0f);
}

3. 디아블로(고정 쿼터뷰 카메라)

void AABCharacter::SetControlMode(EControlMode NewControlMode)
{
	switch (CurrentControlMode)
	{
	case EControlMode::DIABLO:
		SpringArm->TargetArmLength = 800.0f;
		SpringArm->SetRelativeRotation(FRotator(-45.0f, 0.0f, 0.0f));
		SpringArm->bUsePawnControlRotation = false;
		SpringArm->bInheritPitch = false;
		SpringArm->bInheritRoll = false;
		SpringArm->bInheritYaw = false;
		SpringArm->bDoCollisionTest = false;
		bUseControllerRotationYaw = true;
		break;
	}
}

void AABCharacter::UpDown(float NewAxisValue)
{
	switch (CurrentControlMode)
	{
	case EControlMode::GTA:
		AddMovementInput(FRotationMatrix(FRotator(0.0f,
        	GetControlRotation().Yaw, 0.0f)).GetUnitAxis(EAxis::X), NewAxisValue);
		break;
	case EControlMode::DIABLO:
		DirectionToMove.X = NewAxisValue;
		break;
	}
}

void AABCharacter::LeftRight(float NewAxisValue)
{
	switch (CurrentControlMode)
	{
	case EControlMode::GTA:
		AddMovementInput(FRotationMatrix(FRotator(0.0f,
        	GetControlRotation().Yaw, 0.0f)).GetUnitAxis(EAxis::Y), NewAxisValue);
		break;
	case EControlMode::DIABLO:
		DirectionToMove.Y = NewAxisValue;
		break;
	}
}

현재는 키보드 조합에 따라 캐릭터가 최소 45도 단위로 끊어져서 회전한다. Movement 컴포넌트의 UseControllerDesiredRotation 속성을 체크하면 컨트롤 회전이 가리키는 방향으로 캐릭터가 부드럽게 회전한다. UseControllerRotationYaw 대신에 사용한다.

추가로 SpringArm의 길이와 회전 값이 목표 값까지 서서히 변경되도록 FMath 클래스에서 제공하는 InterpTo 명령어를 사용한다.

UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
	GENERATED_BODY()
    	...
    
	float ArmLengthTo = 0.0f;
	FRotator ArmRotationTo = FRotator::ZeroRotator;
	float ArmLengthSpeed = 0.0f;
	float ArmRotationSpeed = 0.0f;
};

AABCharacter::AABCharacter()
{
	...
    ArmLengthSpeed = 3.0f;
    ArmRotationSpeed = 10.0f;
}

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

	switch (CurrentControlMode)
	{
	case EControlMode::GTA:
		//SpringArm->TargetArmLength = 450.0f;
		//SpringArm->SetRelativeRotation(FRotator::ZeroRotator);
		ArmLengthTo = 450.0f;
		SpringArm->bUsePawnControlRotation = true;
		SpringArm->bInheritPitch = true;
		SpringArm->bInheritRoll = true;
		SpringArm->bInheritYaw = true;
		SpringArm->bDoCollisionTest = true;
		bUseControllerRotationYaw = false;
		GetCharacterMovement()->bOrientRotationToMovement = true;
		GetCharacterMovement()->bUseControllerDesiredRotation = false;
		GetCharacterMovement()->RotationRate = FRotator(0.0f, 720.0f, 0.0f);
		break;
	case EControlMode::DIABLO:
		//SpringArm->TargetArmLength = 800.0f;
		//SpringArm->SetRelativeRotation(FRotator(-45.0f, 0.0f, 0.0f));
		ArmLengthTo = 800.0f;
		ArmRotationTo = FRotator(-45.0f, 0.0f, 0.0f);
		SpringArm->bUsePawnControlRotation = false;
		SpringArm->bInheritPitch = false;
		SpringArm->bInheritRoll = false;
		SpringArm->bInheritYaw = false;
		SpringArm->bDoCollisionTest = false;
		bUseControllerRotationYaw = false;
		GetCharacterMovement()->bOrientRotationToMovement = false;
		GetCharacterMovement()->bUseControllerDesiredRotation = true;
		GetCharacterMovement()->RotationRate = FRotator(0.0f, 720.0f, 0.0f);
		break;
	}
}

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

	SpringArm->TargetArmLength = FMath::FInterpTo(SpringArm->TargetArmLength,
	ArmLengthTo, DeltaTime, ArmLengthSpeed);

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

GTA방식에서는 SpringArm의 회전에 컨트롤 회전 값을 사용하고, 디아블로 방식에서는 캐릭터의 방향에 사용한다. 컨트롤을 전환할 때 어색해 보이지 않게 이 값을 미리 지정해주는 코드를 추가했다.

void AABCharacter::ViewChange()
{
	switch (CurrentControlMode)
	{
	case EControlMode::GTA:
		GetController()->SetControlRotation(GetActorRotation());
		SetControlMode(EControlMode::DIABLO);
		break;
	case EControlMode::DIABLO:
		GetController()->SetControlRotation(SpringArm->GetRelativeRotation());
		SetControlMode(EControlMode::GTA);
		break;
	}
}
profile
엔진 프로그래머를 목표로합니다.

0개의 댓글