RPG에서 플레이어 사망은 단순히 캐릭터가 쓰러지는 연출로 끝나는 것이 아니라, 어떤 페널티를 주고 어떤 정보를 화면에 보여줄 것인지까지 함께 설계해야 하는 시스템이다.
특히 사망 원인, 손실 자원, 복귀 흐름이 명확하게 전달되지 않으면 플레이어 입장에서는 왜 손해를 봤는지 체감하기 어렵다.
이번에는 플레이어가 사망했을 때, 마지막 공격자의 이름이 표시되는 UI와 경험치 감소 페널티 시스템을 함께 구현한 과정을 정리해보려고 한다.
이번 작업에서 반영한 목표는 아래와 같았다.
- Player 사망 시 마지막 공격자의 이름 표시
- 사망 페널티로 잃게 되는 경험치량과 퍼센티지 계산 후 UI에 표시
- 현재 레벨에서 가진 경험치가 페널티보다 부족하면 레벨을 차감하고 추가 경험치 차감
- 1레벨 0% 구간에서는 더 이상 레벨이 내려가지 않도록 예외 처리
이번 구현의 핵심은 단순한 UI 출력이 아니라, 사망 시점의 데이터 흐름을 다시 정리하고 각 클래스의 책임을 명확히 나눈 것이다.
1. 사망 UI에 어떤 정보를 보여줄지 먼저 정리
가장 먼저 플레이어가 사망했을 때 표시할 UI의 디자인과 애니메이션을 만들어주었다.
이 UI에는 추후 아래 정보가 들어가도록 구성했다.
- 플레이어를 죽인 마지막 공격자의 이름
- 이번 사망으로 잃은 경험치량
- 이번 사망으로 잃은 경험치 퍼센티지


즉, 단순히 “죽었습니다”만 보여주는 것이 아니라, 누가 죽였는지, 경험치 손해를 얼마나 봤는지를 함께 전달하는 구조로 잡았다.
2. 기존 사망 흐름과 경험치 흐름 먼저 파악
구현을 바로 시작하기 전에, 기존 작업자가 만들어둔 사망 처리 흐름과 경험치 관리 흐름을 먼저 정리했다.
기존 흐름은 대략 아래와 같았다.
- 플레이어 사망 감지는 StateComponent의 OnOutHealth
- OnOutHealth -> OnStartUITimer -> OnStartUI 흐름으로 사망 UI 출력
- PrimaryAttributeSet의 OnOutHealth 델리게이트에 UStateComponent::OnOutHealth가 바인딩되어 있음
- APlayerBase는 Gameplay Tag로 사망 상태를 확인하고, 죽었을 때 다른 행동을 막고 있음
- 경험치와 레벨은 PlayerStateBase에서 관리하며, ExperienceTable을 사용해 레벨업 기준 경험치를 계산하고 있음
즉, 사망 감지와 UI 출력, 경험치 관리는 이미 각각 흩어져 있었고, 이번 작업에서는 그 흐름을 사망 이벤트를 중심으로 다시 묶어주는 작업이 필요했다.
3. 역할 재정리: 누가 무엇을 담당할지 나누기
기존 흐름을 바탕으로, 이번에는 각 클래스의 역할을 아래처럼 다시 정리했다.
PrimaryAttributeSet
플레이어 체력이 0이 되는 순간을 감지하고, 사망 시점의 공격자 정보가 담긴 데이터를 StateComponent 쪽으로 전달
StateComponent
체력이 0이 되었음을 전달받고, PlayerBase의 공개 이벤트를 통해 “플레이어가 죽었다”는 사실을 외부로 전파
최종적으로는 UI 생성도 담당
PlayerBase
플레이어 사망 사실을 외부에 알릴 수 있는 공개 이벤트 OnPlayerDied 제공
PlayerStateBase
OnPlayerDied 이벤트를 구독하고 있다가, 사망 발생 시 경험치 페널티를 서버에서 계산
그 뒤 UI에 필요한 최종 데이터를 StateComponent에 전달
PlayerControllerBase
StateComponent가 생성한 UI를 어느 플레이어 화면에 띄워야 하는지 결정
이렇게 정리하면 사망 감지, 데이터 계산, UI 출력이 한곳에 몰리지 않고, 각각 자기 역할에 맞게 나뉘게 된다.
4. 사망 시 공격자 정보를 함께 넘기도록 구조 수정
사망 UI에 마지막 공격자 이름을 띄우려면, 체력이 0이 되는 순간 누가 마지막으로 공격했는지 같이 전달할 수 있어야 한다.
그래서 먼저 PlayerBase에 외부에서 구독 가능한 사망 이벤트 디스패처를 만들었다.
PlayerBase.h
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
.
.
.
#include "PlayerBase.generated.h"
/** 마지막 공격자 정보를 포함하여 사망 이벤트를 브로드캐스트하기 위한 델리게이트 */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerDiedSignature, AActor*, LastAttacker); // 추가
class UStatComponent;
UCLASS()
class PROJECTJIN_API APlayerBase : public ACharacterBase, ...
{
GENERATED_BODY()
public:
APlayerBase();
.
.
.
/** 플레이어 사망 시 호출될 이벤트 디스패처 */
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnPlayerDiedSignature OnPlayerDied;
};
|
cs |
그 다음 PrimaryAttributeSet에서 체력이 0 이하가 되는 순간, 기존처럼 단순히 죽었다는 사실만 알리는 것이 아니라 FGameplayEffectModCallbackData를 함께 전달하도록 수정했다.
PrimaryAttributeSet.cpp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
void UPrimaryAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
if (Data.EvaluatedData.Attribute == GetHealthAttribute())
{
SetHealth(FMath::Clamp(GetHealth(), 0, GetMaxHealth()));
}
if (Health.GetCurrentValue() <= 0.0f)
{
OnOutHealth.Broadcast(Data); // 체력이 0 이하일 때 델리게이트 호출
}
}
|
cs |
그리고 StateComponent에서는 이 데이터를 받아 실제 공격자를 추출한 뒤, PlayerBase의 OnPlayerDied 이벤트를 브로드캐스트하도록 했다.
StateComponent.cpp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
void UStateComponent::OnOutHealth(const struct FGameplayEffectModCallbackData& Data)
{
APlayerBase* Player = Cast<APlayerBase>(GetOwner());
if (!Player || bIsDead) return;
if (GetOwner()->HasAuthority())
{
bIsDead = true;
}
// 공격자 정보 추출
AActor* LastAttacker = nullptr;
if (Data.EffectSpec.GetContext().IsValid())
{
LastAttacker = Data.EffectSpec.GetContext().GetInstigator();
}
// PlayerBase의 이벤트 디스패처 호출
Player->OnPlayerDied.Broadcast(LastAttacker);
}
|
cs |
즉, 이번 구조에서는 체력 0 감지 -> 공격자 추출 -> 플레이어 사망 이벤트 전파라는 흐름이 명확해졌다.
5. PlayerState에서 경험치 페널티 계산
이번 구현의 핵심 로직은 PlayerStateBase에 들어간 경험치 차감 처리다.
플레이어가 사망하면 현재 레벨 기준 총 필요 경험치의 5%를 페널티로 잃도록 만들었다.
단, 여기서 고려해야 할 예외가 있다.
- 현재 레벨에서 가진 경험치가 충분하면 그대로 5% 차감
- 현재 레벨 진행 경험치가 5%보다 적으면 레벨 다운 필요
- 레벨 다운 후에도 남은 퍼센티지만큼 이전 레벨에서 추가 차감
- 1레벨에서는 더 이상 레벨이 내려가지 않도록 예외 처리
즉, 단순히 Experience -= X로 끝나는 구조가 아니라, 현재 레벨 진행량과 레벨 하락 가능 여부까지 함께 계산하는 방식이다.
먼저 PlayerBase에서 PlayerState가 사망 이벤트를 받을 수 있도록 바인딩해준다.
PlayerBase.cpp
|
1
2
3
4
5
6
7
8
9
10
11
|
void APlayerBase::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
APlayerStateBase* PS = GetPlayerState<APlayerStateBase>();
check(PS);
// ... ASC 및 다른 초기화 ...
// PlayerState가 사망 이벤트를 수신하여 UI 호출을 시작하도록 델리게이트를 바인딩
OnPlayerDied.AddDynamic(PS, &APlayerStateBase::HandleOwnerDeath);
}
|
cs |
그리고 PlayerStateBase에서 실제 경험치 페널티를 계산한다.
PlayerStateBase
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
PlayerStateBase.h
public:
/** 플레이어가 사망했을 때 호출될 함수 */
UFUNCTION()
void HandleOwnerDeath(AActor* LastAttacker);
PlayerStateBase.cpp
void APlayerStateBase::HandleOwnerDeath(AActor* LastAttacker)
{
// 서버에서만 경험치 감소 로직 실행
if (GetLocalRole() != ROLE_Authority)
{
return;
}
// 실제 차감될 경험치량과 퍼센티지 변수
int64 ActualExperienceLost = 0;
float ActualPercentageLost = 0.f;
// 현재 레벨의 총 필요 경험치량과 페널티(5%) 정의
const float PenaltyPercentage = 5.0f;
const int64 TotalExpAtDeathLevel = LevelEndExperience - LevelStartExperience;
if (TotalExpAtDeathLevel > 0)
{
// 현재 레벨 기준 잃어야 할 총 경험치 계산
const int64 TotalPenaltyExp = FMath::RoundToInt64(TotalExpAtDeathLevel * (PenaltyPercentage / 100.0f));
// 현재 레벨에서 보유한 경험치 계산
const int64 CurrentLevelProgressExp = Experience - LevelStartExperience;
// 현재 보유 경험치가 페널티보다 많으면 페널티만큼 깎고 종료
if (CurrentLevelProgressExp >= TotalPenaltyExp)
{
ActualExperienceLost = TotalPenaltyExp;
Experience -= TotalPenaltyExp;
ActualPercentageLost = PenaltyPercentage;
}
// 현재 보유 경험치가 페널티보다 적을 경우
else
{
ActualExperienceLost = CurrentLevelProgressExp;
const int32 NewLevel = FMath::Max(1, Level - 1);
// 1레벨이라 레벨 다운이 불가능한 경우
if (NewLevel == Level)
{
Experience = LevelStartExperience;
// 실제로 잃은 경험치만큼만 %를 계산해서 UI에 표시
ActualPercentageLost = (static_cast<float>(ActualExperienceLost) / TotalExpAtDeathLevel) * 100.0f;
}
// 레벨 다운이 가능한 경우
else
{
float PercentageLostThisLevel = (static_cast<float>(CurrentLevelProgressExp) / TotalExpAtDeathLevel) * 100.0f;
float RemainingPenaltyPercentage = PenaltyPercentage - PercentageLostThisLevel;
int64 PrevLevelStartExp = 0;
int64 PrevLevelEndExp = 0;
if (FindExperienceDataForLevel(NewLevel, PrevLevelStartExp, PrevLevelEndExp))
{
const int64 PrevLevelTotalExp = PrevLevelEndExp - PrevLevelStartExp;
const int64 PenaltyFromPrevLevel = FMath::RoundToInt64(PrevLevelTotalExp * (RemainingPenaltyPercentage / 100.0f));
int64 NewExperience = PrevLevelEndExp - PenaltyFromPrevLevel;
if (NewExperience < PrevLevelStartExp)
{
NewExperience = PrevLevelStartExp;
}
ActualExperienceLost = CurrentLevelProgressExp + (PrevLevelEndExp - NewExperience);
Level = NewLevel;
Experience = NewExperience;
LevelStartExperience = PrevLevelStartExp;
LevelEndExperience = PrevLevelEndExp;
}
// 레벨 다운이 발생했더라도 5% 감소 페널티는 같으므로 UI에는 5%를 표시
ActualPercentageLost = PenaltyPercentage;
}
}
}
// 변경된 경험치를 클라이언트에 동기화
Client_ReplicateExperience(Level, Experience, LevelStartExperience, LevelEndExperience);
// Pawn의 StateComponent를 직접 가져와 새로 만든 RPC 함수 호출
if (APlayerBase* Player = GetPawn<APlayerBase>())
{
if (Player->StateComp)
{
Player->StateComp->Client_ShowDeathUI(LastAttacker, ActualExperienceLost, ActualPercentageLost);
}
}
}
|
cs |
이 로직의 핵심은 현재 레벨 경험치가 부족하면 이전 레벨까지 거슬러 올라가서 남은 페널티를 계산한다는 점이다.
또한 1레벨에서는 더 이상 레벨이 내려가지 않도록 막아두었기 때문에, 하한선에서 오류가 나는 문제도 피할 수 있다.
6. StateComponent에서 최종 UI 생성
경험치 감소 계산까지 끝나면, 이제 남은 일은 플레이어 화면에 사망 UI를 띄우는 것이다.
이 역할은 StateComponent가 맡도록 했다.
즉, PlayerState는 계산만 하고, 실제 UI 생성과 표시에는 직접 관여하지 않는다.
StateComponent
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
StateComponent.h
public:
/** 공격자 정보, 경험치 페널티 정보를 파라미터로 받아 UI를 생성하고 내용을 채우는 함수 */
UFUNCTION(Client, Reliable)
void Client_ShowDeathUI(AActor* LastAttacker, int64 ActualExperienceLost, float ActualPercentageLost);
StateComponent.cpp
void UStateComponent::Client_ShowDeathUI_Implementation(AActor* LastAttacker, int64 ActualExperienceLost, float ActualPercentageLost)
{
if (DeathPopupWidget == nullptr) return;
APlayerControllerBase* PC = Cast<APlayerControllerBase>(GetOwner<APawn>()->GetController());
if (!PC) return;
// 서버에서는 이동 및 충돌 비활성화
if (UKismetSystemLibrary::IsServer(this))
{
if (ACharacter* Cha = Cast<ACharacter>(GetOwner()))
{
Cha->GetCharacterMovement()->SetMovementMode(MOVE_None);
Cha->SetActorEnableCollision(false);
}
}
UDeathPopup* Widget = CreateWidget<UDeathPopup>(PC, DeathPopupWidget);
if (Widget)
{
Widget->AddToViewport();
DeathPopup = Widget;
FText KillerNameText = FText::FromString(TEXT("Unknown"));
if (LastAttacker)
{
if (LastAttacker->GetClass()->ImplementsInterface(UDisplayNameProvider::StaticClass()))
{
KillerNameText = IDisplayNameProvider::Execute_GetDisplayName(LastAttacker);
}
else
{
KillerNameText = FText::FromString(LastAttacker->GetName());
}
}
DeathPopup->SetDeathInfo(KillerNameText, ActualExperienceLost, ActualPercentageLost);
if (DeathPopup->ReviveButton)
{
DeathPopup->ReviveButton->OnClicked.AddDynamic(this, &UStateComponent::Revive);
}
PC->SetInputMode(FInputModeUIOnly());
PC->bShowMouseCursor = true;
}
}
|
cs |
여기서는 마지막 공격자와 경험치 감소량, 퍼센티지를 전달받아 DeathPopup에 채워 넣고, 부활 버튼까지 연결해준다.
7. DeathPopup에서 공격자 이름과 경험치 손실 표시
사망 UI 위젯은 생성 시 애니메이션을 재생하고, 전달받은 데이터를 텍스트 블록에 넣어주는 역할을 한다.
DeathPopup.h
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Animation/WidgetAnimation.h"
#include "Components/Button.h"
#include "Components/TextBlock.h"
#include "DeathPopup.generated.h"
/**
*
*/
UCLASS()
class PROJECTJIN_API UDeathPopup : public UUserWidget
{
GENERATED_BODY()
protected:
virtual void NativeConstruct() override;
UPROPERTY(Transient, meta = (BindWidgetAnim))
TObjectPtr<UWidgetAnimation> PlayerDeathUIAnimation;
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (BindWidget))
TObjectPtr<UButton> ReviveButton;
/** 공격자 이름을 표시할 텍스트 블록 */
UPROPERTY(meta = (BindWidget))
TObjectPtr<UTextBlock> KillerNameText;
/** 잃은 경험치량을 표시할 텍스트 블록 */
UPROPERTY(meta = (BindWidget))
TObjectPtr<UTextBlock> LostExperienceText;
/** 잃은 경험치 퍼센티지를 표시할 텍스트 블록 */
UPROPERTY(meta = (BindWidget))
TObjectPtr<UTextBlock> LostExperiencePercentageText;
/** 공격자 이름, 경험치 페널티 정보를 한 번에 받아 UI에 설정하는 함수 */
void SetDeathInfo(const FText& KillerName, int64 ActualExperienceLost, float ActualPercentageLost);
};
|
cs |
DeathPopup.cpp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
#include "DeathPopup.h"
void UDeathPopup::NativeConstruct()
{
Super::NativeConstruct();
if (PlayerDeathUIAnimation)
{
PlayAnimation(PlayerDeathUIAnimation);
}
}
void UDeathPopup::SetDeathInfo(const FText& KillerName, int64 ActualExperienceLost, float ActualPercentageLost)
{
// 공격자 이름 텍스트 설정
if (KillerNameText)
{
FText KillerFormat = FText::FromString(TEXT("{0}의 공격"));
FText FormattedKillerText = FText::Format(KillerFormat, KillerName);
KillerNameText->SetText(FormattedKillerText);
}
// 잃어버린 경험치 텍스트 설정
if (LostExperienceText)
{
if (ActualExperienceLost <= 0)
{
LostExperienceText->SetText(FText::FromString(TEXT("-")));
}
else
{
LostExperienceText->SetText(FText::AsNumber(ActualExperienceLost));
}
}
// 잃어버린 경험치 퍼센티지 텍스트 설정
if (LostExperiencePercentageText)
{
if (ActualExperienceLost <= 0)
{
LostExperiencePercentageText->SetText(FText::FromString(TEXT("-%")));
}
else
{
int32 PercentageAsInt = FMath::RoundToInt(ActualPercentageLost);
FText ExpPercentFormat = FText::FromString(TEXT("{0}%"));
FText FormattedExpPercentText = FText::Format(ExpPercentFormat, FText::AsNumber(PercentageAsInt));
LostExperiencePercentageText->SetText(FormattedExpPercentText);
}
}
}
|
cs |
이렇게 하면 플레이어는 사망 시점에 누가 죽였는지, 얼마의 경험치를 잃었는지, 몇 퍼센트 손실이 발생했는지를 즉시 확인할 수 있다.
8. 내부 이름 대신 실제 표시용 이름을 보여주기 위해 인터페이스 추가
구현을 마치고 PIE에서 테스트해보면, 처음에는 공격자 이름이 BP_Enemy_C_0처럼 내부 오브젝트 이름으로 출력되는 문제가 있었다.
하지만 실제 게임에서 이런 식으로 출력되면 사용자 입장에서는 의미가 없다.
그래서 공격자의 실제 표시용 이름을 일관되게 가져올 수 있는 인터페이스를 추가했다.
이 방식의 장점은 명확하다.
- StateComponent는 공격자의 구체적인 클래스 타입을 알 필요가 없음
- 적, 다른 플레이어, NPC 등 다양한 공격자 타입에 같은 방식 적용 가능
- UI 표시용 이름을 각 클래스가 직접 제공할 수 있음
DisplayNameProvider.h
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "DisplayNameProvider.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UDisplayNameProvider : public UInterface
{
GENERATED_BODY()
};
/**
* 플레이어 사망 UI에 공격자에 대한 이름을 표시하기 위한 인터페이스
*/
class MyGame_API IDisplayNameProvider
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
/** UI에 표시할 이름 반환; 각 클래스에서 override해야함 */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Display Name")
FText GetDisplayName() const;
};
|
cs |
그리고 우선 기본 적들의 부모 클래스인 EnemyBase에 이 인터페이스를 추가했다.
EnemyBase
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
EnemyBase.h
#include "MyGame/Interface/DisplayNameProvider.h" //
.
.
.
UCLASS()
class MyGame_API AEnemyBase : public ACharacterBase, public IAbilitySystemInterface, public IEnemyAI,
public IAttackResponse, public ICombatStateHandler, public ICustomIDProvider,
public ICheckEnemyDeath, public IDisplayNameProvider // 추가
{
GENERATED_BODY()
.
.
.
public:
/** IDisplayNameProvider; 플레이어 사망 UI에 공격자 정보를 띄위기 위해 추가 */
virtual FText GetDisplayName_Implementation() const override;
}
EnemyBase.cpp
FText AEnemyBase::GetDisplayName_Implementation() const
{
return EnemyName;
}
|
cs |
마지막으로 StateComponent가 UI에 공격자 이름을 넣을 때, 이 인터페이스를 구현하고 있는지 확인한 뒤 GetDisplayName()을 호출하도록 수정했다.
즉, 인터페이스가 있으면 표시용 이름 사용, 없으면 기본 오브젝트 이름 사용이라는 안전한 fallback 구조를 만든 셈이다.
9. 실제 결과
이제 PIE에서 다시 확인해보면, 플레이어가 사망했을 때 사망 UI에 공격자 이름이 정상적으로 표시되고, 동시에 경험치가 차감되는 것도 확인할 수 있다.
예를 들어 경험치가 31%에서 26%로 내려가는 식으로, 현재 레벨 기준 5% 페널티가 적용되는 것을 직접 확인할 수 있다.
즉, 이번 구현은 아래 요소를 모두 만족하게 되었다.
- 마지막 공격자 이름 표시
- 잃은 경험치량 표시
- 잃은 퍼센티지 표시
- 경험치 부족 시 레벨 다운 처리
- 1레벨 예외 처리
- 다양한 공격자 타입에 대응 가능한 이름 표시 구조

이번 작업은 단순한 사망 UI 추가가 아니라, 플레이어 사망 시점의 이벤트 흐름과 데이터 계산 구조를 재정리한 작업에 가깝다.
정리하면 이번 구현의 핵심은 아래와 같다.
- PrimaryAttributeSet에서 체력 0 감지
- StateComponent에서 공격자 추출 후 사망 이벤트 전파
- PlayerBase에서 공개 델리게이트 제공
- PlayerStateBase에서 서버 기준 경험치 페널티 계산
- StateComponent에서 클라이언트 UI 생성
- DisplayNameProvider 인터페이스로 공격자 표시 이름 일관성 확보
이번 구현은 단순한 UI 표시를 넘어, 사망 이벤트 설계, 경험치 페널티 계산, 레벨 다운 예외 처리, 서버/클라이언트 역할 분리, 인터페이스 기반 이름 추상화까지 함께 보여줄 수 있다는 점에서 의미가 있다.
'Unreal Engine > Functional Implementation' 카테고리의 다른 글
| 언리얼 엔진 GAS 텔레포트 구현 - UI 버튼으로 지정 위치 이동하기 (0) | 2025.09.16 |
|---|---|
| 언리얼 엔진 NPC 대화 UI 애니메이션 구현 - 열기, 닫기, 다음 대사 버튼 효과 추가 (0) | 2025.09.09 |
| ClassComponent 추가 변동 사항; 매개변수 추가 (0) | 2025.09.03 |
| 언리얼 엔진 스킬 해금 UI 구현 - 레벨업 시 신규 스킬 정보를 위젯에 표시하기 (0) | 2025.09.01 |
| 언리얼 엔진 신규 스킬 해금 UI 구현 - 위젯 출력과 스킬창 알림 배지 만들기 (5) | 2025.08.25 |