본문으로 건너뛰기
Unreal Engine 멀티플레이LESSON 24

체력, 점수, 킬 로그 동기화하기

난이도입문 → 초급
예상 시간95분
선수지식23강의 서버 권한 발사·데미지

24강. 체력, 점수, 킬 로그 동기화하기

1. 이번 강의에서 해결할 문제​

체력은 맞은 Pawn, 킬 수는 공격 플레이어, 팀 점수와 킬 로그는 경기 전체에 속합니다. 이 값을 Character 하나에 넣으면 Respawn에서 사라지고 모든 내용을 Multicast로 보내면 늦은 참가와 유실에서 불일치합니다. 상태의 소유 단위를 나눠야 합니다.

2. 학습 목표​

3. 핵심 개념​

체력은 현재 Pawn 수명에 속하고 모든 관찰자가 필요할 수 있어 Component RepNotify가 적합합니다. 킬은 플레이어가 Respawn해도 유지돼 PlayerState에 둡니다. 팀 점수와 최근 공용 사건은 GameState가 전달합니다. ApplyDamage와 OnTakeAnyDamage 처리는 서버에서만 권위 있게 실행합니다.

고빈도 킬 로그에는 Fast Array가 유용하지만 이 과정에서는 2인 게임의 가장 최근 사건 하나와 Sequence를 복제해 기반만 익힙니다. Fast Array는 32강에서 심화 과정으로 연결합니다.

4. 단계별 실습​

CaptureHealthComponent.h
UCLASS(ClassGroup = "Capture", meta = (BlueprintSpawnableComponent))
class CAPTUREARENA_API UCaptureHealthComponent : public UActorComponent
{
GENERATED_BODY()

public:
UCaptureHealthComponent();
virtual void GetLifetimeReplicatedProps(
TArray<FLifetimeProperty>& OutLifetimeProps) const override;

protected:
virtual void BeginPlay() override;

UPROPERTY(ReplicatedUsing = OnRep_Health)
float Health = 100.0f;

UPROPERTY(EditDefaultsOnly)
float MaxHealth = 100.0f;

UFUNCTION() void OnRep_Health(float PreviousHealth);
UFUNCTION() void HandleDamage(AActor*, float Damage,
const UDamageType*, AController* InstigatorController, AActor*);

private:
bool bDeathHandled = false;
};
CaptureHealthComponent.cpp
UCaptureHealthComponent::UCaptureHealthComponent()
{
PrimaryComponentTick.bCanEverTick = false;
SetIsReplicatedByDefault(true);
}

void UCaptureHealthComponent::BeginPlay()
{
Super::BeginPlay();
if (GetOwner() && GetOwner()->HasAuthority())
{
Health = MaxHealth;
GetOwner()->OnTakeAnyDamage.AddDynamic(
this, &UCaptureHealthComponent::HandleDamage);
}
}

void UCaptureHealthComponent::HandleDamage(
AActor*, float Damage, const UDamageType*,
AController* InstigatorController, AActor*)
{
if (!GetOwner()->HasAuthority() || Damage <= 0.0f || bDeathHandled) return;

const float Previous = Health;
Health = FMath::Clamp(Health - Damage, 0.0f, MaxHealth);
OnRep_Health(Previous);

if (Previous > 0.0f && Health <= 0.0f)
{
bDeathHandled = true;
NotifyGameModeOfDeath(InstigatorController);
}
}

void UCaptureHealthComponent::OnRep_Health(float PreviousHealth)
{
OnHealthChanged.Broadcast(Health, MaxHealth);
}

void UCaptureHealthComponent::GetLifetimeReplicatedProps(
TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UCaptureHealthComponent, Health);
}
CaptureArenaGameMode.cpp
void ACaptureArenaGameMode::HandlePlayerKilled(
AController* Victim, AController* Killer)
{
if (!HasAuthority() || !Victim) return;

ACaptureArenaPlayerState* KillerState =
Killer ? Killer->GetPlayerState<ACaptureArenaPlayerState>() : nullptr;
ACaptureArenaPlayerState* VictimState =
Victim->GetPlayerState<ACaptureArenaPlayerState>();

if (KillerState && Killer != Victim)
{
KillerState->AddKillOnServer();
GetGameState<ACaptureArenaGameState>()->AddTeamScore(
KillerState->GetTeamId(), 1);
}

GetGameState<ACaptureArenaGameState>()->SetLatestKillEvent(
KillerState, VictimState);
RestartPlayerAfterDelay(Victim);
}

킬 이벤트 구조에는 표시용 PlayerState 참조 또는 안전한 표시 이름과 증가 Sequence를 둡니다. 실명·플랫폼 계정 ID를 사용하지 않고 게임 내 닉네임 정책을 적용합니다.

5. 코드가 동작하는 이유​

HealthComponent는 서버 OnTakeAnyDamage에서 한 번만 0 전이를 확정합니다. GameMode가 킬 규칙을 실행하고 PlayerState·GameState Setter가 서버 값을 변경합니다. 각 클라이언트는 RepNotify와 PlayerState/GameState를 읽어 HUD를 갱신하며 Multicast 없이도 최신 체력·점수에 수렴합니다.

6. 자주 하는 실수와 해결법​

7. 직접 실습​

8. 이해 점검 질문 3개​

9. 핵심 요약​

MINI QUIZ

체력, 점수, 킬 로그 동기화하기 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1다음 중 “체력, 점수, 킬 로그 동기화하기”의 핵심 요약을 실제 상황에 맞게 적용한 것은 무엇인가요?
  2. 문제 2‘킬 수를 Character에 저장’ 실수를 판단할 때 “체력, 점수, 킬 로그 동기화하기” 강의가 제시한 기준은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

24강. 체력, 점수, 킬 로그 동기화하기 미완료 상태