Unreal - 죽음 / 리스폰

2026. 1. 20. 04:24·Unreal 프로젝트 다이어리/두번째 프로젝트

미리보기

구현내용

플레이어가 Hp가 0이 되어 죽었을때의 플레이어 죽음 처리

죽음이후에 처리되는 플레이어 리스폰 처리를 구현하였습니다

리스폰은 두가지 종류가 있으며

플레이어의 라이프포인트가 있다면 제자리 부활인 Revive

플레이어의 라이프포인트가 없다면 해당 맵부터 시작하는 완전리스폰으로 구성되어있습니다.

 

"죽음" 구현 방식

게임에서는 많은 죽음 의 구현 방식이 있습니다

그중에서 대표적인 방법으로는 물리엔진의 렉돌 을 사용하는 방식

 

TeamFortress2 처럼 플레이어가 메시의 Physics Asset 설정값에 따라 저렇게 자유롭게 되는방식

 

혹은 캐릭터가 미리 만들어진 사망 애니메이션을 재생하고 끝나는 방식 등 많습니다.

저의 프로젝트는 세키로를 오마주한 게임이라 적의 시체에 렉돌을 적용하면 맞지 않는다고 판단하여,

사망 애니메이션 후 Freeze 방식을 사용하였습니다.

 

만약 적 혹은 플레이어가 죽었을때 애니메이션 몽타쥬를 재생한다면

딱 애니메이션 만큼 재생을 하고 원래 사용하던 StateMachine의 Idle로 돌아올게 뻔하니

SavePoseSnapshot 기반으로 플레이어를 정지 하는 방식을 채용하였습니다.

void UMainCharacterAnimInstance::AnimNotify_FreezeDeadPose()
{
	SavePoseSnapshot(FName("frozenPose"));
    
	mainCharacter->FreezeDeadBody(true);
    ..이하생략
}

 

frozenPose라는 이름으로 스냅샷을 찍습니다.

 

그리고 Blend Poses by bool 노드를 사용하여 기존의 캐싱된 StateMachine과 스냅샷 변수로 포즈를 얼렸습니다.

플레이어는 Freeze를 실행하게된다면 콜리전을 영향을 받지 않도록 코드처리도 잊지 않고 해주었습니다

//얼렸을때
selfCapsule->SetCollisionEnabled(ECollisionEnabled::NoCollision);
selfCapsule->SetGenerateOverlapEvents(false);

//되돌렸을때
selfCapsule->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
selfCapsule->SetGenerateOverlapEvents(true);

 

플레이어를 시체상태에서 얼린뒤 타이머를 사용하여 순차적으로 연출을 나타내었습니다

헬퍼 함수를 사용하여 중복된 코드가 없도록 설계하였습니다

 

코드더보기

더보기
UFUNCTION()
void InitializeDeathState();

UFUNCTION()
void PlayDeathAnimation();

void SetDeathTimer(float deathTime, TFunction<void()> func);

void AMainCharacter::InitializeDeathState()
{
    statComp->bIsDead = true;
    lifeState = EPlayerLifeState::Dead;

    GetCharacterMovement()->StopMovementImmediately();
    GetCharacterMovement()->bOrientRotationToMovement = true;
    bUseControllerRotationYaw = false;

    mainCon->bMoveForward = false;
    mainCon->bMoveBack = false;
    mainCon->bMoveLeft = false;
    mainCon->bMoveRight = false;
    mainCon->bShiftPressed = false;
}

void AMainCharacter::PlayDeathAnimation()
{
    if (UAnimInstance* animInst = GetMesh()->GetAnimInstance())
    {
        FCharacterAnimDataTable* row = dieNormalRows[0];
        animInst->Montage_Play(row->usingAnimation);
    }

    mainCon->mainWidget->Image_EffectDamage->SetVisibility(ESlateVisibility::Visible);
}

void AMainCharacter::SetDeathTimer(float deathTime, TFunction<void()> func)
{
    FTimerHandle TH_deathScene;
    GetWorldTimerManager().SetTimer(TH_deathScene, MoveTemp(func), deathTime, false);
}
void AMainCharacter::PlayerDead()
{
    if (dieNormalRows.Num() == 0)
        return;
    InitializeDeathState();
    PlayDeathAnimation();
    SetDeathTimer(2.0f, [this]()
        {
            DieScene(true);
        });

    SetDeathTimer(4.3f, [this]()
        {
            StartFade(true);
        });

    if (statComp->revivePoint <= 0)
    {
        lifeState = EPlayerLifeState::NonRevivable;

        SetDeathTimer(7.3f, [this]()
            {
                if (!gameInst->loadingLevelName.IsNone())
                    UGameplayStatics::OpenLevel(this, gameInst->loadingLevelName);
            });

        return;
    }
    SetDeathTimer(9.0f, [this]()
        {
            soundSubsystem->PlaySound("Onkami", this, GetActorLocation());
            SetDialogue(DieText);
        });

    SetDeathTimer(16.0f, [this]()
        {
            StartFade(false);
            mainCon->mainWidget->ResetDialogue();
            mainCon->mainWidget->SetReviveImage(true);
            mainCon->mainWidget->DieScene(false);
            mainCon->mainWidget->Image_Die->SetVisibility(ESlateVisibility::SelfHitTestInvisible);

            lifeState = EPlayerLifeState::Revivable;
        });

}

 

Respawn 과 Revive 의 설계

Revive는 제자리부활이라 Freeze를 풀어주고 원래 상태로 되돌리면 되었습니다

하지만 Respawn은 맵을 다시 열어야하는데 맵을 열때 만약 맵의 규모가 크다면 잠깐 렉이 걸리니

로딩창을 사용하는 방식을 사용하였습니다

 

비동기 로드

만약 OpenLevel을 사용한다면 맵의 규모에 따라서 사용자의 pc화면이 멈칫 하는 좋지않은 경험을 선사할수 있는데

이걸 막고자 비동기 로드를 사용하였습니다

로딩의 방식은 이와 같습니다

  1. 로딩 전용 맵으로 이동하여 로딩 UI를 실행한다
  2. 비동기로 메모리에 로드 한뒤 충분한 시간을 가진뒤 로드가 완료되면 다음 맵으로 이동한다

로딩맵에서 사용될 게임모드의 BeginPlay에서 실행하여 자연스럽게 맵이 이동된후 실행되도록 설계하였습니다.

void ALoadGameMode::BeginPlay()
{
	Super::BeginPlay();

	gameIst = Cast<UProjectGameInstance>(GetGameInstance());
	if (gameIst && gameIst->loadingWidget)
		gameIst->loadingWidget->AddToViewport();

	if (!gameIst->loadMap01.IsNone())
	{
		FString levelPath = FString::Printf(TEXT("/Game/Maps/%s.%s"), *gameIst->loadMap01.ToString(), *gameIst->loadMap01.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);
    }
}

 

로딩위젯 작업도 해줍니다

어후 위젯작업 할게 너무 많다..

결과

플레이어는 죽으면 빨간 레이어 그리고 死 한자가 연출되면서 시체상태로 얼어붙습니다.

 

페이드인 이 실행된뒤 대사가 연출됩니다.

 

대사 연출이 끝나면 플레이어가 소생포인트가 남아있다면 리스폰 or 회생 을 선택할수 있습니다

회생하기를 선택하면 플레이어의 라이프포인트를 소모하고 제자리부활을 실행합니다

 

이대로 죽는다 를 선택하거나 소생포인트가 없으면 로딩창으로 이동된뒤 해당 레벨이 다시 플레이됩니다

 

영상

 

 

저작자표시 비영리 변경금지 (새창열림)

'Unreal 프로젝트 다이어리 > 두번째 프로젝트' 카테고리의 다른 글

Unreal - 상호작용 추가  (2) 2026.01.24
Unreal - 트리거 매니저 ( 낙사 & 오토세이브 )  (1) 2026.01.21
Unreal - 적 공격 간파하기  (2) 2026.01.16
Unreal - 가드 (Guard) / 체간 (Posture) 시스템  (4) 2026.01.12
Unreal - 그래플링 훅  (0) 2026.01.08
'Unreal 프로젝트 다이어리/두번째 프로젝트' 카테고리의 다른 글
  • Unreal - 상호작용 추가
  • Unreal - 트리거 매니저 ( 낙사 & 오토세이브 )
  • Unreal - 적 공격 간파하기
  • Unreal - 가드 (Guard) / 체간 (Posture) 시스템
lucodev
lucodev
언리얼 포폴개발 일기
  • lucodev
    루코 개발테이블
    lucodev
  • 전체
    오늘
    어제
    • 분류 전체보기 (236)
      • Unreal 프로젝트 다이어리 (132)
        • 첫번째 프로젝트 (73)
        • 두번째 프로젝트 (59)
      • Unreal 팁 (8)
      • Unreal 디버깅 (8)
      • C++ 프로그래머스 (52)
        • Stack,Queue (7)
        • Hash (4)
        • Heap (2)
        • Sort (5)
        • Exhaustive search (5)
        • Greedy (2)
        • BFS , DFS (7)
        • Graph (2)
        • Dynamic Programming (1)
        • C++ Math (2)
        • 기타 문제 (14)
      • C++ 백준 (5)
      • C++ 팁 (1)
      • 개인 코테 & 스타디 <비공개> (29)
        • 코드 개인보관함 (9)
        • 코딩테스트+@ (11)
        • 알고리즘 스타디 (6)
        • 알고리즘 스타디 과제 (3)
        • 비공개 (0)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 링크

  • 공지사항

  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 태그

    언리얼 시퀀스
    언리얼 parkour
    unreal 상호작용
    언리얼 상호작용
    unreal inventory
    언리얼 behaviortree
    언리얼 컷씬
    언리얼 인터렉션
    언리얼 세키로
    unreal npc
    unreal 파쿠르
    unreal 세키로
    언리얼 파쿠르
    언리얼 behavior tree
    언리얼
    unreal 인벤토리
    언리얼 인벤토리
    언리얼 비헤이비어트리
    unreal
    언리얼 ui
  • hELLO· Designed By정상우.v4.10.3
lucodev
Unreal - 죽음 / 리스폰
상단으로

티스토리툴바