LeeTaes 공부노트

[UE Team Project/T.A.] 10. 무기 교체 (CombatComponent, UI) 본문

프로젝트/TimelessAdventure

[UE Team Project/T.A.] 10. 무기 교체 (CombatComponent, UI)

리태s 2024. 10. 11. 13:53
728x90
반응형

개요

이번 포스팅에서는 여러 종류의 무기를 교체하는 방식에 대해 정리해보도록 하겠습니다.


구현 아이디어 설명

포스팅에서 다루지는 않았지만 무기는 총 4종류(맨손, 검, 활, 횃불)가 존재하며, 프로젝트 기획에서 무기는 장비 창을 통해 교체하는 것이 아닌 위젯을 띄워 선택된 위젯을 바탕으로 교체되도록 만들기로 하였습니다.

 

생각해낸 아이디어는 무기 교체 버튼을 누름과 동시에 위젯을 화면에 띄우고 마지막으로 마우스 Hover 이벤트를 받았던 무기로 교체하는 것입니다.

무기 교체 위젯

 

즉, 주먹/검/활/불 칸을 버튼으로 제작하고, Hover 이벤트 발생 시 위젯에 마지막으로 들어온 버튼이 무슨 버튼인지 체크하며 이후 해당 값에 따라 실제 무기를 교체하는 방식으로 제작하게 되었습니다.


Combat Component 무기 교체 로직 구현 

CombatComponent는 현재 들고 있는 무기의 상태를 저장하며, 상태가 변경되는 경우 상태에 해당하는 무기를 새로 스폰하는 로직을 만들어주었습니다.

무기 타입 구분을 위한 열거형
임시/현재 무기의 상태

 

즉, 위젯이 비활성화 되는 동시에 무기 교체 함수 (ChangeWeapon)을 호출하며, 현재 착용중인 무기를 삭제하고 새로운 무기를 스폰하는 방식입니다.

 

CombatComponent.cpp

더보기
// 휠 버튼 Released 이벤트 발생 시 호출되는 함수
void UTA_CombatComponent::MiddleClickEnd()
{
    if (!OwnerPlayer) return;

    if (CombatState == ECombatState::CS_Idle || CombatState == ECombatState::CS_Dash)
    {
        ATA_PlayerController* PC = Cast<ATA_PlayerController>(OwnerPlayer->GetController());
        if (PC)
        {
            // 위젯 안보이도록 설정
            PC->VisibleWeaponSelectWidget(false);
        }

        // 무기 교체 함수 호출
        ChangeWeapon();
    }
}

// 무기 교체 함수
void UTA_CombatComponent::ChangeWeapon()
{
    // 임시 타입(위젯에서 설정된 타입)에 따라 현재 타입 변환
    EquippedState = TempEquippedState;

    // 기존 무기가 존재하는 경우
    if (IsValid(EquippedWeapon))
    {
        // 해당 무기 삭제
        EquippedWeapon->RemoveWeapon();
        EquippedWeapon = nullptr;
    }

    // 무기 장착
    EquipWeapon();
}

// 무기 착용 함수
void UTA_CombatComponent::EquipWeapon()
{
    // 현재 타입에 따른 무기가 존재하는 경우
    if (WeaponClassMap[EquippedState])
    {
        // 무기 스폰 & 장착
        if (WeaponClassMap[TempEquippedState])
        {
            ATA_WeaponBase* NewWeapon = GetWorld()->SpawnActor<ATA_WeaponBase>(WeaponClassMap[TempEquippedState], OwnerPlayer->GetActorTransform());
            if (NewWeapon)
            {
                NewWeapon->EquipWeapon(OwnerPlayer->GetMesh());
                EquippedWeapon = NewWeapon;
            }
        }
    }
}

ChangeWeapon 위젯

위젯의 디자인은 다음과 같으며 모든 버튼에 Hover 이벤트가 발생하는 경우 CombatComponent의 임시 무기 상태(TempEquipState)를 변경하도록 구현하였습니다.

WBP_ChangeWeapon

또한, 간단한 애니메이션을 넣어 무기 교체 위젯이 열리는 순간 점차 커지는 애니메이션을 재생하였습니다.

 

https://apth1023.tistory.com/28

 

[Unreal Engine 5] 위젯 애니메이션 (C++)

서론현재 진행중인 UE5 엔진을 사용한 사이드 프로젝트에서 직접 위젯에 애니메이션을 추가해보며 작업 내용을 간단히 정리해보기 위해 글을 작성하게 되었습니다. 언리얼 엔진에서 위젯 애니

apth1023.tistory.com

 

ChangeWeapon.h

더보기
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UI/TA_CustomWidget.h"
#include "TA_ChangeWeapon.generated.h"

/**
 * 
 */
UCLASS()
class TIMELESSADVENTURE_API UTA_ChangeWeapon : public UTA_CustomWidget
{
	GENERATED_BODY()
	
protected:
	virtual void NativeConstruct() override;

public:
	void OpenWidget();

private:
	UFUNCTION()
	void OnHoverFist();

	UFUNCTION()
	void OnHoverSword();

	UFUNCTION()
	void OnHoverBow();

	UFUNCTION()
	void OnHoverTorch();

	UFUNCTION()
	void OnHoverCancel();

private:
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<class UOverlay> OL_Main;

	UPROPERTY(meta = (BindWidget))
	TObjectPtr<class UButton> BTN_Fist;

	UPROPERTY(meta = (BindWidget))
	TObjectPtr<class UButton> BTN_Sword;

	UPROPERTY(meta = (BindWidget))
	TObjectPtr<class UButton> BTN_Bow;

	UPROPERTY(meta = (BindWidget))
	TObjectPtr<class UButton> BTN_Torch;

	UPROPERTY(meta = (BindWidget))
	TObjectPtr<class UButton> BTN_Cancel;

	UPROPERTY(meta = (BindWidgetAnim), Transient)
	TObjectPtr<UWidgetAnimation> ChangeWidgetOpen;
};

ChangeWeapon.cpp

더보기
// Fill out your copyright notice in the Description page of Project Settings.


#include "UI/TA_ChangeWeapon.h"
#include "Interface/CombatComponentInterface.h"
#include "Component/TA_CombatComponent.h"

#include "Components/Overlay.h"
#include "Components/CanvasPanelSlot.h"
#include "Blueprint/WidgetLayoutLibrary.h"
#include "Components/Button.h"

void UTA_ChangeWeapon::NativeConstruct()
{
	Super::NativeConstruct();

	BTN_Fist->OnHovered.AddDynamic(this, &UTA_ChangeWeapon::OnHoverFist);
	BTN_Sword->OnHovered.AddDynamic(this, &UTA_ChangeWeapon::OnHoverSword);
	BTN_Bow->OnHovered.AddDynamic(this, &UTA_ChangeWeapon::OnHoverBow);
	BTN_Torch->OnHovered.AddDynamic(this, &UTA_ChangeWeapon::OnHoverTorch);
	BTN_Cancel->OnHovered.AddDynamic(this, &UTA_ChangeWeapon::OnHoverCancel);
}

void UTA_ChangeWeapon::OpenWidget()
{
	FVector2D MousePos = UWidgetLayoutLibrary::GetMousePositionOnViewport(GetWorld());
	UCanvasPanelSlot* slot = UWidgetLayoutLibrary::SlotAsCanvasSlot(OL_Main);
	if (slot)
	{
		slot->SetPosition(MousePos);
	}

	PlayAnimation(ChangeWidgetOpen);
}

void UTA_ChangeWeapon::OnHoverFist()
{
	if (OwnerActor)
	{
		ICombatComponentInterface* CombatComponentInterface = Cast<ICombatComponentInterface>(OwnerActor);
		if (CombatComponentInterface)
		{
			CombatComponentInterface->GetCombatComponent()->SetChangeWeaponState(EEquippedState::ES_Idle);
		}
	}
}

void UTA_ChangeWeapon::OnHoverSword()
{
	if (OwnerActor)
	{
		ICombatComponentInterface* CombatComponentInterface = Cast<ICombatComponentInterface>(OwnerActor);
		if (CombatComponentInterface)
		{
			CombatComponentInterface->GetCombatComponent()->SetChangeWeaponState(EEquippedState::ES_Sword);
		}
	}
}

void UTA_ChangeWeapon::OnHoverBow()
{
	if (OwnerActor)
	{
		ICombatComponentInterface* CombatComponentInterface = Cast<ICombatComponentInterface>(OwnerActor);
		if (CombatComponentInterface)
		{
			CombatComponentInterface->GetCombatComponent()->SetChangeWeaponState(EEquippedState::ES_Bow);
		}
	}
}

void UTA_ChangeWeapon::OnHoverTorch()
{
	if (OwnerActor)
	{
		ICombatComponentInterface* CombatComponentInterface = Cast<ICombatComponentInterface>(OwnerActor);
		if (CombatComponentInterface)
		{
			CombatComponentInterface->GetCombatComponent()->SetChangeWeaponState(EEquippedState::ES_Torch);
		}
	}
}

void UTA_ChangeWeapon::OnHoverCancel()
{
	if (OwnerActor)
	{
		ICombatComponentInterface* CombatComponentInterface = Cast<ICombatComponentInterface>(OwnerActor);
		if (CombatComponentInterface)
		{
			CombatComponentInterface->GetCombatComponent()->SetChangeWeaponState(EEquippedState::ES_Cancel);
		}
	}
}

SetChangeWeaponState는 매우 간단히 임시 상태만 수정하도록 구현하였습니다.


결과

Change Weapon

728x90
반응형