폰에 스프링 암과 카메라를 어태치하면 월드에서 카메라가 폰을 따라다니는 방식을 조정할 수 있습니다.
1. 카메라를 폰에 어태치합니다.
-
언리얼 엔진 을 실행하고 새 기본 템플릿을 생성하는 것으로 시작합니다.
-
새 게임 프로젝트를 선택합니다. 프로젝트 세팅 창에서 디폴트 코드를 블루프린트에서 C++로 변경하고 시작용 콘텐츠를 포함합니다. 언리얼 프로젝트를 저장할 디렉터리를 선택한 후 프로젝트 이름을 HowTo_PlayerCamera로 지정하고 프로젝트 생성(Create)을 클릭합니다.
-
소스 패널에서 C++ 클래스(C++ Class) 폴더로 이동합니다. 게임 모드 베이스 클래스를 포함하는 단일 클래스가 보일 것입니다. 인접한 회색 공간을 우클릭한 후 드롭다운 창에서 새 C++ 클래스(New C++ class) 를 선택합니다.
-
폰(Pawn)을 부모 클래스로 선택하고 다음(Next)을 클릭합니다. 새 폰의 이름을 PawnWithCamera로 지정합니다.
-
Visual Studio에서
PawnWithCamera.h
파일로 이동하여 클래스 정의의 protected 네임스페이스에 다음 코드를 추가합니다.protected: UPROPERTY(EditAnywhere) class USpringArmComponent* SpringArmComp; UPROPERTY(EditAnywhere) class UCameraComponent* CameraComp; UPROPERTY(EditAnywhere) UStaticMeshComponent* StaticMeshComp;
이러한 변수는 끝에 CameraComponent 가 어태치된 SpringArmComponent 를 생성하는 데 사용됩니다. 스프링 암은 카메라를 비롯한 다른 컴포넌트를 어태치할 수 있는 간단한 방법으로, 관측하는 오브젝트와 함께 움직이므로 유연하고 부드러운 모션이 구현됩니다.
-
PawnWithCamera.cpp
로 이동하여 다음을 선언합니다.#include "GameFramework/SpringArmComponent.h" #include "Camera/Component.h"
-
이제 스태틱 메시, 스프링 암, 카메라 컴포넌트를 생성할 차례이므로, PawnWithCamera.cpp의 클래스 생성자 APawnWithCamera::APawnWithCamera: 내부에 다음 코드를 선언해야 합니다. 다음 코드를 추가하세요.
//컴포넌트 생성 RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootSceneComponent")); StaticMeshComp = CreateDefaultSubobject <UStaticMeshComponent>(TEXT("StaticMeshComponent")); SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComponent")); CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent")); //컴포넌트 어태치 StaticMeshComp->SetupAttachment(RootComponent); SpringArmComp->SetupAttachment(StaticMeshComp); CameraComp->SetupAttachment(SpringArmComp,USpringArmComponent::SocketName); //SpringArm 클래스 변수 할당 SpringArmComp->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f)); SpringArmComp->TargetArmLength = 400.f; SpringArmComp->bEnableCameraLag = true; SpringArmComp->CameraLagSpeed = 3.0f;
이를 통해 씬 컴포넌트를 컴포넌트 목록 계층구조의 루트로 만들고 스태틱 메시 컴포넌트를 생성하여 어태치합니다. 그런 다음에는 스프링 암 컴포넌트와 카메라 컴포넌트가 생성되며, 카메라 컴포넌트가 스프링 암 컴포넌트의 소켓 길이 끝에 어태치됩니다. 스프링 암의 디폴트 피치는 -60도로, 위치는 루트 위 50유닛으로 설정되어 있습니다. 또한 스프링 암의 길이와 모션의 스무드니스를 결정하는 SpringArmComponent 클래스별 값 몇 가지도 설정합니다.
-
마지막으로, 디폴트 플레이어 컨트롤러가 생성자에서 폰에 빙의하도록 설정하겠습니다.
//디폴트 플레이어를 제어 AutoPossessPlayer = EAutoReceiveInput::Player0;
이제 카메라를 편히 제어할 수 있는 단순한 폰이 생겼습니다. 다음으로는 언리얼 엔진 에디터에서 입력을 환경설정하고 이에 반응하는 코드를 생성하겠습니다.
작업 중인 코드
PawnWithCamera.h
// Copyright 1998-2021 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "PawnWithCamera.generated.h"
UCLASS()
class HOWTO_PLAYERCAMERA_API APawnWithCamera : public APawn
{
GENERATED_BODY()
public:
// 이 폰 프로퍼티의 디폴트값 설정
APawnWithCamera();
protected:
// 게임 시작 또는 스폰 시 호출
virtual void BeginPlay() override;
public:
// 프레임마다 호출
virtual void Tick( float DeltaSeconds ) override;
//함수 기능을 입력에 바인딩하기 위해 호출
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
protected:
UPROPERTY(EditAnywhere)
class USpringArmComponent* SpringArmComp;
UPROPERTY(EditAnywhere)
class UCameraComponent* CameraComp;
UPROPERTY(EditAnywhere)
UStaticMeshComponent* StaticMeshComp;
};
PawnWithCamera.cpp
// Copyright 1998-2021 Epic Games, Inc. All Rights Reserved.
#include "HowTo_PlayerCamera.h"
#include "PawnWithCamera.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
// 디폴트값 설정
APawnWithCamera::APawnWithCamera()
{
// 이 폰이 프레임마다 Tick()을 호출하도록 설정합니다. 이 설정이 필요 없는 경우 비활성화하면 퍼포먼스가 향상됩니다.
PrimaryActorTick.bCanEverTick = true;
//컴포넌트 생성
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootSceneComponent"));
StaticMeshComp = CreateDefaultSubobject <UStaticMeshComponent>(TEXT("StaticMeshComponent"));
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComponent"));
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
//컴포넌트 어태치
StaticMeshComp->SetupAttachment(RootComponent);
SpringArmComp->SetupAttachment(StaticMeshComp);
CameraComp->SetupAttachment(SpringArmComp,USpringArmComponent::SocketName);
//SpringArm 클래스 변수 할당
SpringArmComp->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
SpringArmComp->TargetArmLength = 400.f;
SpringArmComp->bEnableCameraLag = true;
SpringArmComp->CameraLagSpeed = 3.0f;
//디폴트 플레이어를 제어
AutoPossessPlayer = EAutoReceiveInput::Player0;
}
// 게임 시작 또는 스폰 시 호출
void APawnWithCamera::BeginPlay()
{
Super::BeginPlay();
}
// 프레임마다 호출
void APawnWithCamera::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
}
//함수 기능을 입력에 바인딩하기 위해 호출
void APawnWithCamera::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
Super::SetupPlayerInputComponent(InputComponent);
}
2. 입력이 카메라를 제어하도록 환경설정
카메라와 폰을 제어하는 입력 키를 결정한 다음, 그에 따라 입력을 구성해야 합니다. 이 프로젝트 샘플에서는 오른쪽 마우스 버튼을 길게 누르고 있으면 따라가기 거리가 짧아지고 필드 오브 뷰가 줌 인될 수 있도록 하겠습니다.
-
또한, 마우스로 시야 각도를, WASD 키로 폰 이동을 제어하겠습니다. 이렇게 하려면 언리얼 엔진 에디터의 편집(Edit) 드롭다운 메뉴에서 프로젝트 세팅(Project Settings) 을 열어야 합니다.
-
프로젝트 세팅 창에서 엔진(Engine) > 입력(Input) > 바인딩(Bindings) 으로 이동합니다. 여기에서 액션 및 축 매핑 옆의 + 기호를 클릭하여 새 키 맵을 추가합니다.
-
입력 로직을 제어할 때 사용되는 다음 입력 액션 매핑(Action Mapping) 및 축 매핑(Axis Mapping) 을 정의해야 합니다.
액션 매핑: ZoomIn 오른쪽 마우스 버튼 축 매핑: MoveForward W 1.0 S -1.0 MoveRight A -1.0 D 1.0 CameraPitch 마우스 Y 1.0 CameraYaw 마우스 X 1.0 입력 매핑의 작동 원리를 더 이해하고 싶다면 플레이어 입력 및 폰 튜토리얼을 참조하세요.
입력을 정의했으니 Visual Studio 로 돌아가서 다음 단계를 계속 진행합니다.
3. 입력에 반응하도록 C++ 코드 작성
수신할 입력 데이터를 보관하는 멤버 변수를 구성하여 입력 매핑을 활용할 때 필요한 코드를 작성해 보겠습니다. 이벤트 틱 업데이트 중에는 이동 및 마우스 방향 축의 값(각각 2차원 벡터)을 알아야 합니다.
또한 줌 인/줌 아웃된 카메라 뷰 쪽으로 이동하는지 여부와 현재 카메라 스테이트 2개 간의 거리도 파악해야 합니다.
-
먼저, PawnWithCamera.h의 클래스 정의의 protected 네임스페이스에 다음 코드를 추가합니다.
//입력 변수 FVector2D MovementInput; FVector2D CameraInput; float ZoomFactor; bool bZoomingIn;
-
입력을 트래킹하는 함수를 생성해야 합니다. 먼저 PawnWithCamera.h의 클래스 정의에 다음을 추가합니다.
//입력 함수 void MoveForward(float AxisValue); void MoveRight(float AxisValue); void PitchCamera(float AxisValue); void YawCamera(float AxisValue); void ZoomIn(); void ZoomOut();
-
이제 PawnWithCamera.Cpp 파일로 이동하고 다음 코드를 추가하여 함수 로직 구현을 시작합니다.
//입력 함수 void APawnWithCamera::MoveForward(float AxisValue) { MovementInput.X = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f); } void APawnWithCamera::MoveRight(float AxisValue) { MovementInput.Y = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f); } void APawnWithCamera::PitchCamera(float AxisValue) { CameraInput.Y = AxisValue; } void APawnWithCamera::YawCamera(float AxisValue) { CameraInput.X = AxisValue; } void APawnWithCamera::ZoomIn() { bZoomingIn = true; } void APawnWithCamera::ZoomOut() { bZoomingIn = false; }
아직 ZoomFactor에는 아무런 작업도 하지 않았습니다. 해당 변수는 그 값이 버튼 스테이트를 기반으로 시간에 따라 지속적으로 변경되기 때문에, 폰 의 Tick 함수 도중에 업데이트됩니다.
-
이제 입력 데이터를 보관하는 데 필요한 코드를 생성했습니다. 다음 단계는 엔진에 코드 호출 시점을 알리는 것입니다. 폰의 입력 이벤트에 함수를 바인딩하려면 다음과 같이 APawnWithCamera::SetupPlayerInputComponent 메서드에 코드를 추가하면 됩니다.
//'ZoomIn'에 대한 이벤트 연결 InputComponent->BindAction("ZoomIn", IE_Pressed, this, &APawnWithCamera::ZoomIn); InputComponent->BindAction("ZoomIn", IE_Released, this, &APawnWithCamera::ZoomOut); //4개 축에 대해 모든 프레임 핸들링 연결 InputComponent->BindAxis("MoveForward", this, &APawnWithCamera::MoveForward); InputComponent->BindAxis("MoveRight", this, &APawnWithCamera::MoveRight); InputComponent->BindAxis("CameraPitch", this, &APawnWithCamera::PitchCamera); InputComponent->BindAxis("CameraYaw", this, &APawnWithCamera::YawCamera);
-
최종적으로, Tick 함수의 해당 값을 사용하여 프레임마다 폰 및 카메라 를 업데이트할 수 있습니다. 다음 코드 블록 전체는 PawnWithCamera.cpp의 APawnWithCamera::Tick 에 추가해야 합니다.
//ZoomIn 버튼이 눌려 있으면 줌 인하고, 눌려 있지 않으면 다시 줌 아웃 { if (bZoomingIn) { ZoomFactor += DeltaTime / 0.5f; //0.5초에 걸쳐 줌 인 } else { ZoomFactor -= DeltaTime / 0.25f; //0.5초에 걸쳐 줌 아웃 } ZoomFactor = FMath::Clamp<float>(ZoomFactor, 0.0f, 1.0f); //ZoomFactor를 기반으로 카메라의 FOV 및 SpringArm의 길이를 블렌딩 CameraComp->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, ZoomFactor); SpringArmComp->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor); }
이 코드는 줌 시간 0.5초 및 0.25초, 줌 아웃 필드 오브 뷰 90도 및 줌 인 필드 오브 뷰 60도, 줌 아웃 카메라 거리 400 및 줌 인 카메라 거리 300 등 다수의 하드코딩된 값을 사용합니다. 이러한 값은 일반적으로 UPROPERTY(EditAnywhere) 가 태그된 변수로 에디터에 노출됩니다. 이 예시에서는 UPROPERTY(EditAnywhere) 지정자 태그를 사용하여 해당 값을 변경할 수 있는 비프로그래머에게 추가 기능을 제공했습니다. 또한 프로그래머도 코드를 컴파일하지 않고 값을 변경하거나 에디터에서 게임을 플레이하는 도중 동적으로 값을 변경할 수 있습니다.
//액터의 요를 회전하며, 이에 따라 어태치된 카메라가 회전 { FRotator NewRotation = GetActorRotation(); NewRotation.Yaw += CameraInput.X; SetActorRotation(NewRotation); } //카메라의 피치를 회전하되 항상 아래를 보도록 제한 { FRotator NewRotation = OurCameraSpringArm->GetComponentRotation(); NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f); SpringArmComp->SetWorldRotation(NewRotation); }
이 코드 블록은 폰의 요를 마우스의 X축으로 직접 회전하지만, 마우스의 Y축으로 인한 피치 변화에 반응하는 것은 카메라 시스템뿐입니다. 액터 또는 액터 하위 클래스를 회전하면 실제로 루트 레벨 컴포넌트 가 회전하여 해당 컴포넌트에 어태치된 모든 것에 간접적으로 영향을 줍니다.
//'MoveX' 및 'MoveY' 축을 기반으로 이동 처리 { if (!MovementInput.IsZero()) { //초당 100유닛만큼 이동 입력 축 값을 스케일 조절 MovementInput = MovementInput.SafeNormal() * 100.0f; FVector NewLocation = GetActorLocation(); NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime; NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime; SetActorLocation(NewLocation); } }
GetActorForwardVector 및 GetActorRightVector 를 사용하여 액터가 향하는 방향에 상대적으로 이동할 수 있습니다. 카메라가 액터와 같은 방향으로 향하기 때문에, 전방 키가 항상 플레이어의 시야 기준으로 앞을 향하게 됩니다.
-
축하합니다! 코딩을 완료하셨습니다. 이제 언리얼 엔진 에디터 내에서 코드를 컴파일하고 콘텐츠 브라우저 에서 새 클래스의 인스턴스를 레벨 에디터 창으로 드래그하면 됩니다.
스태틱 메시 또는 다른 비주얼 컴포넌트를 추가해도 좋고, 추가하지 않은 상태로 플레이해도 좋습니다. 레벨에서 카메라가 따라올 때 카메라 이동 가속 및 감속은 부드럽지만 회전은 딱딱하고 즉각적으로 느껴질 것입니다. '카메라 회전 지연(Camera Rotation Lag)'을 추가하거나 카메라 지연을 증가 또는 감소시키는 등 SpringArmComponent 의 프로퍼티 일부를 변경하여 제어하는 느낌에 어떤 영향을 미치는지 확인해 보세요.
-
완성된 코드 기능은 아래와 같은 모습이어야 합니다.
카메라 피치/요
*앞으로 이동/오른쪽으로 이동**
줌 인/줌 아웃
완성된 코드
PawnWithCamera.h
// Copyright 1998-2021 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "PawnWithCamera.generated.h"
UCLASS()
class HOWTO_PLAYERCAMERA_API APawnWithCamera : public APawn
{
GENERATED_BODY()
public:
// 이 폰 프로퍼티의 디폴트값 설정
APawnWithCamera();
protected:
// 게임 시작 또는 스폰 시 호출
virtual void BeginPlay() override;
public:
// 프레임마다 호출
virtual void Tick( float DeltaSeconds ) override;
//함수 기능을 입력에 바인딩하기 위해 호출
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
protected:
UPROPERTY(EditAnywhere)
class USpringArmComponent* OurCameraSpringArm;
UPROPERTY(EditAnywhere)
class UCameraComponent* OurCamera;
UPROPERTY(EditAnywhere)
UStaticMeshComponent* StaticMeshComp;
//입력 변수
FVector2D MovementInput;
FVector2D CameraInput;
float ZoomFactor;
bool bZoomingIn;
//입력 함수
void MoveForward(float AxisValue);
void MoveRight(float AxisValue);
void PitchCamera(float AxisValue);
void YawCamera(float AxisValue);
void ZoomIn();
void ZoomOut();
};
PawnWithCamera.cpp
// Copyright 1998-2021 Epic Games, Inc. All Rights Reserved.
#include "PawnWithCamera.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
// 디폴트값 설정
APawnWithCamera::APawnWithCamera()
{
// 이 폰이 프레임마다 Tick()을 호출하도록 설정합니다. 이 설정이 필요 없는 경우 비활성화하면 퍼포먼스가 향상됩니다.
PrimaryActorTick.bCanEverTick = true;
//컴포넌트 생성
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
StaticMeshComp = CreateDefaultSubobject <UStaticMeshComponent>(TEXT("StaticMeshComponent"));
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComponent"));
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
StaticMeshComp->SetupAttachment(RootComponent);
SpringArmComp->SetupAttachment(StaticMeshComp);
CameraComp->SetupAttachment(SpringArmComp,USpringArmComponent::SocketName);
//SpringArmComponent의 디폴트 변수 및 비헤이비어 설정
SpringArmComp->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
SpringArmComp->TargetArmLength = 400.f;
SpringArmComp->bEnableCameraLag = true;
SpringArmComp->CameraLagSpeed = 3.0f;
//디폴트 플레이어를 제어
AutoPossessPlayer = EAutoReceiveInput::Player0;
}
// 게임 시작 또는 스폰 시 호출
void APawnWithCamera::BeginPlay()
{
Super::BeginPlay();
}
// 프레임마다 호출
void APawnWithCamera::Tick( float DeltaTime )
{
Super::Tick(DeltaTime);
//ZoomIn 버튼이 눌려 있으면 줌 인하고, 눌려 있지 않으면 다시 줌 아웃
{
if (bZoomingIn)
{
ZoomFactor += DeltaTime / 0.5f; //0.5초에 걸쳐 줌 인
}
else
{
ZoomFactor -= DeltaTime / 0.25f; //0.5초에 걸쳐 줌 아웃
}
ZoomFactor = FMath::Clamp<float>(ZoomFactor, 0.0f, 1.0f);
//ZoomFactor를 기반으로 카메라의 FOV 및 SpringArm의 길이를 블렌딩
CameraComp->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, ZoomFactor);
SpringArmComp->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor);
}
//액터의 요를 회전하며, 이에 따라 어태치된 카메라가 회전
{
FRotator NewRotation = GetActorRotation();
NewRotation.Yaw += CameraInput.X;
SetActorRotation(NewRotation);
}
//카메라의 피치를 회전하되 항상 아래를 보도록 제한
{
FRotator NewRotation = SpringArmComp->GetComponentRotation();
NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f);
SpringArmComp->SetWorldRotation(NewRotation);
}
//'MoveX' 및 'MoveY' 축을 기반으로 이동 처리
{
if (!MovementInput.IsZero())
{
//초당 100유닛만큼 이동 입력 축 값을 스케일 조절
MovementInput = MovementInput.SafeNormal() * 100.0f;
FVector NewLocation = GetActorLocation();
NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime;
NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime;
SetActorLocation(NewLocation);
}
}
}
//함수 기능을 입력에 바인딩하기 위해 호출
void APawnWithCamera::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
Super::SetupPlayerInputComponent(InputComponent);
//'ZoomIn'에 대한 이벤트 연결
InputComponent->BindAction("ZoomIn", IE_Pressed, this, &APawnWithCamera::ZoomIn);
InputComponent->BindAction("ZoomIn", IE_Released, this, &APawnWithCamera::ZoomOut);
//4개 축에 대해 모든 프레임 핸들링 연결
InputComponent->BindAxis("MoveForward", this, &APawnWithCamera::MoveForward);
InputComponent->BindAxis("MoveRight", this, &APawnWithCamera::MoveRight);
InputComponent->BindAxis("CameraPitch", this, &APawnWithCamera::PitchCamera);
InputComponent->BindAxis("CameraYaw", this, &APawnWithCamera::YawCamera);
}
//입력 함수
void APawnWithCamera::MoveForward(float AxisValue)
{
MovementInput.X = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}
void APawnWithCamera::MoveRight(float AxisValue)
{
MovementInput.Y = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}
void APawnWithCamera::PitchCamera(float AxisValue)
{
CameraInput.Y = AxisValue;
}
void APawnWithCamera::YawCamera(float AxisValue)
{
CameraInput.X = AxisValue;
}
void APawnWithCamera::ZoomIn()
{
bZoomingIn = true;
}
void APawnWithCamera::ZoomOut()
{
bZoomingIn = false;
}
4. 직접 해보기!
여기에서 배운 것을 활용하여 다음과 같은 작업을 해보세요.
- 길게 누르면 폰의 이동 속도가 높아지는 달리기 키를 플레이어에게 부여합니다.
- 자동 및 입력 기반 카메라 이동을 다양한 방식으로 결합하여 실험합니다. 게임 개발의 매우 심도 있는 영역으로, 탐색할 여지가 풍부합니다!
- 스프링 컴포넌트 에서 지연을 증가시키거나, 감소시키거나, 제거하여 지연이 카메라의 전반적 느낌에 어떤 영향을 주는지 더 자세히 알아봅니다.
- 약간의 주기적 모션(살짝 랜덤화하거나 커브 에셋 사용도 가능)을 구현하여 카메라에 핸드헬드 느낌을 줍니다.
- 카메라 에 일정량의 자동 회전을 적용하여, 카메라가 이동 중인 플레이어 오브젝트 뒤로 점진적으로 자리 잡고 플레이어가 이동하는 방향을 향하도록 합니다.
이 튜토리얼에서 배운 내용을 더 알아보세요.