이 튜토리얼에서는 카메라를 활성화하고 활성 카메라를 다른 카메라로 변경하는 방법을 보여줍니다.
1 - 월드에 카메라 배치
언리얼 엔진 을 처음 사용하신다면 프로그래밍 퀵스타트를 먼저 읽어보세요. 이 튜토리얼에서는 여러분이 언리얼 엔진에서의 프로젝트 생성, 프로젝트에 C++ 코드 추가, 코드 컴파일, 액터 에 컴포넌트 추가에 익숙하다고 가정합니다.
-
'HowTo_AutoCamera'라는 이름의 시작용 콘텐츠가 포함된 새 기본 코드 프로젝트를 만드는 것부터 시작하겠습니다. 가장 먼저 해야 할 일은 월드에 두 개의 카메라를 만드는 것입니다. 카메라를 구성하는 방법은 여러 가지가 있으므로 여기서는 가장 일반적인 두 가지 방법을 선택하겠습니다. 첫 번째 카메라의 경우 액터 배치(Place Actors) 패널로 이동하여 모든 클래스(All Classes) 탭을 선택하면 카메라(Camera) 액터를 찾을 수 있습니다. 이 카메라를 레벨 에디터(Level Editor) 로 드래그하고 씬에서 잘 보이는 위치에 놓습니다.
이 작업이 완료되면 레벨 에디터 창에 카메라 액터 가 선택되어 있다면 새로운 카메라 액터 로 볼 수 있는 화면 속 화면(picture-in-picture) 뷰가 표시됩니다.
-
두 번째 카메라의 경우 좀 더 심층적인 제어가 가능한 방법을 사용하겠습니다. 먼저 액터 배치 패널에서 기본(Basic) 탭을 클릭한 다음 큐브(Cube) 를 레벨 에디터 창으로 드래그합니다.
이 단계에서는 거의 모든 액터 클래스를 사용할 수 있습니다. 퀵스타트 튜토리얼에서 생성한 MyActor 클래스로 큐브를 대체할 수도 있습니다.
-
큐브 액터가 배치되면 큐브 에 대한 디테일(Details) 패널에서 + 컴포넌트 추가(Add Component) 버튼을 클릭하여 CameraComponent 를 추가합니다. 이제 해당 CameraComponent 의 위치와 회전을 설정하여 이전에 배치한 CameraActor 와는 다른 씬 뷰를 제공할 수 있습니다.
-
CameraActor 의 세팅과 일치하도록 종횡비 제한(Constrain Aspect Ratio) 을 켜서 CameraComponent 를 커스터마이징해야 합니다. 이렇게 하면 카메라 뷰 간의 전환이 더 부드러워지지만 필수는 아닙니다.
월드 구성이 완료되었으므로 이제 카메라 뷰를 제어할 클래스를 만들 준비가 되었습니다.
2 - C++에서 카메라 뷰 제어하기
-
카메라 뷰를 제어하기 위해 C++ 클래스를 만들 준비가 되었습니다. 이 튜토리얼에서는 액터 를 CameraDirector라는 새 클래스로 확장할 수 있습니다.
-
CameraDirector.h에서 ACameraDirector 클래스 정의 맨 아래에 다음 코드를 추가합니다.
UPROPERTY(EditAnywhere) AActor* CameraOne; UPROPERTY(EditAnywhere) AActor* CameraTwo; float TimeToNextCameraChange;
UPROPERTY 매크로는 언리얼 엔진 에 변수가 표시되도록 합니다. 이렇게 하면 게임을 시작하거나 향후 작업 세션에서 레벨이나 프로젝트를 다시 로드할 때 이러한 변수에 설정된 값이 재설정되지 않습니다. 또한 언리얼 에디터 에서 CameraOne과 CameraTwo를 설정할 수 있는 EditAnywhere 키워드도 추가했습니다.
-
CameraDirector.cpp에서 파일 맨 위의 다른 #include 줄 바로 아래에 다음 코드 줄을 추가합니다.
#include "Kismet/GameplayStatics.h"
GameplayStatics 헤더 파일을 사용하면 몇 가지 유용한 범용 함수에 액세스할 수 있으며, 그중 하나가 이 튜토리얼에 필요합니다. 완료되면 ACameraDirector::Tick 하단에 다음 코드를 추가합니다.
const float TimeBetweenCameraChanges = 2.0f; const float SmoothBlendTime = 0.75f; TimeToNextCameraChange -= DeltaTime; if (TimeToNextCameraChange <= 0.0f) { TimeToNextCameraChange += TimeBetweenCameraChanges; // 로컬 플레이어의 제어를 처리하는 액터를 찾습니다. APlayerController* OurPlayerController = UGameplayStatics::GetPlayerController(this, 0); if (OurPlayerController) { if ((OurPlayerController->GetViewTarget() != CameraOne) && (CameraOne != nullptr)) { // 즉시 카메라 1로 잘라냅니다. OurPlayerController->SetViewTarget(CameraOne); } else if ((OurPlayerController->GetViewTarget() != CameraTwo) && (CameraTwo != nullptr)) { // 카메라 2로 부드럽게 블렌딩합니다. OurPlayerController->SetViewTargetWithBlend(CameraTwo, SmoothBlendTime); } } }
이 코드를 사용하면 3초마다 디폴트 플레이어의 뷰를 서로 다른 두 카메라 간에 전환할 수 있습니다.
-
이제 코드를 컴파일할 준비가 되었으므로 언리얼 에디터 로 돌아가서 컴파일(Compile) 버튼을 누를 수 있습니다.
추가 코드는 필요하지 않습니다. 이제 월드에 CameraDirector를 구성할 수 있습니다.
완료된 코드
CameraDirector.h
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/Actor.h"
#include "CameraDirector.generated.h"
UCLASS()
class HOWTO_AUTOCAMERA_API ACameraDirector : public AActor
{
GENERATED_BODY()
public:
// 이 액터 프로퍼티의 디폴트값 설정
ACameraDirector();
protected:
// 게임 시작 시 또는 스폰 시 호출
virtual void BeginPlay() override;
public:
// 모든 프레임에서 호출
virtual void Tick( float DeltaSeconds ) override;
UPROPERTY(EditAnywhere)
AActor* CameraOne;
UPROPERTY(EditAnywhere)
AActor* CameraTwo;
float TimeToNextCameraChange;
};
CameraDirector.cpp
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "HowTo_AutoCamera.h"
#include "CameraDirector.h"
#include "Kismet/GameplayStatics.h"
// 디폴트값 설정
ACameraDirector::ACameraDirector()
{
// 이 액터가 프레임마다 Tick()을 호출하도록 설정합니다. 필요 없는 경우 퍼포먼스 향상을 위해 이 설정을 끌 수 있습니다.
PrimaryActorTick.bCanEverTick = true;
}
// 게임 시작 시 또는 스폰 시 호출
void ACameraDirector::BeginPlay()
{
Super::BeginPlay();
}
// 모든 프레임에서 호출
void ACameraDirector::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
const float TimeBetweenCameraChanges = 2.0f;
const float SmoothBlendTime = 0.75f;
TimeToNextCameraChange -= DeltaTime;
if (TimeToNextCameraChange <= 0.0f)
{
TimeToNextCameraChange += TimeBetweenCameraChanges;
//로컬 플레이어의 제어를 처리하는 액터를 찾습니다.
APlayerController* OurPlayerController = UGameplayStatics::GetPlayerController(this, 0);
if (OurPlayerController)
{
if ((OurPlayerController->GetViewTarget() != CameraOne) && (CameraOne != nullptr))
{
//즉시 카메라 1로 잘라냅니다.
OurPlayerController->SetViewTarget(CameraOne);
}
else if ((OurPlayerController->GetViewTarget() != CameraTwo) && (CameraTwo != nullptr))
{
//카메라 2로 부드럽게 블렌딩합니다.
OurPlayerController->SetViewTargetWithBlend(CameraTwo, SmoothBlendTime);
}
}
}
}
3 - 월드에 카메라 디렉터 배치
-
코드가 컴파일되면 콘텐츠 브라우저 에서 레벨 에디터 로 새 클래스의 인스턴스를 드래그할 수 있습니다.
-
다음으로 CameraOne 및 CameraTwo 변수를 설정해야 합니다. 월드 아웃라이너 에서 CameraDirector를 찾아 디테일 패널 에서 편집합니다.
'없음(None)'이라고 표시된 드롭다운 박스를 클릭하고 변수를 이전에 만든 Cube 와 CameraActor 로 설정합니다.
-
플레이(Play) 를 누르면 카메라가 이 뷰로 스냅되는 것을 볼 수 있습니다.
그런 다음 이 뷰에 부드럽게 블렌딩됩니다.
몇 초 후에 다시 스냅됩니다.
이제 게임 로직만을 기반으로 플레이어의 카메라를 움직이는 시스템이 생겼습니다. 이 코드는 플레이어가 카메라를 직접 제어할 수 없거나 카메라 뷰 간의 블렌딩이 유용한 모든 게임에서 사용하도록 수정할 수 있습니다.
완료된 코드
CameraDirector.h
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/Actor.h"
#include "CameraDirector.generated.h"
UCLASS()
class HOWTO_AUTOCAMERA_API ACameraDirector : public AActor
{
GENERATED_BODY()
public:
// 이 액터 프로퍼티의 디폴트값 설정
ACameraDirector();
protected:
// 게임 시작 시 또는 스폰 시 호출
virtual void BeginPlay() override;
public:
// 모든 프레임에서 호출
virtual void Tick( float DeltaSeconds ) override;
UPROPERTY(EditAnywhere)
AActor* CameraOne;
UPROPERTY(EditAnywhere)
AActor* CameraTwo;
float TimeToNextCameraChange;
};
CameraDirector.cpp
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "HowTo_AutoCamera.h"
#include "CameraDirector.h"
#include "Kismet/GameplayStatics.h"
// 디폴트값 설정
ACameraDirector::ACameraDirector()
{
// 이 액터가 프레임마다 Tick()을 호출하도록 설정합니다. 필요 없는 경우 퍼포먼스 향상을 위해 이 설정을 끌 수 있습니다.
PrimaryActorTick.bCanEverTick = true;
}
// 게임 시작 시 또는 스폰 시 호출
void ACameraDirector::BeginPlay()
{
Super::BeginPlay();
}
// 모든 프레임에서 호출
void ACameraDirector::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
const float TimeBetweenCameraChanges = 2.0f;
const float SmoothBlendTime = 0.75f;
TimeToNextCameraChange -= DeltaTime;
if (TimeToNextCameraChange <= 0.0f)
{
TimeToNextCameraChange += TimeBetweenCameraChanges;
//로컬 플레이어의 제어를 처리하는 액터를 찾습니다.
APlayerController* OurPlayerController = UGameplayStatics::GetPlayerController(this, 0);
if (OurPlayerController)
{
if (CameraTwo && (OurPlayerController->GetViewTarget() == CameraOne))
{
//카메라 2로 부드럽게 블렌딩합니다.
OurPlayerController->SetViewTargetWithBlend(CameraTwo, SmoothBlendTime);
}
else if (CameraOne)
{
//즉시 카메라 1로 잘라냅니다.
OurPlayerController->SetViewTarget(CameraOne);
}
}
}
}
4 - 직접 해보기
여기에서 배운 것을 활용하여 다음과 같은 작업을 해보세요.
- 움직이는 액터에 카메라를 연결하여 크레인이나 돌리 샷을 만듭니다.
- 두 개만이 아닌 원하는 숫자의 시퀀스를 진행할 수 있도록 CameraOne 및 CameraTwo 대신 카메라에 저장된 단일 배열 변수를 사용합니다.
- 액터 포인터를 사용하여 카메라를 저장하는 대신 포인터와 뷰를 변경하기 전의 시간을 저장하는 구조를 만들고 시간을 새 뷰에 블렌딩합니다.
이 튜토리얼에서 살펴본 내용에 대해서는 다음을 참조하세요.
- 카메라 및 그 제어 방법에 대한 자세한 내용은 카메라 프레임워크 페이지를 참조하거나 플레이어 제어 카메라 튜토리얼을 진행해 보세요.
- 추가 튜토리얼은 C++ 프로그래밍 튜토리얼 페이지를 참조하세요.