[Unreal_C++_DarkSoul]#20_Level Sequence
Cinematic을 재생하는 Level Sequence를 제작하고, Game Play에 삽입합니다.
Overview
- 개요 및 설계
- 코드
- 영상
#1. 개요 및 설계
1. 개요
- 일반 Stage(일반 근거리 공격 객체와 원거리 공격 객체만 나오는 단계)와 Boss Stage(보스 객체가 나오는 단계)의 구별이 필요하다고 판단했으며, 이를 위해 오직 Boss Stage에 발생하는 특별한 연출을 구현해보고자 했습니다.
- 재생 가능한 Level Sequence를 제작하고, Game Play 중 Player 캐릭터 객체가 일정 위치에 도달하면 Cinematic을 재생합니다. 이때, Level Sequence의 재생은 Game Play와 자연스럽게 연결되어 Seamless Transition(Game Play와 Cinematic 재생의 서로 간 끊김 없는 전환)을 목표로합니다.
2. 설계
- Level Sequence : Unreal Engine의 Level Sequence는 Level의 시퀀스 애니메이션을 만들고 편집하는 데 사용되는 도구입니다. 더불어, Level Sequence는 레벨에 배치된 개체, 조명, 카메라 및 기타 요소를 사용하여 영화 같은 시퀀스를 만들 수 있습니다. 이 프로젝트에서 Level Sequence는 플레이어 캐릭터가 Boss Stage의 특정 위치에 도달하면 Cinematic 영상을 재생하여, 일반 Stage와 대비를 만들기 위한 장치로 활용합니다.
- Trigger_CinematicPlay 객체: 먼저, Level에 위치한 Level Sequence의 재생을 유도하기 위한 Trigger_CinematicPlay 객체를 정의합니다. Trigger_CinematicPlay 객체는 Level의 특정 장소에 위치하여 Player 캐릭터 객체와 상호작용합니다. 단순히, Player 캐릭터 객체가 Trigger_CinematicPlay의 Box Component가 정의하는 특정 바운더리 내 위치하면 "충돌 검사"를 통해 Player 캐릭터 객체를 인지합니다. 그리고, Trigger_CinematicPlay 객체는 Editor에서 미리 입력받은 Level Sequence를 재생시킵니다.
- Grid 객체: Grid 객체는 공간 분할 패턴과 객체 풀링을 활용해 월드에 배치된 적 객체들을 "위치 값"을 기준으로 효율적으로 나누어 저장하고 관리합니다. Grid객체는 Level Sequence가 Cinematic 영상을 재생하는 동안 자신이 관리하는 적 객체들의 활동을 비활성화시키고, Level Sequence의 재생이 완료되는 시점에 다시 활성화시키도록 수정합니다.
#2. 코드
1. Level Sequence
Details
- 먼저, Level Sequence를 제작합니다.
- 제작 방법은 보통의 편집 프로그램과 비슷하고, 추가적으로 카메라 샷의 "블렌드" 기능을 활성화시켜 영상에 적용하면 Game Play와 자연스럽게 연결되어 Seamless Transition을 구현할 수 있습니다! 자세한 내용은 아래 "영상" 항목에 게시한 구현 영상을 참고하세요.
2. Trigger_CinematicPlay::BeginPlay()
void ATrigger_CinematicPlay::BeginPlay()
{
Super::BeginPlay();
// [23-09-08] : Level Sequence And Cinematic
CinematicTriggerBox->OnComponentBeginOverlap.AddDynamic(this, &ATrigger_CinematicPlay::OnOverlapBegin);
}
Details
- Trigger_CinematicPlay 객체는 Player 캐릭터 객체와 "충돌"하면, Unreal 에디터에서 입력받은 Level Sequence를 재생합니다. 따라서, Box Component의 "OnComponentBeginOverlap" Delegate 함수에 Level Sequence의 재생 여부를 관리하는 함수 "OnOverlapBegin"를 동적 바인딩 합니다.
3. Trigger_CinematicPlay::OnOverlapBegin()
void ATrigger_CinematicPlay::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
FTimerHandle LevelSeqPlayHandler;
GetWorld()->GetTimerManager().SetTimer(LevelSeqPlayHandler, FTimerDelegate::CreateLambda([&]() {
if (PlayCount < Max_PlayCount && LevelSeqActor)
{
PlayLevelSeq();
PlayCount++;
}
}), 2.f, false);
}
Details
- Player Character 객체와 Box Component의 충돌 처리를 수행하는 함수 "OnBeginOverlap" 함수는 Level Sequence의 최대 재생 횟수를 확인하고 "PlayLevelSeq" 함수를 호출합니다.
- 이때, Timer를 활용하여 Player Character와 Box Component의 충돌 시점과 Level Sequence 재생 시점 사이에 딜레이를 두기 위함입니다.
4. Grid::BeginPlay()
void AGrid::BeginPlay()
{
Super::BeginPlay();
//...
// [23-09-08] : Level Sequence And Cinematic
if (bDeactivateGridWhileLevelSeqPlay)
{
HandleLevelSeqStarted();
TArray<AActor*> AllActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ALevelSequenceActor::StaticClass(), AllActors);
for (auto Actor : AllActors)
{
if (Actor)
{
ALevelSequenceActor* LevelSeq = Cast<ALevelSequenceActor>(Actor);
if (LevelSeq && LevelSeq->GetSequencePlayer())
{
LevelSeq->GetSequencePlayer()->OnFinished.AddDynamic(this, &AGrid::HandleLevelSeqFinished);
}
}
}
}
}
Details
- Grid 객체는 Level Sequence가 재생하는 동안 자신이 관리하는 적 객체 그룹의 활동을 비활성화시키며, Level Sequence가 끝나는 시점에 다시 활성화시킵니다.
- Grid 객체는 Unreal 에디터에서 미리 입력받은 "bDeactivateGridWhilleLevelSeqPlay" 값이 참일 경우 Level Sequence가 재생하는 시점에 적 객체들의 활동성을 관리하는 "HandleLevelSeqStarted" 함수를 호출하고, Level Sequence의 "OnFinished" Delegate 함수에 역시 적 객체 그룹의 활동성을 관리하는 "HandleLevelSeqFinished" 함수를 동적 바인딩합니다.
5. Grid::HandleLevelSeqStarted()
// [23-08-31] Deactivate Grid While Level Seq Playing
void AGrid::HandleLevelSeqStarted()
{
for (const auto Enemy : EnemyGroupOnGrid)
{
if (Enemy)
{
// #1. Actor
Enemy->SetActorHiddenInGame(true);
if (Enemy->GetWeaponComponent()->GetCurrentWeapon())
Enemy->GetWeaponComponent()->GetCurrentWeapon()->OffSelected();
// #3. AI
AAIController* AI = Cast<AAIController>(Enemy->GetController());
if (AI && AI->GetBrainComponent())
{
AI->GetBrainComponent()->PauseLogic("AI Pause Logic");
}
PrintLine();
}
}
if (BossEnemy)
{
BossEnemy->SetActorHiddenInGame(true);
if (BossEnemy->GetWeaponComponent()->GetCurrentWeapon())
BossEnemy->GetWeaponComponent()->GetCurrentWeapon()->OffSelected();
AAIController* AI = Cast<AAIController>(BossEnemy->GetController());
if (AI && AI->GetBrainComponent())
{
AI->GetBrainComponent()->PauseLogic("AI Pause Logic");
}
}
PrintLine();
}
Details
- "HandleLevelSeqStarted" 함수는 Grid 객체가 관리하는 적 객체 그룹의 활동을 비활성화시킵니다.
- 먼저, Grid 객체가 관리하는 모든 적 객체를 숨기고, 이들이 현재 착용하고 있는 무기 또한 해제해 주며 이들의 AI 객체의 Brain Component를 멈춥니다.
6. Grid::HandleLevelSeqFinished()
// [23-08-31] Deactivate Grid While Level Seq Playing
void AGrid::HandleLevelSeqFinished()
{
for (const auto Enemy : EnemyGroupOnGrid)
{
if (Enemy)
{
Enemy->SetActorHiddenInGame(false);
TArray<AWeapon*> Weapons = Enemy->GetWeaponComponent()->GetWeapons();
if (Weapons.Num() > 0 && Weapons.IsValidIndex(0))
{
EWeaponType WeaponType = Weapons[0]->GetWeaponType();
Enemy->GetWeaponComponent()->EquipWeapon(WeaponType);
}
AAIController* AI = Cast<AAIController>(Enemy->GetController());
if (AI && AI->GetBrainComponent())
{
AI->GetBrainComponent()->ResumeLogic("AI Resume Logic");
}
}
}
if (BossEnemy)
{
BossEnemy->SetActorHiddenInGame(false);
TArray<AWeapon*> Weapons = BossEnemy->GetWeaponComponent()->GetWeapons();
if (Weapons.Num() > 0 && Weapons.IsValidIndex(0))
{
EWeaponType WeaponType = Weapons[0]->GetWeaponType();
BossEnemy->GetWeaponComponent()->EquipWeapon(WeaponType);
}
AAIController* AI = Cast<AAIController>(BossEnemy->GetController());
if (AI && AI->GetBrainComponent())
{
AI->GetBrainComponent()->ResumeLogic("AI Resume Logic");
}
}
bDeactivateGridWhileLevelSeqPlay = false;
}
Details
- 반대로, "HandleLevelSeqFinished" 함수는 Grid 객체가 관리하는 모든 적 객체의 가시성을 활성화고 무기를 착용하며, AI 객체의 Brain Component를 다시 동작하도록 합니다.
#3. 영상
1. 구현 영상
'개인프로젝트' 카테고리의 다른 글
[Unreal_C++_DarkSoul]#22_기능 구현, HUD (0) | 2023.12.01 |
---|---|
[Unreal_C++_DarkSoul]#21_기능 구현, 데이터 변환 (0) | 2023.11.05 |
[Unreal_C++_DarkSoul]#19_기능 구현, Portal (0) | 2023.07.23 |
[Unreal_C++_DarkSoul]#18_기능 구현, Sound (0) | 2023.07.23 |
[Unreal_C++_DarkSoul]#17_기능 구현, Grid 클래스 (0) | 2023.06.03 |