게임 프로젝트를 진행하면서 암전 효과가 필요했다.
특정한 버튼을 누르면 화면이 어두워졌다가 다시 밝아지면서 맵 또는 캐릭터가 변화하는 느낌을 주고 싶었다.
이를 언리얼에는 간단하게 시간 기반 애니메이션을 재생할 수 있도록 도와주는 타임라인이라는 시스템을 활용하여 암전 효과를 구현해보자.
에디터에서의 준비
일단 암전효과를 구현하기 위해 PostProcessVolume을 사용할 것이므로 레벨에 하나 놓아두고, 원하는 범위를 설정한다.
PostProcessVolume의 Exposure의 노출 보정(Auto Exposure Bias)를 활성화시켜준다.
값은 숫자가 커질수록 화면이 밝아지고, 작아지면 어두워지기 때문에 타임라인을 사용해 값을 낮췄다가 다시 복구시킬 것이다.
또한 코드상에서 이 볼륨을 찾기 위해 MainPostProcessVolume이라는 Tag를 설정해준다.
타임라인을 사용할 때 기본적으로 Curve가 필요하다.
물론 C++상에서 직접 값을 적어서 코드로 구현할 수 있지만, 필자는 에디터에서 구현해서 간단하게 적용하였다.
에디터에서 CV_Blank 라는 이름으로 커브를 생성한 후, 키 추가로 내가 원하는 커브를 만들어준다.
Exposure값을 활용할 것이기 때문에 값을 0초와 1초때는 1, 0.5초때엔 -15로 설정한다.
코드로 구현
사용할 액터를 생성한다.
필자는 현재 플레이어가 사용하고 있는 캐릭터를 활용할 것이기 때문에, 기존에 존재하는 FirstPerson파일을 사용하였다.
//Character.h
...
private:
APostProcessVolume* MainPostProcessVolume; // 1
FPostProcessSettings* settings; // 2
FOnTimelineFloat BlankCallback; // 3
FOnTimelineEvent BlankTimelineFinish; // 4
UPROPERTY()
UTimelineComponent* BlankTimeline; // 5
UPROPERTY(EditAnywhere, Category="Timeline")
UCurveFloat* BlankCurve; // 6
UFUNCTION()
void BlankInterpReturn(float value); // 7
UFUNCTION()
void BlankFinish(); // 8
...
1은 레벨 상에 존재하는 ProcessVolume을 찾아서 넣어줄 값이다.
2는 1의 설정들을 확인해서 넣어줄 값이다.
3과 7은 타임라인이 돌아가면서 Delegate를 이용해 함께 기능을 수행할 것이다.
4와 8은 타임라인이 끝날 때 Delegate를 이용해 기능을 수행할 것이다.
5는 메인 타임라인이다.
6은 타임라인에서 사용할 커브를 에디터 상에서 불러와 넣어둔다.
//Character.cpp
APracticeProjectCharacter::APracticeProjectCharacter()
{
...
MainPostProcessVolume = nullptr;
settings = nullptr;
BlankTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("BlankTimeline"));
BlankCallback.BindUFunction(this, FName("BlankInterpReturn"));
BlankTimelineFinish.BindUFunction(this, FName("BlankFinish"));
static ConstructorHelpers::FObjectFinder<UCurveFloat> curve(TEXT("/Game/Assets/CV_Blank.CV_Blank"));
if (curve.Succeeded())
{
BlankCurve = curve.Object;
}
}
Timeline을 생성해 준 뒤, BindUFunction을 이용해 각각의 기능들을 바인드해준다.
Curve는 에디터상에서 생성해 둔 Curve의 레퍼런스 주소를 복사해서 ConstructorHelper를 사용해 지정해준다.
//Character.cpp
void APracticeProjectCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
...
for (TActorIterator<APostProcessVolume> Actor(GetWorld()); Actor; ++Actor)
{
if (Actor->ActorHasTag("MainPostProcessVolume"))
{
MainPostProcessVolume = *Actor;
settings = (FPostProcessSettings*)MainPostProcessVolume->GetProperties().Settings;
break;
}
}
BlankTimeline->SetLooping(false);
BlankTimeline->AddInterpFloat(BlankCurve, BlankCallback);
BlankTimeline->SetTimelineFinishedFunc(BlankTimelineFinish);
BlankTimeline->SetTimelineLength(1.0f);
}
void APracticeProjectCharacter::BlankInterpReturn(float value)
{
settings->AutoExposureBias = value;
}
BeginPlay()가 실행될 때, TActorIterator를 사용해서 MainPostProcessVolume태그를 가진 볼륨을 찾고, 그 볼륨과 설정값을 헤더 1번과 2번 변수에 넣어준다.
또한 생성한 타임라인의 기본 설정을 지정해준다.
SetLooping(false)은 타임라인의 반복 여부, AddInterpFloat(BlankCurve, BlankCallback)는 커브값에 따라 지정되어있는 커브에 따라 실행할 함수 지정, SetTimelineFinishedFunc(BlankTimelineFinish)는 타임라인이 끝나고 실행할 함수, SetTimelineLength(1.0f)는 타임라인의 길이를 설정한다.
MainProcessVolume에서 노출 보정의 값을 수정하는 것은 위에서 이미 저장해 둔 settings의 AutoExposureBias를 사용하면 된다.
BlankTimeline->PlayFromStart();
이제 타임라인을 실행할 위치해서 이 코드를 실행시키면 커브에 따라 타임라인이 실행되면서 암전 효과를 재생할 수 있다.
결과는 다음과 같다.
'UnrealEngine > 공부' 카테고리의 다른 글
[Unreal Engine C++] 청크 개념을 이용한 랜덤 맵 생성 알고리즘 (0) | 2024.03.30 |
---|---|
[Unreal Engine C++] 랜덤 맵 생성에 관한 아이디어들 (0) | 2024.03.29 |
[Unreal Engine C++] Fireball의 구현 (1) | 2024.02.27 |
[Unreal Engine C++] 진행도에 따른 텔레포트 설정 (1) | 2024.02.27 |
[Unreal Engine C++] 현재 레벨 저장 기능의 구현 (0) | 2024.02.27 |
CSE & GAME 개발 블로그
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 부탁드립니다!