💭회고
오늘은 언리얼 엔진을 활용한 무기 시스템 구현해보았다. 총기 탄약 UI 연동, 실시간 탄약 정보 업데이트, 총알 아이콘의 연동, 그리고 줌인 시 크로스헤어 표시 기능을 단계별로 구현해보았다...!
이를 통해 HUD와 게임 로직을 분리하여 델리게이트를 통한 통신 방식을 적극 활용하면, 유지보수와 확장성을 높일 수 있다는 것을 배웠다.
🗺️마인드맵
📒학습 내용
1. 무기 탄약 UI 연동 및 기본 초안
1.1 초안 구성 및 CWeapon.h
- 무기 클래스 확장: 기존 ACWeapon 클래스를 기반으로 탄약 관련 멤버와 HUD 연동용 getter 함수를 추가했다.
- 핵심 코드:
- GetMaxMagazineCount(), GetCurrentMagazineCount()를 통해 탄약 정보를 반환하며, HUD 연동을 위해(편의를 위해) 별칭 함수인 GetMaxAmmo()와 GetCurrentAmmo()를 사용했다.
1.2 CWeaponComponent.h / .cpp
- 무기 타입 및 탄약 정보 관리:
- 무기 종류(라이플, 피스톨, 칼 등)를 구분하고, 해당 무기별로 탄약 정보와 UI 업데이트 델리게이트를 정의!
- 델리게이트(FAmmoChanged)를 통해 탄약 정보가 변경될 때마다 HUD에 내용을 전달했다.
- 코드 활용:
- 무기 장착, 발사, 재장전 시 각각 최신 탄약 정보를 HUD에 즉시 전달했다.
- 핵심 요약:
- 무기 컴포넌트는 각 무기의 상태를 관리하며, 탄약 업데이트를 위한 델리게이트를 활용하여 UI와의 연동을 실시간으로 수행한다.
더보기

.h

.cpp

.cpp

class ACWeapon;
class ACCharacter;
UENUM(meta = (BlueprintSpawnableComponent))
enum class EWeaponType : uint8
{
Rifle, Pistol, Knife, Max
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FWeaponTypeChanged, EWeaponType, InPrevType, EWeaponType, InNewType);
// 탄약 정보 변경 델리게이트 선언 (현재 탄약, 최대 탄약)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAmmoChanged, int32, CurrentAmmo, int32, MaxAmmo);
UCLASS (ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class START_API UCWeaponComponent : public UActorComponent
{
GENERATED_BODY()
private:
UPROPERTY(EditAnywhere, Category ="Settings")
TArray<TSubclassOf<class ACWeapon>> WeaponClasses;
public:
FORCEINLINE bool IsUnarmedModeMode(){return Type == EWeaponType::Max;}
FORCEINLINE bool IsRifleMode(){return Type == EWeaponType::Rifle;}
FORCEINLINE bool IsPistolMode(){return Type == EWeaponType::Pistol;}
FORCEINLINE bool IsKnifeMode(){return Type == EWeaponType::Knife;}
// 생략 ~
public:
FWeaponTypeChanged OnWeaponTypeChanged; // 무기 타입 변경 델리게이트
FAmmoChanged OnAmmoChanged; // 무기의 탄약 정보 변경 시 전달할 델리게이트 추가


void UCWeaponComponent::SetMode(EWeaponType InType)
{
if (Type == InType)
{
SetUnarmedMode();
return;
}
else if(IsUnarmedModeMode() == false)
{
//무기를 장착하고 있는 상태라면 현재 무기를 장착해제할 수 있는지 체크한뒤 무기 장착 해제
if(GetCurrentWeapon()->CanUnequip() == false)
return;
GetCurrentWeapon()->Unequip();
}
if (Weapons[(int32)InType] == nullptr)
return;
if (Weapons[(int32)InType]->CanEquip() == false)
return;
Weapons[(int32)InType]->Equip();
ChangeType(InType);
}
// ~
void UCWeaponComponent::Begin_Fire() // 무기 발사 시작
{
if (GetCurrentWeapon() == nullptr)
return;
if (GetCurrentWeapon()->CanFire() == false)
return;
GetCurrentWeapon()->BeginFire();
// 발사 후 최신 탄약 정보 전달 (HUD 업데이트용)
int32 CurrentAmmo = GetCurrentWeapon()->GetCurrentAmmo();
int32 MaxAmmo = GetCurrentWeapon()->GetMaxAmmo();
OnAmmoChanged.Broadcast(CurrentAmmo, MaxAmmo);
}
void UCWeaponComponent::End_Fire()
{
if (GetCurrentWeapon() == nullptr)
return;;
GetCurrentWeapon()->EndFire();
}
// ~
void UCWeaponComponent::Reload()
{
if(GetCurrentWeapon() == nullptr)
return;
GetCurrentWeapon()->Reload();
// 재장전 후 최신 탄약 정보 전달 (HUD 업데이트용)
int32 CurrentAmmo = GetCurrentWeapon()->GetCurrentAmmo();
int32 MaxAmmo = GetCurrentWeapon()->GetMaxAmmo();
OnAmmoChanged.Broadcast(CurrentAmmo, MaxAmmo);
}
2. 실시간 탄약 업데이트
2.1 기능 추가 배경
- 개선 사항:
🐞키패드 1번을 누르면 총을 들게 되는데 현재 문제점은 마우스 클릭을 한 번 눌러줘야 탄약 정보가 업데이트가 된다는 것이다. 그리고 또 개선하고 싶은 부분은 계속 탄약 정보가 보여지는 것이 아니라 총을 들 때 보이도록 하고 싶었다...!
무기를 장착할 때 즉시 라이플의 탄약이 업데이트되어 보여지도록 하고, 마우스 오른쪽 클릭 전까지 기다리지 않고 바로 반영하도록 개선한다.
2.2 적용 내용 및 수정 코드
- CWeaponComponent.cpp
- 무기 장착 시, 탄약 정보를 바로 HUD에 전달하도록 OnAmmoChanged.Broadcast() 호출을 추가했다.
- CHUDWidget.h
- 새로운 HUD 업데이트 함수를 추가했다 ➡️ UpdateWeaponType(), SetAmmoVisibility()
- CPlayerController.cpp
- HUD 위젯 생성 후, 무기 컴포넌트의 델리게이트에 HUD의 UpdateAmmo()와 함께 UpdateWeaponType() 함수를 바인딩했다.
- 핵심 요약:
- 무기 장착과 발사, 재장전 시 탄약 정보를 실시간으로 HUD에 업데이트하여 플레이어에게 즉각적인 피드백을 제공한다.
더보기

CWeaponComponent.cpp

CHUDWidget.h

CPlayerController.cpp



3. 총알 아이콘과 HUD 연동 방법
3.1 아이콘 표시 조건
- 목적:
- 무기를 들었을 때만 총알 아이콘(AmmoIcon)이 보이도록 하며, 탄약이 0인 경우에는 아이콘을 숨겨서 탄약이 없다는 것을 표현하고 싶었다✨
- 구현 방식:
- HUD 위젯 내의 UpdateAmmo() 함수에서 탄약 수에 따라 AmmoIcon의 Visibility를 제어했다.
- SetAmmoVisibility() 함수에서 탄약 텍스트와 아이콘의 표시 여부를 동시에 설정했다.
- 중요한 점 ! ➡️ 위젯블루프린트에서 아이콘의 설정을 Hidden으로 해줘야 한다. (처음에는 보이지 않도록!)
3.2 코드
void UCHUDWidget::UpdateAmmo(int32 iCurrentAmmo, int32 iMaxAmmo)
{
if (AmmoText)
{
FString AmmoString = FString::Printf(TEXT("%d / %d"), iCurrentAmmo, iMaxAmmo);
AmmoText->SetText(FText::FromString(AmmoString));
}
// 탄약이 0 이상이면 아이콘 표시, 아니면 숨김 처리한다.
if (AmmoIcon)
{
AmmoIcon->SetVisibility(iCurrentAmmo > 0 ? ESlateVisibility::Visible : ESlateVisibility::Hidden);
}
}
3.3 실무 팁⚙️
- 코드 관리:
- UI 업데이트 관련 코드는 한 곳에 모아두어 변경 사항이 있을 때 쉽게 유지보수할 수 있도록 관리한다.
- 디버깅:
- HUD 업데이트 시 로그를 활용하여 상태를 확인하고, 예외 상황(ex. null 포인터)이 발생하지 않도록 주의한다.
4. 줌인 시 크로스헤어(조준점) 표시 구현
4.1 기능 필요성 및 개념 설명
- 기능 설명:
- 🐞플레이어가 무기를 장착한 후, 줌인(aim) 상태로 전환할 때 크로스헤어(조준점)를 화면에 표시하고 싶었다. 왜냐하면 FPS가 아닌 TPS 시점이라 플레이어 캐릭터의 머리가 크로스헤어와 중첩됐기 때문이다.
4.2 구현 방법 및 코드 적용
- 무기 컴포넌트:
- FAimChanged 델리게이트를 선언하여 Aim 상태 변경을 알리고, BeginAim()과 EndAim() 함수에서 이를 Broadcast 했다.
- HUD 위젯:
- SetCrosshairVisibility(bool bVisible) 함수를 통해 크로스헤어 아이콘의 Visibility를 제어했다.
- CPlayerController:
- HUD 생성 후, 무기 컴포넌트의 OnAimChanged 델리게이트를 HUD의 SetCrosshairVisibility()에 바인딩하여 Aim 상태가 변경될 때마다 크로스헤어 표시를 즉시 업데이트했다.
4.3 코드
// UCWeaponComponent.cpp - BeginAim() 함수 예시
void UCWeaponComponent::BeginAim()
{
if (GetCurrentWeapon() == nullptr)
return;
GetCurrentWeapon()->BeginAim();
OnAimChanged.Broadcast(true); // Aim 상태로 변경됨을 알림한다.
}
// CHUDWidget.cpp - SetCrosshairVisibility() 함수 예시
void UCHUDWidget::SetCrosshairVisibility(bool bVisible)
{
if (CrosshairIcon)
{
CrosshairIcon->SetVisibility(bVisible ? ESlateVisibility::Visible : ESlateVisibility::Hidden);
}
}
// HUD 생성 후, 무기 컴포넌트의 델리게이트 바인딩 (Ammo, WeaponType, Aim)
if (APawn* MyPawn = GetPawn())
{
// 캐스팅하여 UCHUDWidget로 사용
if (UCHUDWidget* HUD = Cast<UCHUDWidget>(CurrentWidget))
{
if (UCWeaponComponent* WeaponComp = MyPawn->FindComponentByClass<UCWeaponComponent>())
{
WeaponComp->OnAmmoChanged.AddDynamic(HUD, &UCHUDWidget::UpdateAmmo);
WeaponComp->OnWeaponTypeChanged.AddDynamic(HUD, &UCHUDWidget::UpdateWeaponType);
WeaponComp->OnAimChanged.AddDynamic(HUD, &UCHUDWidget::SetCrosshairVisibility);
}
}
}
🔗참고 링크
https://designerd.tistory.com/entry/Unreal-55강
[UE] 총알 연사, HUD & CrossHair
전반적으로 언리얼 엔진에서 TPS 총알 발사를 구현하려면 블루프린트, 애니메이션 및 물리 시뮬레이션의 조합이 필요하다. 올바른 설정을 통해 플레이어가 게임에 계속 몰입할
designerd.tistory.com
🟣오늘의 옵시디언 현황
'Dev. > UE 언리얼 엔진' 카테고리의 다른 글
[TIL_250226] 언리얼 엔진 사운드 이펙트 구현: 무기 장전 & 엘리베이터 트리거 (0) | 2025.02.26 |
---|---|
[TIL_250225] 언리얼 엔진으로 구현하는 표면 기반 발소리 & 3D 사운드 시스템 (0) | 2025.02.25 |
[TIL_250220] 언리얼 엔진 디졸브(Dissolve) 이펙트 가이드 (0) | 2025.02.20 |
[TIL_250219] FPS(TPS) 게임 개발 :: 메인 메뉴 UI 및 HUD 구현 (0) | 2025.02.19 |
[TIL_250214] UI 위젯 설계와 실시간 데이터 연동하기 (0) | 2025.02.14 |