본문 바로가기
프로젝트/UE_Project

인벤토리 시스템 추가 구현

by 화릿불 2024. 1. 10.

 
숨가쁜 23년 12월달을 보내고 계속해서 작업을 할 수 없는 상황이었다가 드디어 다시 작업을 시작할 수 있게 되었다.
 
이번에는 인벤토리에 대해서 추가적인 구현 요청이 들어왔다.
 
첫번째는 인벤토리에 있는 아이템들을 정렬하는 기능이고
두번째는 인벤토리의 비어있는 공간에 기존에 있는 아이템을 배치할 수 있도록 하는 것이었다.
 
첫번째 기능은 쉽게 구현할 수 있을거라고 생각했는데 문제는 두번째였다.
 
처음에 내가 만든 인벤토리의 형태는 다음과 같다.
 

// APInventoryComponent.h
protected:

	UPROPERTY(VisibleAnywhere, Category = "Inventory")
    TArray<TObjectPtr<UAPItemBase>> InventoryContents;
    
    void AddNewItem(UAPItemBase* Item, const int32 AmountToAdd);
    

// APInventoryComponent.cpp
void UAPInventoryComponent::AddNewItem(UAPItemBase* Item, const int32 AmountToAdd)
{
	UAPItemBase* NewItem;

	if (Item->bIsCopy || Item->bIsPickup)
	{
		NewItem = Item;
		NewItem->ResetItemFlags();
	}
	else
	{
		NewItem = Item->CreateItemCopy();
	}

	NewItem->OwningInventory = this;
	NewItem->SetQuantity(AmountToAdd);

	// 실제 아이템이 인벤토리에 추가되는 곳

	InventoryContents.Add(NewItem);
	InventoryTotalWeight += NewItem->GetItemStackWeight();
	OnInventoryUpdated.Broadcast();
}

 
실제로 동작하는 인벤토리는 ItemBase로 Object의 포인터 형태로 배열을 선언해서 사용하고 있었다.
인벤토리의 최대 슬롯 수가 될때까지 아이템을 "순차적으로" 추가하는 형태였다.
 
그렇다보니 첫째로 이 배열 자체를 바꿔야되나 생각했다. 처음부터 다시 시작해야하는거라 솔직히 꺼렸지만 ....,,
이를 흔히 사용하는 Int Arr[25]와 같은 형태를 사용하려고 했는데 이마저도 잘 되지 않았다. 다행이다.
내가 잘못 사용했던건지 아예 사용 자체가 불가했다.
 
그 다음으로 비어있는 아이템을 넣어놓고 실제 값이 있는 아이템이 들어오는 경우 위치를 바꾸는 형태를 생각했다.
 
하지만 인벤토리에 비어있는 아이템을 넣으려고해도 제대로 적용이 되지 않았다.
 

// APInventoryComponent.cpp
UAPInventoryComponent::UAPInventoryComponent()
{
	// 비어있는 아이템 선언
    // 할당이 필요한 것으로 예상
	TObjectPtr<UAPItemBase> Item;

	for (int i = 0; i < InventorySlotsCapacity; i++) InventoryContents.Add(Item);
}

 
아무래도 할당이나 그런 부분이 필요하다고 생각이 들어서 우선은 후순위로 미뤄둔 상태이다.
완전하게 비어있는 아이템 데이터 형태로 더미아이템을 만들어서 넣어줄 필요가 있어보인다.
 
이는 차후 아마도 24년 1월 2째주 주말까지 계속해서 시도해볼 것이다 ... 
 
다음으로 진행한 것은 정렬이다.
 
얘는 좀 삽질을 많이했다.
만들어둔 인벤토리가 ptr 타입이다보니 포인터로 전달해줘야하나? 라는 생각을 하게 된 것.
그래서 작동은 하지만 엉성한 결과가 나오고 말았다.
 

 
정렬의 기능은 다음과 같다.
 
1. 정렬은 처음 누르면 아이템의 종류에 따라 오름차순 정렬된다.
2. 정렬을 두번째 누르면 아이템의 등급에 따라 오름차순 정렬된다.
3. 정렬을 세번째 누르면 아이템의 가격에 따라 오름차순 정렬된다.
 
이후 정렬을 누를때마다 1, 2, 3이 계속 반복적으로 진행된다.
 
하지만 포인터 형태로 구현된 정렬로 인해 아이템의 정보가 제대로 바뀌지 않는 상황이 벌어졌다.
당황한 나머지 사진이나 코드, GIF형태로 남기지는 못했는데 위의 GIF에서 계속 한 아이템으로 도배가 되는 상황이었다고 보면 된다 ...
 
그래서 그냥 기존에 배열 정렬하는 알고리즘처럼 tmp 선언해서 바꿔보았는데 아니 ... 이게 된다 !!
( 무식하게 시도해보다가 얻어걸렸다 )
 

// APInventoryComponente.cpp
void UAPInventoryComponent::SortingInventory()
{
	// 정렬할 아이템이 없으면 동작 X
	if (!InventoryContents.Num()) return;

	for (int i = InventoryContents.Num() - 1; i > 0; i--)
	{
		for (int j = 0; j < i; j++)
		{
			bool IsActive = false;
            // 정렬 기준
			const int RangeType = SortingTimes % 3;
			if (RangeType == 0) IsActive = InventoryContents[j]->ItemType < InventoryContents[j + 1]->ItemType;
			else if (RangeType == 1) IsActive = InventoryContents[j]->ItemQuality < InventoryContents[j + 1]->ItemQuality;
			else if (RangeType == 2) IsActive = InventoryContents[j]->ItemNumericData.Cost < InventoryContents[j + 1]->ItemNumericData.Cost;

			// 정렬을 할 것인가 ?
			if (IsActive)
			{
				TObjectPtr<UAPItemBase> tmp = InventoryContents[j];
				InventoryContents[j] = InventoryContents[j + 1];
				InventoryContents[j + 1] = tmp;
			}
		}
	}

	// 다음 정렬 준비 및 인벤토리 리프레쉬
	SortingTimes++;
	OnInventoryUpdated.Broadcast();
}

 
버블정렬로 구현해놓아서 아이템 슬롯의 수가 굉장히 커지면 효율이 급격하게 떨어질 것으로 예상된다.
하지만 인벤토리의 슬롯 수는 많아봤자 100개 이하일 것으로 예상이 되어서 우선은 이상태로 둘 것이다.
 
문제는 이걸 구현할때까지만해도 키보드 입력을 통해서 정렬을 수행했었는데, 요청받은 사항은 인벤토리 탭에 정렬을 수행할 버튼을 만들고 그거에 맞춰서 작동하도록 만들어야 했다.
 
사실 여기서 삽질을 더 많이해서 시간을 너무 많이 잡아먹었다.
 
인벤토리 시스템이 작동하는것은 APInventoryComponent지만 실제로 유저에게 보여지는 인벤토리는 InventoryPanel이라는 클래스에서 작동하고 있었기 때문에 UI에 대한 바인드 작업도 해당 클래스에서 했어야했다. 하지만 이 사실을 망각한채 계속 컴포넌트클래스에서 작업을 시도했고 당연히 버튼이 있어도 작동할 생각을 안했다.
 
인벤토리 UI는 InventoryPanel클래스를 상속받고 있기 때문에 내가 필요한 작업을 여기서 수행해야했다.
 

// InventoryPanel.h
public:

	// 사용할 버튼 선언 및 바인드
	UPROPERTY(meta = (BindWidget))
    UButton* SortingButton;
 
protected:
	
    // UI의 초기화 함수
    virtual	void NativeOnInitialized() override;
    
// InventoryPanel.cpp
void UInventoryPanel::NativeOnInitialized()
{
	Super::NativeOnInitialized();

	if (SortingButton)
	{
		SortingButton->OnClicked.AddDynamic(this, &UInventoryPanel::InventorySorting);
	}
}
스크립트의 SortingButton 과 UI에 부착한 SortingButton

 
위 사진에 보이는 것처럼 UI와 클래스에 대해서 바인드를 수행하기 위해서는 UI에 넣은 오브젝트의 이름과 클래스에서의 이름이 동일해야 자동으로 바인드가 된다.
 
이런식으로 바인드를하고 클래스내의 초기화 부분에서 클릭에대한 이벤트를 처리해줌으로써 기능구현을 완료했다.
 

 
생각한대로 아주 잘 작동하고 있는것을 확인했고 컨펌까지 받았다 !
야호 이제 못다한 기능 구현해야지 ...
 
2024년 1월 6일
 

'프로젝트 > UE_Project' 카테고리의 다른 글

튜토리얼  (0) 2023.12.17
인벤토리 시스템  (0) 2023.11.14
연출시스템 구현  (0) 2023.11.14
프로젝트 개요  (0) 2023.11.14