이 튜토리얼에서는 카메라를 활성화하고 활성 카메라를 다른 카메라로 변경하는 방법을 보여줍니다.
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 대신 카메라에 저장된 단일 배열 변수를 사용합니다.
- 액터 포인터를 사용하여 카메라를 저장하는 대신 포인터와 뷰를 변경하기 전의 시간을 저장하는 [구조](programming-and-scripting\programming-language-implementation## 1 - Place Cameras in the World
If you are new to Unreal Engine, you might want to read our Programming Quick Start first. For this tutorial, we will assume you are familiar with creating a project, adding C++ code to it, compiling your code, and adding Components to Actors in Unreal Engine.
-
We will begin by creating a new Basic Code project, with starter content, named "HowTo_AutoCamera". The first thing we'll need to do is create two cameras in our world. Since there are multiple ways to set up cameras, we'll go with the two most common ways here. For our first camera, go to the Place Actors panel and select the All Classes tab, you'll find a Camera actor. Drag this into the Level Editor and position it so that it has a good view of our scene.
When this is done, the Level Editor window will have a picture-in-picture view of what our new Camera Actor can see as long as we have the Camera Actor selected.
-
For our second camera, we'll use a method that goes a little more in-depth and gives us a little more control. Start by clicking the Basic tab in the Place Actors panel, then drag a Cube into the Level Editor window.
We can use almost any actor class for this step. The MyActor class we created in the Quick Start tutorial might be interesting to substitute for the Cube.
-
When our Cube actor is placed, add a CameraComponent by clicking the + Add Component button in the Details panel for the Cube. You can now set the position and rotation of that CameraComponent to give us a different view of the scene than the CameraActor we placed earlier.
-
We should customize our CameraComponent by turning on Constrain Aspect Ratio so that it matches the setting on our CameraActor. This will make transitions between camera views smoother, but it is not required.
With our world set up, we're now ready to create the class that will control our camera view.
2 - Control Camera View in C++
-
We're ready to create a C++ class to control our camera view. For this tutorial, we can extend Actor into a new class which we'll call CameraDirector.
-
In CameraDirector.h, add the following code to the bottom of the ACameraDirector class definition:
UPROPERTY(EditAnywhere) AActor* CameraOne; UPROPERTY(EditAnywhere) AActor* CameraTwo; float TimeToNextCameraChange;
The UPROPERTY macro makes our variables visible to Unreal Engine. This way, values set in these variables will not be reset when we launch the game or reload our level or project in a future work session. We have also added the EditAnywhere keyword, which will allow us to set CameraOne and CameraTwo in the Unreal Editor.
-
In CameraDirector.cpp, add the following line of code to the top of the file, right underneath the other #include lines:
#include "Kismet/GameplayStatics.h"
The GameplayStatics header file gives us access to some useful general-purpose functions, one of which we will need for this tutorial. When that is done, add the following code to the bottom of ACameraDirector::Tick:
const float TimeBetweenCameraChanges = 2.0f; const float SmoothBlendTime = 0.75f; TimeToNextCameraChange -= DeltaTime; if (TimeToNextCameraChange <= 0.0f) { TimeToNextCameraChange += TimeBetweenCameraChanges; // Find the actor that handles control for the local player. APlayerController* OurPlayerController = UGameplayStatics::GetPlayerController(this, 0); if (OurPlayerController) { if ((OurPlayerController->GetViewTarget() != CameraOne) && (CameraOne != nullptr)) { // Cut instantly to camera one. OurPlayerController->SetViewTarget(CameraOne); } else if ((OurPlayerController->GetViewTarget() != CameraTwo) && (CameraTwo != nullptr)) { // Blend smoothly to camera two. OurPlayerController->SetViewTargetWithBlend(CameraTwo, SmoothBlendTime); } } }
This code will cause us to switch the default player's view between two different cameras every three seconds.
-
Our code is now ready to be compiled, so we can return to the Unreal Editor and press the Compile button.
No further code is needed. We can set up our CameraDirector in our world now.
Finished Code
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:
// Sets default values for this actor's properties
ACameraDirector();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
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"
// Sets default values
ACameraDirector::ACameraDirector()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void ACameraDirector::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
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;
//Find the actor that handles control for the local player.
APlayerController* OurPlayerController = UGameplayStatics::GetPlayerController(this, 0);
if (OurPlayerController)
{
if ((OurPlayerController->GetViewTarget() != CameraOne) && (CameraOne != nullptr))
{
//Cut instantly to camera one.
OurPlayerController->SetViewTarget(CameraOne);
}
else if ((OurPlayerController->GetViewTarget() != CameraTwo) && (CameraTwo != nullptr))
{
//Blend smoothly to camera two.
OurPlayerController->SetViewTargetWithBlend(CameraTwo, SmoothBlendTime);
}
}
}
}
3 - Place a Camera Director in the World
-
Once our code has compiled, we can drag an instance of our new class from the Content Browser into the Level Editor.
-
Next, we'll need to set the CameraOne and CameraTwo variables. Find our CameraDirector in the World Outliner and edit it in the Details Panel.
Click on the dropdown boxes labeled "None" and set our variables to the Cube and the CameraActor that we created earlier.
-
If we press Play, we will see the camera snap to this view:
And then blend smoothly to this view:
Where it will wait a few seconds before snapping back.
We now have a system that moves the player's camera based purely on game logic. This code can be modified for use in any game where the player does not have direct control over the camera, or where blending between camera views is useful.
Finished Code
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:
// Sets default values for this actor's properties
ACameraDirector();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
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"
// Sets default values
ACameraDirector::ACameraDirector()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void ACameraDirector::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
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;
//Find the actor that handles control for the local player.
APlayerController* OurPlayerController = UGameplayStatics::GetPlayerController(this, 0);
if (OurPlayerController)
{
if (CameraTwo && (OurPlayerController->GetViewTarget() == CameraOne))
{
//Blend smoothly to camera two.
OurPlayerController->SetViewTargetWithBlend(CameraTwo, SmoothBlendTime);
}
else if (CameraOne)
{
//Cut instantly to camera one.
OurPlayerController->SetViewTarget(CameraOne);
}
}
}
}
4 - On Your Own!
Using what you have learned, try to do the following:
- Attach a Camera to a moving Actor to create a crane or dolly shot.
- Use a single Array variable to store your cameras, instead of CameraOne and CameraTwo, so you can go through a sequence of any number of cameras, instead of just two.
- Instead of using Actor pointers to store your cameras, create a Structure that holds the pointer, as well as time before changing the view, and blend time to the new view.
As for the specifics covered in this tutorial:
- For more information on Cameras and ways to control them, see the Camera framework page, or try the Player-Controlled Cameras tutorial.
- For further tutorials, see the C++ Programming Tutorials page. \Structs)를 만들고 시간을 새 뷰에 블렌딩합니다.
이 튜토리얼에서 살펴본 내용에 대해서는 다음을 참조하세요.
- 카메라 및 그 제어 방법에 대한 자세한 내용은 카메라 프레임워크 페이지를 참조하거나 플레이어 제어 카메라 튜토리얼을 진행해 보세요.
- 추가 튜토리얼은 C++ 프로그래밍 튜토리얼 페이지를 참조하세요.