[UE5 C++] 아이템 & 인벤토리 시스템 - 5

LeeTaes·2024년 5월 18일
0

[UE_Project] MysticMaze

목록 보기
16/17

언리얼 엔진을 사용한 RPG 프로젝트 만들기

  • 인벤토리 이동 구현
  • Tooltip 구현

인벤토리 이동 구현

  • NativeTick() 함수를 통해 버튼을 누르고 있는 동안 마우스 위치를 따라 인벤토리를 이동시켜 보도록 하겠습니다.
  • 블루프린트 상에서 추가해둔 border를 찾아 위치를 지정하는 방식으로 구현해보도록 하겠습니다.

인벤토리를 이동시키기 위한 함수와 변수를 추가해주도록 합니다.

로직은 다음과 같습니다.

  1. 드래그 시작 : Border 위젯의 위치 구하기. 마우스 위치 구하기. Tick() 시작
  2. 드래그 중 : Tick() 함수에서 마우스가 이동한 만큼 Border 위젯의 위치 재설정
  3. 드래그 종료 : Tick() 함수 막기
// MMInventoryWidget Header

#pragma once

#include "CoreMinimal.h"
#include "UI/MMCustomWidget.h"
#include "GameData/MMEnums.h"
#include "MMInventoryWidget.generated.h"

/**
 * 
 */
UCLASS()
class MYSTICMAZE_API UMMInventoryWidget : public UMMCustomWidget
{
	GENERATED_BODY()
	
protected:
	
    ...
    
	virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;

	UFUNCTION()
	void MoveStart();

	UFUNCTION()
	void MoveEnd();
    
    ...
    
// Main
public:
	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UButton> BTN_MainButton;

	...
    
private:
	// 드래그 중인지를 판별하기 위한 변수
	uint8 bIsDragging;
	
    // 초기 위치 및 오프셋
	FVector2D InitialOffset;
	FVector2D InitialPos;

	// 찾은 border를 저장하기 위한 변수
	TObjectPtr<class UBorder> Border;
};
// MMInventoryWidget Cpp

void UMMInventoryWidget::Init()
{
	// 드래그 false
	bIsDragging = false;

	// border 위젯 찾아 저장하기 
	TArray<UWidget*> TempWidgets;
	WidgetTree->GetAllWidgets(TempWidgets);
	for (UWidget* widget : TempWidgets)
	{
		UBorder* border = Cast<UBorder>(widget);
		if (border)
		{
			Border = border;
			break;
		}
	}
    
    ...
    
    // 메인 버튼에 함수 연동하기
    if (BTN_MainButton)
	{
		BTN_MainButton->OnPressed.AddDynamic(this, &UMMInventoryWidget::MoveStart);
		BTN_MainButton->OnReleased.AddDynamic(this, &UMMInventoryWidget::MoveEnd);
	}
    
    ...
}

void UMMInventoryWidget::MoveStart()
{
	// 드래그를 시작 처리
	bIsDragging = true;

	// border의 현재 위치를 구해 저장합니다.
	FVector2D WidgetPos;
	UCanvasPanelSlot* slot = UWidgetLayoutLibrary::SlotAsCanvasSlot(Border);
	if (slot)
	{
		WidgetPos = slot->GetPosition();
	}
    InitialPos = WidgetPos;

	// 마우스의 현재 위치를 구해 저장합니다.
	InitialOffset = UWidgetLayoutLibrary::GetMousePositionOnViewport(this);

}

void UMMInventoryWidget::MoveEnd()
{
	// 드래그 종료 처리
	bIsDragging = false;
}

void UMMInventoryWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
	Super::NativeTick(MyGeometry, InDeltaTime);

	// 드래그 중이라면?
	if (bIsDragging)
	{
    	// 현재 마우스 위치를 받아옵니다.
		FVector2D MousePos = UWidgetLayoutLibrary::GetMousePositionOnViewport(GetWorld());

		// 초기 마우스 위치와의 차이를 계산합니다.
		float DeltaX = InitialOffset.X - MousePos.X;
		float DeltaY = InitialOffset.Y - MousePos.Y;

		// 초기 마우스 위치와의 차이를 초기 위젯 위치에 반영합니다.
		InitialPos.X += -DeltaX;
		InitialPos.Y += -DeltaY;

		// 오프셋을 재설정합니다. (이전 마우스 위치 -> 현재 마우스 위치)
		InitialOffset = MousePos;

		// border의 위치를 재설정합니다.
		UCanvasPanelSlot* slot = UWidgetLayoutLibrary::SlotAsCanvasSlot(Border);
		if (slot)
		{
			slot->SetPosition(InitialPos);
		}
	}
}

툴팁(Tooltip) 구현하기

  • 인벤토리 내부 슬롯에 아이템이 존재할 경우 아이템에 대한 설명을 띄워주기 위해 툴팁을 구현해주도록 하겠습니다.

인벤토리 항목별(장비, 소비, 기타) 툴팁이 달라지도록 구현하기 위해 모든 툴팁의 원형이 되는 "MMToolTip" 클래스를 구현합니다.

기본적으로 모든 항목의 툴팁은 아이템 이름을 보여주도록 할 것이므로 MMToolTip에는 아이템 이름 항목만 추가해주도록 합니다.

// MMToolTip Header

#pragma once

#include "CoreMinimal.h"
#include "UI/MMCustomWidget.h"
#include "MMToolTip.generated.h"

/**
 * 
 */
UCLASS()
class MYSTICMAZE_API UMMToolTip : public UMMCustomWidget
{
	GENERATED_BODY()
	
public:
	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UTextBlock> TXT_ItemName;
};

MMToolTip을 상속받은 위젯들은 보여주고 싶은 항목을 위주로 편하게 작성하시면 되겠습니다.

ex) MMEquipmentToolTip

  • 장비 슬롯의 툴팁은 다음과 같습니다.
// MMEquipmentToolTip Header

#pragma once

#include "CoreMinimal.h"
#include "UI/ToolTip/MMToolTip.h"
#include "MMEquipmentToolTip.generated.h"

/**
 * 
 */
UCLASS()
class MYSTICMAZE_API UMMEquipmentToolTip : public UMMToolTip
{
	GENERATED_BODY()
	
public:
	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UTextBlock> TXT_ItemType;

	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UTextBlock> TXT_WeaponType;

	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UTextBlock> TXT_STR;

	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UTextBlock> TXT_DEX;

	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UTextBlock> TXT_CON;

	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UTextBlock> TXT_INT;

	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UTextBlock> TXT_PurchasePrice;

	UPROPERTY(meta = (BindWidget = "true"))
	TObjectPtr<class UTextBlock> TXT_SalePrice;
};

툴팁 적용하기 (Slot)

  • 슬롯별로 다른 툴팁을 적용하기 위해 <슬롯 타입, 툴팁 클래스> 형식의 맵을 선언해 주도록 합니다.
  • 비어있는 칸의 경우 툴팁을 nullptr로 설정해 아무 정보도 뜨지 않도록 하겠습니다.
// MMSlot Header

...

// ToolTips
public:
	UPROPERTY(EditAnywhere, Category = "Slot")
	TMap<ESlotType, TSubclassOf<class UMMToolTip>> ToolTipClassMap;

	UPROPERTY(VisibleAnywhere, Category = "Slot")
	TMap<ESlotType, TObjectPtr<class UMMToolTip>> ToolTipMaps;
    
    ...
    
protected:
	...
	
	void SetEquipmentToolTip(class UMMToolTip* EquipmentToolTipWidget, class UMMItemData* ItemData);
	void SetConsumableToolTip(class UMMToolTip* ConsumableToolTipWidget, class UMMItemData* ItemData);
	void SetOtherToolTip(class UMMToolTip* OtherToolTipWidget, class UMMItemData* ItemData);
// MMSlot Cpp

void UMMSlot::Init()
{
	// 툴팁 위젯들을 생성해 Map에 저장합니다.
	for (const auto& ToolTipClass : ToolTipClassMap)
	{
		if (ToolTipClass.Value)
		{
			ToolTipMaps.Add(ToolTipClass.Key, CreateWidget<UMMToolTip>(GetWorld(), ToolTipClass.Value));
		}
	}
	
    ...
    
}

이후 슬롯을 업데이트 하는 과정에서 슬롯별로 툴팁을 세팅해주도록 합니다.
(소비, 기타 창 동일)

툴팁 위젯별로 초기화를 진행합니다.
(장비 슬롯의 툴팁 위젯 초기화)


결과 확인

profile
클라이언트 프로그래머 지망생

0개의 댓글