미리보기

구현내용
플레이어는 포탈을 통해 맵 간 이동을 수행하며,
이동시 Level Sequence의 Dolly - Zoom 카메라 기법을 활용하여
포탈에 빨려 들어가는 듯한 시각적 연출을 구현
달리 줌 (Dolly - Zoom) 이란?
일명 히치콕 줌 또는 현기증 효과라고 불리며
카메라의 전후로 이동하는 동시에 줌을 조절하여 독특한 원근감을 만드는 카메라 기법입니다
피사체의 크기는 일정하게 유지하면서 배경만 기묘하게 압축/확장해 보이는 영상 효과입니다

구현c++
C++에서 레벨시퀀스를 사용할려면 Build.cs파일의 수정이 필요합니다
해당 참조를 추가해줍니다
"LevelSequence", "MovieScene"
필요한 헤더도 추가해줍니다
#include "LevelSequence.h"
#include "LevelSequencePlayer.h"
#include "LevelSequenceActor.h"
BeginPlay에서 시퀀스재생준비를 하고 ActivatePortal함수를 외부에서 호출하면 시퀀스를 실행시킵니다
UPROPERTY(EditAnywhere, Category="Settings")
class ULevelSequence* portalSequence;
UPROPERTY()
class ULevelSequencePlayer* sequencePlayer;
UPROPERTY()
class ALevelSequenceActor* sequenceActor;
void APortal::BeginPlay()
{
Super::BeginPlay();
if (portalSequence)
{
FMovieSceneSequencePlaybackSettings settings;
sequencePlayer =ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(),portalSequence,settings,sequenceActor);
sequencePlayer->OnFinished.AddDynamic(this, &APortal::OnPortalSequenceFinished);
}
}
void APortal::ActivatePortal(AMainCharacter* player)
{
cachedPlayer = player;
if (!cachedPlayer)
return;
if (APlayerController* pc = Cast<APlayerController>(cachedPlayer->GetController()))
pc->DisableInput(pc);
if (sequencePlayer)
sequencePlayer->Play();
HidePingIndicator();
cachedPlayer->LoadFade();
}
void APortal::OnPortalSequenceFinished()
{
if (APlayerController* pc = Cast<APlayerController>(cachedPlayer->GetController()))
pc->EnableInput(pc);
cachedPlayer->LoadBossMap();
}
레벨 시퀀스를 만들어줍니다

카메라를 카메라릭에 Attach해준뒤 카메라는 카메라릭을 따라 뒤로 이동합니다

카메라 컴포넌트를 선택하고 이벤트 - 리피터 를 선택하여 이벤트존을 생성해줍니다

리피터 카메라존의 노드입니다
간단한 카메라 작업입니다 대충 플레이어와 카메라와의 거리를 계산해서 초점길이를 계산해주는일을 담당합니다
간단한작업이라 블루프린트로 진행하였습니다

맵 이동
맵 이동 방식은 비동기 레벨 이동 방식을 사용하였습니다
맵을 미리 로딩 후 로딩이 완료되면 맵을 이동하도록 설계하였습니다
이전글에서 다루었던 내용이니 올려두겠습니다
2026.01.20 - [Unreal 프로젝트 다이어리/두번째 프로젝트] - Unreal - 죽음 / 리스폰
Unreal - 죽음 / 리스폰
미리보기구현내용플레이어가 Hp가 0이 되어 죽었을때의 플레이어 죽음 처리죽음이후에 처리되는 플레이어 리스폰 처리를 구현하였습니다리스폰은 두가지 종류가 있으며플레이어의 라이프포인
lucodev.tistory.com
void ALoadGameMode::BeginPlay()
{
Super::BeginPlay();
gameIst = Cast<UProjectGameInstance>(GetGameInstance());
if (gameIst && gameIst->loadingWidget)
gameIst->loadingWidget->AddToViewport();
FName targetMap = NAME_None;
if (loadType == ELoadMapType::FromSave)
{
if (gameIst->saveManager && gameIst->saveManager->currentSave)
targetMap = gameIst->saveManager->currentSave->savedMapName;
}
else if (loadType == ELoadMapType::FromFixedCoordinates)
{
targetMap = gameIst->loadMapToBoss;
}
if (!targetMap.IsNone())
{
FString levelPath = FString::Printf(TEXT("/Game/Maps/%s.%s"),*targetMap.ToString(),*targetMap.ToString());
LoadPackageAsync(levelPath, FLoadPackageAsyncDelegate::CreateUObject(this, &ALoadGameMode::OnGameLevelLoaded));
}
}
void ALoadGameMode::OnGameLevelLoaded(const FName& packageName, UPackage* loadedPackage, EAsyncLoadingResult::Type result)
{
if (gameIst && gameIst->loadingWidget)
gameIst->loadingWidget->SetVisibility(ESlateVisibility::Visible);
if (result == EAsyncLoadingResult::Succeeded)
{
FTimerHandle TH_OpenLevelPack;
FTimerDelegate TD_OpenLevelDel;
TD_OpenLevelDel.BindLambda([this, packageName]()
{
FString mapName = packageName.ToString();
UGameplayStatics::OpenLevel(this, FName(*mapName));
});
GetWorldTimerManager().SetTimer(TH_OpenLevelPack, TD_OpenLevelDel, 3.0f, false);
}
}
결과
플레이어는 포탈근처로 다가가서 상호작용 E키를 누르면 시퀀스가 발동합니다

시퀀스를 통해 달리줌 효과가 나타납니다

페이드아웃이 실행된뒤 로딩맵 -> 지정된 보스맵 으로 이동합니다

영상
(안타깝게도 3월중순부터 티스토리가 자체적으로 영상을 업로드할수없게 정책이 바뀌고
올라갔던 영상도 삭제한다더라구하더라구요 힘들지만 영상을 전부 유튜브로 옮기게 되었습니다)
'Unreal 프로젝트 다이어리 > 두번째 프로젝트' 카테고리의 다른 글
| Unreal - AI (순찰, 전투) (0) | 2026.02.05 |
|---|---|
| Unreal - AI (Behavior Tree 설계하기) (0) | 2026.02.02 |
| Unreal - 상호작용 추가 (2) | 2026.01.24 |
| Unreal - 트리거 매니저 ( 낙사 & 오토세이브 ) (1) | 2026.01.21 |
| Unreal - 죽음 / 리스폰 (0) | 2026.01.20 |