44.Weapon Class

45.PickupWidget

46.Variable Replication

 

아래 내용은 3편(44~46강)의 연속 강의를 한 편의 블로그 글처럼 정리한 것입니다.
무기(Weapon) 클래스를 만들고, 픽업(Pickup) 위젯을 통해 무기를 집을 수 있는 UI를 표시하며,
변수 복제(Replication)를 통해 서버-클라이언트 간 동기화를 어떻게 처리하는지에 대한 강의 내용입니다.


목차

  1. 무기(Weapon) 클래스 생성 및 기본 구성
  2. 픽업(Pickup) 위젯 만들기
  3. 변수 복제(Replication)로 무기 상태 동기화
  4. 마무리 및 팁

1. 무기(Weapon) 클래스 생성 및 기본 구성

1.1 무기 클래스 생성

  • 새 C++ 클래스를 생성하고, 부모 클래스로 AActor를 선택합니다.
  • 폴더 구조상 Weapon 전용 폴더를 만들어 관리하면 추후 여러 무기 파생 클래스 작성 시 편리합니다.
// Weapon.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Weapon.generated.h"

UENUM(BlueprintType)
enum class EWeaponState : uint8
{
    EWS_Initial UMETA(DisplayName="Initial State"),
    EWS_Equipped UMETA(DisplayName="Equipped"),
    EWS_Dropped UMETA(DisplayName="Dropped"),

    EWS_MAX UMETA(DisplayName="Default MAX")
};

UCLASS()
class AWeapon : public AActor
{
    GENERATED_BODY()

public:
    AWeapon();

protected:
    virtual void BeginPlay() override;

private:
    // 무기 메시에 사용할 스켈레탈 메시
    UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")
    USkeletalMeshComponent* WeaponMesh;

    // 플레이어가 일정 거리 내에 들어올 때 무기를 집을 수 있도록 감지하는 구체
    UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")
    class USphereComponent* AreaSphere;

    // 무기의 현재 상태
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Weapon Properties", meta = (AllowPrivateAccess = "true"))
    EWeaponState WeaponState;

public:
    // 무기 상태 제어 함수 등 추후 구현
};

구성 요소 설명

  • USkeletalMeshComponent* WeaponMesh: 실제 무기의 형태(골격)를 표시.
  • USphereComponent* AreaSphere: 플레이어와의 겹침(Overlap)을 감지하는 역할.
  • EWeaponState: 무기가 현재 “바닥에 놓여있음(Initial)”, “장착됨(Equipped)”, “버려짐(Dropped)” 등의 상태를 표현.

1.2 Weapon.cpp 주요 초기 설정

AWeapon::AWeapon()
{
    // 무기 메시 초기화
    WeaponMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WeaponMesh"));
    SetRootComponent(WeaponMesh);
    
    // 필요에 따라 충돌 여부 설정
    WeaponMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
    WeaponMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);

    // 구체 컴포넌트 초기화
    AreaSphere = CreateDefaultSubobject<USphereComponent>(TEXT("AreaSphere"));
    AreaSphere->SetupAttachment(RootComponent);
    
    // 구체는 기본적으로 모든 충돌을 무시하도록 설정한 뒤, 
    // 서버에서만 실제 겹침을 활성화할 수 있도록 구성(멀티플레이 고려)
    AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
    AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);

    WeaponState = EWeaponState::EWS_Initial;
}
  • 멀티플레이 상황을 고려해, 충돌이나 겹침은 서버 권한(HasAuthority())이 있을 때만 활성화하도록 두는 편이 안전합니다.
  • 무기 상태(WeaponState)는 초기화할 때 기본적으로 EWS_Initial로 설정.

1.3 무기 블루프린트(BP) 설정

  1. 언리얼 에디터에서 Weapon C++ 클래스를 기반으로 한 **블루프린트(BP)**를 만듭니다.
  2. 무기 메시(Skeletal Mesh)로 실제 총기(예: 돌격소총, 샷건 등)를 할당합니다.
  3. AreaSphere의 Sphere Radius를 적당히 조절하여, 플레이어가 가까워졌을 때 감지하도록 설정합니다.

이렇게 하면 레벨 위에 해당 BP 무기를 배치해둘 수 있고, 플레이어 캐릭터가 다가오면(겹침되면) 무기를 주울 수 있는 로직을 구현할 수 있게 됩니다.


2. 픽업(Pickup) 위젯 만들기

무기가 바닥에 떨어져 있을 때, 플레이어가 가까이 가면 “E 키를 눌러 주울 수 있음” 같은 안내 메시지를 표시하는 UI가 필요합니다. 이를 위해 Widget Blueprint를 사용합니다.

2.1 새 위젯 블루프린트 생성

  • HUD 관련 폴더(혹은 원하는 폴더)에서 우클릭 → User Interface → Widget Blueprint를 생성하고 이름을 WBP_PickupWidget 등으로 설정합니다.
  • 캔버스 위젯 대신 텍스트 블록 하나만 두고, "Press E to Pick Up"처럼 안내 문구를 작성합니다.
    • 폰트 크기나 정렬 등을 취향껏 조절합니다.

2.2 Weapon 클래스에 UWidgetComponent 추가

// Weapon.h (일부)
private:
    UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")
    class UWidgetComponent* PickupWidget;

// Weapon.cpp (생성자에서)
PickupWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("PickupWidget"));
PickupWidget->SetupAttachment(RootComponent);
PickupWidget->SetVisibility(false); // 처음엔 보이지 않게
  • UWidgetComponent를 통해 3D 월드 공간에서도 UI를 띄울 수 있습니다.
  • 위젯 클래스로 우리가 만든 WBP_PickupWidget을 할당해 주면, 무기 위에 "Press E to Pick Up" 라벨이 뜨게 됩니다(가시성 활성화 시).

2.3 구체 겹침(Overlap)으로 위젯 제어

  • 플레이어가 AreaSphere에 들어오면 Widget 가시성을 true로, 벗어나면 false로 설정.
  • 다만 이 겹침은 서버 권한에서만 감지하게(멀티플레이 기준) 설계하는 것이 일반적입니다.
    • 서버에서만 “캐릭터가 무기에 겹쳤다”는 로직이 유효하게 동작 → 이후 필요한 변수(예: ‘현재 겹치는 무기’)를 서버-클라이언트 간에 복제.

3. 변수 복제(Replication)로 무기 상태 동기화

멀티플레이어 환경에서는 “서버에서만 중첩(Overlap) 이벤트 발생 → 해당 정보를 클라이언트와 동기화”가 핵심입니다.
언리얼에서 RepNotify(= Replicated Using) 기능과 관련 매크로들을 활용해 쉽게 설정할 수 있습니다.

3.1 캐릭터에 “현재 겹치는 무기” 변수 추가

플레이어 캐릭터 클래스(BlasterCharacter 등)에 다음과 같은 변수를 둡니다.

// BlasterCharacter.h
private:
    UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon)
    class AWeapon* OverlappingWeapon;

UFUNCTION()
void OnRep_OverlappingWeapon(); // RepNotify 함수
  • ReplicatedUsing을 통해 OverlappingWeapon이 서버에서 변경될 때, 클라이언트에서 복제된 후 자동으로 OnRep_OverlappingWeapon()이 호출됩니다.
  • 즉, “어떤 무기가 겹치고 있는지”를 서버가 결정하고, 그 결과만 클라이언트에 알려주는 방식.

3.2 RepNotify 함수 구현

// BlasterCharacter.cpp
void ABlasterCharacter::OnRep_OverlappingWeapon()
{
    if (OverlappingWeapon)
    {
        OverlappingWeapon->ShowPickupWidget(true); 
    }
    else
    {
        // 이전 무기를 숨기고 싶다면, 과거 OverlappingWeapon 정보도 확인 가능
        // 혹은 별도 로직으로 현재 UI 숨김 처리
    }
}
  • OverlappingWeapon이 Null이 아닌 경우 → 클라이언트 측에서 “무기를 집을 수 있음” 위젯을 표시.
  • Null이 된 경우(겹침 해제) → 더 이상 표시하지 않음.

3.3 변수 등록(도입) & 조건부 복제

  • 언리얼은 GetLifetimeReplicatedProps 함수를 오버라이드하여 어떤 변수를 복제할지 지정합니다.
  • 조건부 복제(예: COND_OwnerOnly)를 사용하면, 해당 무기를 겹치는 플레이어(소유자) 만 변수 정보를 받도록 할 수 있습니다.
// BlasterCharacter.cpp
void ABlasterCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);

    // OverlappingWeapon 변수를 소유자(Owner)에게만 복제
    DOREPLIFETIME_CONDITION(ABlasterCharacter, OverlappingWeapon, COND_OwnerOnly);
}
  • 이렇게 하면 다른 클라이언트에게는 “내 캐릭터가 어떤 무기를 겹치는지” 정보를 굳이 보낼 필요가 없어, 네트워크 트래픽을 절감할 수 있습니다.

4. 마무리 및 팁

  1. Weapon 클래스
    • Skeletal Mesh, Sphere Overlap, WidgetComponent를 기본으로 두고,
    • EWeaponState 같은 열거형으로 무기 상태를 구분하면 확장성이 좋습니다.
  2. Pickup Widget
    • UWidgetComponent로 월드 공간에 위젯 표시.
    • 겹침 이벤트 발생 시 Widget 가시성 활성화.
  3. 변수 복제(Replication)
    • 서버에서만 상태 결정” 후, 필요한 정보만 “클라이언트에게 복제”하는 원칙이 중요.
    • RepNotify를 통해 클라이언트 측에서 수신 즉시 UI를 켜거나 끄는 식으로 처리.
    • COND_OwnerOnly 등 조건부 복제를 활용해 불필요한 트래픽을 줄임.

추가 팁

  • Physics & Collision:
    무기를 “버리거나(Dropped)” 상태일 때 물리로 굴러다니도록 하려면, WeaponMesh->SetSimulatePhysics(true)와 적절한 충돌 채널 설정이 필요합니다.
  • 상태별 분기:
    무기 상태 EWeaponState를 활용해, Equipped이면 플레이어 손에 붙이고, Dropped이면 땅에 떨어뜨리고, Initial이면 바닥에 고정해두는 식으로 분기 처리할 수 있습니다.

시각 자료 제안

  1. 클래스 다이어그램
    • BlasterCharacter ↔ AWeapon (OverlappingWeapon 포인터 관계)
    • AWeapon 내부 구성(스켈레탈 메시, 구체 컴포넌트, 위젯 컴포넌트)
  2. 변수 복제 흐름도
    • 서버 권한에서 OverlappingWeapon 설정 → 클라이언트에 RepNotify 호출 → 위젯 표시
  3. 코드 스니펫 표 정리파일 주요 코드 설명
    Weapon.h USkeletalMeshComponent* WeaponMesh; 등 무기 구성 요소 및 EWeaponState
    BlasterCharacter.h AWeapon* OverlappingWeapon; 현재 겹치는 무기에 대한 포인터(복제)

결론

이번 강의 시리즈(무기 클래스 구현 → 픽업 위젯 표시 → 변수 복제)는 언리얼 엔진에서의 멀티플레이 프로그래밍 기초를 잘 보여줍니다.

  • 무기 클래스: Skeletal Mesh, 충돌 감지 구체, UI 위젯 컴포넌트를 구성
  • 픽업 위젯: 겹침 시 플레이어에게 “E 키로 픽업” 메시지 제공
  • Replication(변수 복제): 서버에서만 무기 겹침 로직을 처리하고, 결과를 소유한 클라이언트로 복제하여 UI를 표시

여기까지 따라오셨다면,
이후에는 무기 장착, 사격 로직, 물리 시뮬레이션 등을 추가해 더 다양한 게임플레이를 구현할 수 있습니다.
다음 단계로 넘어가기 전, 지금까지 구성한 시스템이 잘 동작하는지 충분히 테스트해 보시길 추천드립니다.


감사합니다!
더 궁금한 점이나, 다른 멀티플레이 관련 주제가 있다면 댓글로 알려주세요.

'UnraealEngine > Multiplayer Shooter' 카테고리의 다른 글

47. Equipping Weapons  (0) 2025.03.13
46.Variable Replication  (0) 2025.03.13
41.Seamless Travel and Lobby  (0) 2025.03.12
42.Network Role  (0) 2025.03.12
25. Join Sessions from the Menu  (0) 2025.03.12

+ Recent posts