25.09.11 변동 사항
PlayerBase의 멤버 변수를 제거하고, PrimaryAttribute에 있던 공격자에 관한 데이터를 받아오는 Delegate로 수정하여 Player를 사망하게 한 공격자에 관한 정보를 Player 사망 UI에 전달하도록 해주었다.
우선 PrimaryAttributeSet에 있는 Delegate에서 DYNAMIC을 제거하여 표준 C++ 델리게이트로 변경해주었다.
PrimaryAttributeSet
|
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
|
PrimaryAttributeSet.h
/** 델리게이트가 공격자 정보가 포함된 FGameplayEffectModCallbackData를 전달하도록 변경 */
DECLARE_MULTICAST_DELEGATE_OneParam(FOnOutHealth, const FGameplayEffectModCallbackData& /*Data*/);
PrimaryAttributeSet.cpp
void UPrimaryAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
UAbilitySystemComponent* ASC = GetOwningAbilitySystemComponent();
// 이하 제거
//if (Data.EvaluatedData.Attribute.GetUProperty() == FindFieldChecked<FProperty>(UPrimaryAttributeSet::StaticClass(), GET_MEMBER_NAME_CHECKED(UPrimaryAttributeSet, Health)))
//{
// // 이펙트 정보에서 공격자를 가져옴
// AActor* Instigator = nullptr;
// if (Data.EffectSpec.GetContext().IsValid())
// {
// Instigator = Data.EffectSpec.GetContext().GetInstigator();
// }
// // 소유자(플레이어)에 마지막 공격자 정보를 저장
// if (Instigator)
// {
// AActor* OwnerActor = ASC->GetAvatarActor();
// if (APlayerBase* Player = Cast<APlayerBase>(OwnerActor))
// {
// Player->LastAttacker = Instigator;
// }
// }
//}
if (Data.EvaluatedData.Attribute == GetHealthAttribute())
{
SetHealth(FMath::Clamp(GetHealth(), 0, GetMaxHealth()));
UE_LOG(LogKWH, Warning, TEXT("[%s] Health : %f"), *ASC->GetAvatarActor()->GetName(), GetHealth())
}
.
.
.
}
|
cs |
불필요해진 LastAttacker 멤버 변수를 PlayerBase에서 일단 제거해주고, 기존에 PlayerBase에서 받던 Delegate를 StateComponent에서 받도록 수정해주었다.
그리고 기존에 작성했던 Delegate가 바뀌었으므로, StateComponent에서 추가해주었던 Delegate를 기존에 사용하던 AddDynamic 대신 AddUObject로 수정해주었다.
아래 두 코드의 수정 사항이 모두 적혀있다.
PlayerBase
|
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
|
PlayerBase.h
// LastAttacker 변수 제거
// UPROPERTY(BlueprintReadOnly, Category = "Combat")
// TObjectPtr<AActor> LastAttacker;
PlayerBase.cpp
void APlayerBase::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
APlayerStateBase* PS = GetPlayerState<APlayerStateBase>();
check(PS);
ASC = PS->GetAbilitySystemComponent();
ASC->InitAbilityActorInfo(PS, this);
// 기본 시작 어빌리티 부여
for (const auto& StartAbility : StartAbilities)
{
FGameplayAbilitySpec StartSpec(StartAbility);
ASC->GiveAbility(StartSpec);
}
// 캐릭터 사망 관련 바인딩
UPrimaryAttributeSet* PrimaryAttributes = PS->GetAttributeSet();
check(PrimaryAttributes);
// StateComponent가 스스로 바인딩하도록 위임
StateComp->BindToAttributeSet(PrimaryAttributes);
.
.
.
}
|
cs |
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
|
StateComponent.h
public:
/** AttributeSet OnOutHealth 델리게이트에 컴포넌트의 OnOutHealth를 바인딩하기 위한 함수 */
void BindToAttributeSet(class UPrimaryAttributeSet* Attr);
StateComponent.cpp
void UStateComponent::BindToAttributeSet(UPrimaryAttributeSet* Attr)
{
if (!Attr) return;
Attr->OnOutHealth.AddUObject(this, &UStateComponent::OnOutHealth); // AddDynamic 대신 AddUObject 사용
}
void UStateComponent::OnOutHealth(const FGameplayEffectModCallbackData& Data) // 변수
{
APlayerBase* Player = Cast<APlayerBase>(GetOwner());
if (!Player || bIsDead) return;
if (GetOwner()->HasAuthority())
{
bIsDead = true;
}
// Data에서 공격자 정보를 즉시 추출
AActor* Attacker = nullptr;
if (Data.EffectSpec.GetContext().IsValid())
{
Attacker = Data.EffectSpec.GetContext().GetInstigator();
}
// PlayerBase의 OnPlayerDied 이벤트를 통해 파라미터로 전달
Player->OnPlayerDied.Broadcast(Attacker);
}
|
cs |
25.09.29 변동 사항
기존 코드는 Player의 체력이 0이 되는 즉시 HandleOwnerDeath 함수가 호출되어 경험치 감소 로직을 처리하고, 곧바로 클라이언트에 RPC를 보내 사망 UI를 띄우는 구조였는데, 이를 Player가 사망하고 1.5초 뒤에 사망 UI가 출력되도록 수정함
Player 사망 함수를 처리하던 StateComponent에서 타이머를 사용해 구현
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
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
103
104
105
106
|
StateComponent.h
.
.
.
public:
.
.
.
/** 공격자 정보, 경험치 페널티 정보를 파라미터로 받아 1.5초 후 UI를 생성하고 내용을 채우는 함수 */
UFUNCTION(Client, Reliable)
void Client_ShowDeathUI(AActor* LastAttacker, int64 ActualExperienceLost, float ActualPercentageLost);
.
.
.
private:
.
.
.
/** 타이머 만료 시 사망 UI 생성 함수 */
void ShowDeathPopup();
.
.
.
/** Death UI 출력 지연을 위한 타이머 핸들 */
FTimerHandle DeathUITimerHandle;
/** Death UI에 표시할 공격자 정보; RPC로 받은 정보 임시 저장용 */
UPROPERTY()
TObjectPtr<AActor> StoredLastAttacker;
/** Death UI에 표시할 손실 경험치량; RPC로 받은 정보 임시 저장용 */
int64 StoredExperienceLost;
/** Death UI에 표시할 손실 경험치 퍼센티지; RPC로 받은 정보 임시 저장용 */
float StoredPercentageLost;
};
StateComponent.cpp
#include "TimerManager.h" // 추가
.
.
.
void UStateComponent::Client_ShowDeathUI_Implementation(AActor* LastAttacker, int64 ActualExperienceLost, float ActualPercentageLost)
{
// 기존 UI 생성 코드는 ShowDeathPopup으로 이동
// UI에 필요한 정보들을 멤버 변수에 임시 저장
StoredLastAttacker = LastAttacker;
StoredExperienceLost = ActualExperienceLost;
StoredPercentageLost = ActualPercentageLost;
// 1.5초 뒤에 ShowDeathPopup 함수를 호출하는 타이머 설정
GetWorld()->GetTimerManager().SetTimer(
DeathUITimerHandle,
this,
&UStateComponent::ShowDeathPopup,
1.5f,
false);
}
void UStateComponent::ShowDeathPopup()
{
// 함수의 내용은 기존 Client_ShowDeathUI_Implementation에 있던 UI 생성 코드와 동일
// UI 정보를 설정할 때 파라미터 대신 임시 저장해둔 멤버 변수(StoredLastAttacker 등) 사용
if (DeathPopupWidget == nullptr) return;
APlayerControllerBase* PC = Cast<APlayerControllerBase>(GetOwner<APawn>()->GetController());
if (!PC) return;
// ... 서버 전용 로직 생략 ...
UDeathPopup* Widget = CreateWidget<UDeathPopup>(PC, DeathPopupWidget);
if (Widget)
{
Widget->AddToViewport();
DeathPopup = Widget;
FText KillerNameText = FText::FromString(TEXT("Unknown"));
if (StoredLastAttacker) // 임시 저장된 변수 사용
{
// ... 공격자 이름 가져오는 로직 ...
}
// 임시 저장된 변수를 사용해 UI 내용 설정
DeathPopup->SetDeathInfo(KillerNameText, StoredExperienceLost, StoredPercentageLost);
if (DeathPopup->ReviveButton)
{
DeathPopup->ReviveButton->OnClicked.AddDynamic(this, &UStateComponent::Revive);
}
PC->SetInputMode(FInputModeUIOnly());
PC->bShowMouseCursor = true;
}
}
|
cs |
위와 같이 수정만 해주면 Player가 사망하고 1.5초 뒤에 UI가 뜨게 됨

'Unreal Engine > Functional Implementation' 카테고리의 다른 글
| 언리얼 엔진 레벨 전환 데이터 관리 - GameInstance와 GameMode 역할 정리 (0) | 2025.10.27 |
|---|---|
| 언리얼 엔진 파괴 가능한 오브젝트 구현 - 폭발, 파편, 범위 대미지 만들기 (0) | 2025.10.21 |
| 언리얼 엔진 플레이어 체력 경고 UI 구현 - HP 20% 이하일 때 Widget과 사운드 출력 (0) | 2025.09.29 |
| Ultimate Scan Effects and Screen Effects 추가 변동 사항 (0) | 2025.09.29 |
| Teleport 구현 추가 변동 사항 (1) | 2025.09.19 |