프로젝트 개요
여러 명이 하나의 스포츠 영상을 시청하면서 특정 대상(스포츠 팀 / 선수)을 응원하는 플랫폼을 제공하는 메타버스 컨텐츠를 제작하는 프로젝트
인벤토리의 필요성
스포츠 경기를 관람할 때 다이나믹한 응원을 위해 폭죽을 터트리고 응원봉을 흔들듯 여러 아이템이 있으면 영상을 시청하는데 재미 요소를 더할 수 있을 것이라 판단했고, 여러 아이템을 자신이 가진 포인트로 구매하고 구매한 아이템을 관리 기능이 필요하다 판단하여 인벤토리를 구현하게 되었다.
구현 개요
인벤토리는 배열로 구현하고자 하였고 하나의 인벤토리의 슬롯에 대한 구조체와 아이템의 정보에 대한 구조체를 만들고
아이템 정보를 하나의 Row로 하는 DataTable을 만들어 구매, 사용, 획득 간에 DataTable을 참조하여 실제 아이템 오브젝트를 인벤토리에 넣는 것이 아닌 아이템에 대한 정보를 인벤토리에 담으려 함.
인벤토리 구조

SlotStruct는 구조체로 인벤토리 한 칸에 대한 정보를 의미한다.
SlotStruct는 ItemData라는 구조체와 SlotQuantity라는 int32 자료형의 변수를 멤버로 갖고 있다.
ItemData는 아이템이 가져야 할 기본적인 데이터를 멤버로 갖는다.
ItemData에 == 연산자를 다중정의한 이유는 두 개의 아이템이 같은 아이템인지 비교할 때, 아이템 데이터를 비교하여 판단하기 위함이다.
USTRUCT(BlueprintType)
struct FItemData : public FTableRowBase
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString ItemName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UTexture2D* ItemIcon = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 ItemPrice;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<AHG_ItemBase> ItemClass;
UPROPERTY(EditDefaultsOnly)
EItemCategory ItemCategory;
bool operator==(const FItemData& Other) const
{
return ItemName == Other.ItemName;
}
};
USTRUCT(Atomic, BlueprintType)
struct FSlotStruct
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
int32 Quantity = 0;
UPROPERTY(EditDefaultsOnly)
FItemData ItemInfo;
};
인벤토리는 SlotStruct라는 구조체를 하나의 원소로 갖는 1차원 배열로 선언

Unreal Editor에서 ItemData를 하나의 행으로 하는 DataTable 생성
※ Unreal Editor에서 DataTable을 만들 때, C++로 구현한 구조체를 DataTable의 RowData로 설정하려면 FTableRowBase를 상속받아야 함.


인벤토리 기능 함수
- 인벤토리 관련 기능은 Player 클래스에서 분리하고 PlayerInventoryComponent라는 클래스에서 따로 관리하고 Player가 PlayerInventoryComponent를 컴포넌트로 갖도록 하는 구조
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "SlotStruct.h"
#include "HG_PlayerInventoryComponent.generated.h"
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class METACHEERINGROOM_API UHG_PlayerInventoryComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UHG_PlayerInventoryComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
// 인벤토리의 최대 크기
UPROPERTY(EditDefaultsOnly)
int32 InventorySize;
// FSlotStruct을 요소로 갖는 배열 (실제 인벤토리)
TArray<FSlotStruct> Inventory;
// 인벤토리에 아이템 추가
// Parameter
// Item : 인벤토리에 추가할 아이템의 정보(FItemData 구조체)
// Quantity : 인벤토리에 넣을 아이템 개수
void AddtoInventory(FItemData Item, int32 Quantity);
// 인벤토리에 아이템이 존재하는지 존재한다면 그 아이템의 인덱스를 반환
// Parameter
// ItemName : 검사하고싶은 아이템 이름
// Return
// int32, 찾은 아이템의 인덱스, 없으면 -1 반환
int32 FindSlot(FString ItemName);
// 인벤토리에서 아이템 제거
// Parameter
// Item : 인벤토리에 제거할 아이템의 정보(FItemData 구조체)
// Quantity : 인벤토리에서 제거할 아이템 개수
void RemoveFromInventory(FItemData Item, int32 Quantity);
// 아이템 정보를 담는 데이터 테이블
UPROPERTY(EditDefaultsOnly)
class UDataTable* ItemDataTable;
// 컴포넌트를 갖고있는 플레이어의 정보
class AHG_Player* CompOwner;
};
- void AddtoInventory(FItemData Item, int32 Quantity)
void UHG_PlayerInventoryComponent::AddtoInventory(FItemData Item, int32 Quantity)
{
// 만약 인벤토리의 크기가 최대 크기가 넘는지 확인
if (Inventory.Num() <= InventorySize)
{
// 인벤토리에 아이템이 존재하는지 확인
if (FindSlot(Item.ItemName) != -1)
{
// 만약 인벤토리에 아이템이 존재한다면
// 인벤토리 배열에서 찾은 아이템 인덱스의 Quantity를 증가
Inventory[FindSlot(Item.ItemName)].Quantity += Quantity;
}
else
{
// 만약 인벤토리에 아이템이 존재하지 않는다면
// SlotStruct를 만들어서
FSlotStruct Temp;
// 인벤토리에 더하려는 아이템 정보를 슬롯의 아이템 정보로 초기화
Temp.ItemInfo = Item;
// SlotStruct의 Quantity (초깃값 0)에 매개변수 Quantity를 더해줌
Temp.Quantity += Quantity;
// Inventory에 Temp라는 슬롯 추가
Inventory.Add(Temp);
}
}
// GameInstance의 인벤토리 정보 갱신
CompOwner->GI->CurrentInventory = Inventory;
}
- int32 FindSlot(FString ItemName);
int32 UHG_PlayerInventoryComponent::FindSlot(FString ItemName)
{
// 인벤토리 배열의 모든 인덱스 순회
for (int32 i = 0; i < Inventory.Num(); i++)
{
// 만약 현재 순회 중인 인덱스의 요소의 ItemName이 매개변수로 전달된 ItemName과 같다면
if (Inventory[i].ItemInfo.ItemName == ItemName)
{
// 현재의 인덱스를 반환
return i;
}
}
// 모두 순회했지만 찾지 못했다면 -1을 반환
return -1;
}
- void RemoveFromInventory(FItemData Item, int32 Quantity);
void UHG_PlayerInventoryComponent::RemoveFromInventory(FItemData Item, int32 Quantity)
{
// Item이라는 아이템 데이터를 갖는 슬롯이 존재하는지 확인
if (FindSlot(Item.ItemName) != -1)
{
// 존재한다면
// 찾은 인덱스의 아이템의 Quantity를 줄여준다.
// 여기서 인덱스의 Quantity가 0 이하로 내려가지 않도록 Clamp를 통해 최솟값을 0이 되도록함
Inventory[FindSlot(Item.ItemName)].Quantity = FMath::Clamp(Inventory[FindSlot(Item.ItemName)].Quantity - Quantity, 0, InventorySize);
// 만약 Quantity가 0이 됐다면
if (Inventory[FindSlot(Item.ItemName)].Quantity == 0)
{
// 인벤토리에서 찾은 인덱스의 요소를 제거
Inventory.RemoveAt(FindSlot(Item.ItemName));
}
}
// GameInstance의 현재 Inventory정보 저장
CompOwner->GI->CurrentInventory = Inventory;
}
'SIN' 카테고리의 다른 글
| [UE5] 인벤토리 시스템 - 인벤토리 UI (2) | 2024.11.04 |
|---|