ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 블루아카이브 루트슈터 팬게임 프로젝트(4) - 인벤토리 기능
    루트슈터 프로젝트 2023. 12. 31. 00:47

    원본 : 보더랜드3

     

    루트슈터 장르는 기본적으로 FPS지만, 디아블로식 아이템 파밍요소가 있기 때문에 인벤토리, 장비 기능이 필수입니다. 그냥 단순 슈터장르면 인벤토리 기능은 필요없는데.. 괜히 루트슈터 장르를 만들어 보겠다고 일거리가 늘어났습니다(?)

     

    인벤토리 기능의 대부분(사실상 전부)은 별빛상영관님 https://starlight-showcase.tistory.com/36 의 게시글을 참고하여 구현하였습니다. 친절한 설명 감사합니다. 


     

     

    플레이어 캐릭터 클래스 헤더파일

     

    먼저 플레이어의 헤더파일에 1편에서 만든 아이템 구조체 배열을 만들어줍니다. 원래 인벤토리 슬롯 개수같은건 이렇게 하드코딩으로 해놓으면 절대 안되는것을 알지만... 구현만 먼저 해놓자는 마인드로 이렇게 설정해놓았습니다. 이걸 또 나중에 바꿔주려고 엄청 삽질할거 같네요 
    위의 gunSlot은 장비창에서 캐릭터의 총 장착슬롯에 대한 배열입니다. 제 게임에서는 최대 3개만 장착하게 하고 싶으니 이부분은 하드코딩을 해놓아도 될것이라고 생각합니다.. 아마도요 

     

    CustomUI.h

     

    인벤토리 UI의 상단에 위치할 CustomUI 클래스입니다. 이 클래스를 상속받을 인벤토리, 슬롯 클래스에서 초기화 하기 위한 Init 함수를 가상함수로 구현해놓고, 플레이어 캐릭터만 가지고 있을 뿐입니다. cpp파일에 다른 코드는 없습니다.

    여담으로 저는 언리얼이 완전 처음이라 몰랐는데 언리얼에서는 UI를 Widget이라고 부르더라고요. 

     

     

     

    GameUI.h

     

    위의 CustomUI 클래스를 상속받은 GameUI 클래스 입니다. 이곳에서 인게임의 모든 UI를 전부 총괄합니다..만 지금은 인벤토리UI만 구현하려고 하기 때문에 Inventory밖에 없습니다. 앞으로 이곳에 체력바라던가, 현재 총알개수 등등의 Widget이 추가될 예정입니다. 이곳에서 인벤토리 초기화를 하기 위한 Init 함수를 오버라이드 하고 있습니다. 

     

    참고로 다른 위젯 클래스라던가 필수적인 항목들을 Bind하고 싶다면 UPROPERTY에 meta = (BindWidget)를 꼭! 넣으셔야 합니다. GameUI에는 인벤토리 위젯을 가져야 하기 때문에 BindWidget를 넣어준 것을 확인하실 수 있습니다.

     

     

     

    GamUI.cpp

     

    GameUI의 cpp파일도 아직은 별것 없습니다. 인벤토리를 잘 가지고 있는지 확인 후 초기화 과정을 해줍니다. 다른 UI(위젯)이 늘어난다면 이곳에서 초기화를 해주면 될 것 같습니다 

     

     

     

     

    Inventory.h

     

    인벤토리 위젯의 헤더파일입니다.  역시 Init 함수를 가상함수로부터 받아 오버라이드 합니다.
    defaultTexture는 인벤토리 슬롯들이 비어있다면(아이템이 있는 상태가 아니라면) 어떤 이미지(Texture2D)를 보여줄지에 대한 정보입니다. 이것은 블루프린트에서 설정해줄 수 있습니다. 

    밑에는 인벤토리의 슬롯과 총기류 장비창에 대한 슬롯 클래스를 가지고 있습니다. 만약 인벤토리가 30개 한도라면 그 슬롯들 30개에 대해 참조를 할 수 있도록 가지고 있는것입니다.

     

    Inventory.cpp

     

    인벤토리의 초기화, 그리고 Refresh 기능을 담당하는 Init 함수입니다. 

     

    인벤토리의 개수(저는 12개라고 정했습니다) 만큼 플레이어의 인벤토리를 검사합니다. 검사를 해봤을때 디폴트값이거나(저는 아이템이 없다면 기본 Default값이라고 정했습니다) NULL값이라면 그 인벤토리칸은 비어있다는 것이므로 인벤토리의 UI 슬롯 또한 위에서 정한 DefaultTexture로 보여줘야 하므로 설정해 주는 코드입니다. 

     

    밑의 부분부터는 조금 복잡해지는데요, slots.Init(nullptr, 12); 함수 선언은 TArray였던 슬롯 배열을 12개로 만들고 전부 nullptr로 초기화 해주는 부분입니다. 

     

        TArray<UWidget*> widgets;
        WidgetTree->GetAllWidgets(widgets); //위젯트리로부터 모든 widget 받아오기
     

    바로 밑의 이부분은 주석 그대로 위젯 트리로부터 모든 위젯을 다 받아오는 것입니다. 지금은 제가 만든 위젯이 몇개 없지만, 위젯이 많아진다면 여러가지 종류의 상관없는 위젯들까지 전부 가져와지겠죠? 

     

        for(auto widget : widgets)
        {
            UUISlot* slot = nullptr;
            slot = Cast<UUISlot>(widget);
            if(!slot) continue;
            UE_LOG(LogTemp, Error, TEXT("UISlot찾음!"));

     

    그리고 모든 위젯 요소들을 전부 Cast를 통해 검사해봅니다. 내가 원하는 인벤토리 슬롯 위젯이 아니라면 continue로 패스하고 맞다면 밑의 코드들이 실행될겁니다. 

     

            slot->player = this->player;
            slot->Init();
            slots[slot->slotNum] = slot;

     

    그리고 인벤토리 슬롯들에게 플레이어 포인터를 할당해주고 초기화를 해준 후 인벤토리 슬롯 배열에게 정보를 넘겨줍니다.

     

     

     

    UISlot.h

    public:
        void Init() override;
        void SetTexture(UTexture2D* tex);
        void Refresh();
    public:
        UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (UIMax = 31, UIMin = -1))
        int slotNum; //플레이어의 인벤토리 배열의 몇번째 num과 대응되는가에 대한 변수
        UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (BindWidget))
        UImage* icon; //아이템의 이미지

        UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (BindWidget))
        UImage* backColor; //아이템의 등급에 따른 뒷배경색깔

        UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (BindWidget))
        EItemType slotItemType; //슬롯에 들어있는 아이템 타입

        UPROPERTY(BlueprintReadOnly)
        ESlot slotType = ESlot::ES_INV;

     

    마지막으로 UISlot에 대한 헤더파일입니다만, 여기서부터 여러가지 변수들이 많이 선언됩니다. 

    먼저 슬롯마다 플레이어의 인벤토리 배열의 몇번 인덱스에 대응되는가에 대한 slotNum 변수가 있습니다.

    그리고 icon은 제가 1편의 아이템 구조체에 있던 아이템의 아이콘 사진입니다. slotItemType는 슬롯에 들어가 있는 아이템의 타입입니다. 비어있다면 NULL이나 Default가 들어갑니다.

     

    그리고 backColor 이미지가 있는데요, 디아블로 같은 게임을 해보시면 아시겠지만 아이템의 등급에 따라 아이템 슬롯의 배경색깔이 다릅니다. 

     

    디아블로4의 인벤토리 예시

     

    이런식으로 말이죠. 때문에 저는 배경 이미지를 추가하여 들어온 아이템의 희귀도에 따라 뒷배경에 색깔을 넣어줄 생각입니다. 

     

     

    UISlot.cpp

    void UUISlot::Init()
    {
        Refresh();
    }

    void UUISlot::SetTexture(UTexture2D *tex)
    {
        UE_LOG(LogTemp,Error, TEXT("텍스쳐%s 등록"), *tex->GetName());
        icon->SetBrushFromTexture(tex);
    }

     

    먼저 Init 함수는 그저 인벤토리 슬롯을 새로고침 해주는 함수를 실행시켜줄 뿐입니다. 그리고 SetTexture는 플레이어의 아이템의 아이콘을 가져와서 인벤토리의 아이콘으로 등록시키는 함수입니다. 

     

     

     

    void UUISlot::Refresh() //슬롯 정보 갱신
    {
        FItemData& data = player->itemInventory[slotNum];
       
        switch(data.itemRarity){ //아이템 등급별 배경색 설정
            case ERarity::ER_NORMAL:
            backColor->SetColorAndOpacity(FLinearColor(0.99f, 0.99f, 0.99f, 1.0f));
            break;

            case ERarity::ER_UNCOMMOM:
            backColor->SetColorAndOpacity(FLinearColor::Blue);
            break;

            case ERarity::ER_RARE:
            backColor->SetColorAndOpacity(FLinearColor(1.0f, 0.509f, 1.0f, 1.0f));
            break;

            case ERarity::ER_LEGENDARY:
            backColor->SetColorAndOpacity(FLinearColor(1.0f, 0.647f, 0, 1.0f));
            break;
           
            default:
            break;
        }
        slotItemType = data.itemType; //슬롯에 들어가있는 아이템 정보 갱신
        if(data.itemIcon) SetTexture(data.itemIcon);
        SetWeaponToolTipWidget();
    }

     

    그리고 가장 중요한 역할을 해주는 Refresh 함수입니다. 먼저 이 슬롯의 번호에 해당하는 플레이어 인벤토리의 정보를 가져옵니다. 그리고 뒷배경의 색깔을 정해주는데요, 아이템의 등급을 참조하여 FLinearColor로 색깔을 정해줍니다. 노말템이라면 흰색, 언커먼 파란색, 레어템 보라색, 전설템 노란색입니다. 

     

    그리고 슬롯 아이템 타입 변수도 등록시켜주고 아이콘까지 SetTexture로 교체해주면 c++ 인벤토리 작업이 끝납니다. 정말 기네요.. 

     

    이제 지금까지 작성한 클래스를 기반으로 한 블루프린트를 만들어 줍니다. 

     

     

     

    SlotUI 블루프린트

     

    먼저 인벤토리 슬롯 하나에 대한 블루프린트입니다. 상당히 심플한 디자인입니다.. 참고로 backColor 변수와 icon 변수는 제가 meta = BindWidget 프로퍼티를 추가하였기 때문에 처음 BP를 생성하면 빨리 만들라고 컴파일 오류를 뿜어댈것입니다. 당황하지 않고 각자 타입에 맞는 요소들을 팔레트나 라이브러리에서 추가하여 이름을 거기에 맞게 바꿔주면됩니다. 

     

     

     

    UInventory 블루프린트

     

    그 다음으로 인벤토리에 대한 블루프린트입니다. 계층구조를 보시면 아시겠지만 슬롯이 플레이어 인벤토리 개수만큼 선언돼있습니다. 슬롯 하나하나 누르셔서 디테일칸에 이 슬롯이 플레이어의 인벤토리 슬롯의 몇번 인덱스에 해당되는지 전부 적으셔야합니다.

     

     

     

     

     

    마지막으로 GameUI 블루프린트입니다. 아무것도 없이 인벤토리만 달랑 있네요. 앞으로 체력바라던가 더 추가해야겠습니다.

     

    이제 뷰포트에 추가해서 인게임을 확인해보면 

     

    인게임 결과물..

     

    이렇게 인벤토리에 있는 총들을 보여주는 것을 확인할 수 있습니다. 아이콘 잘 보이고, 템 등급에 따라서 확실하게 뒷배경도 템 등급에 따라 다르게 설정되는것도 확인할 수 있습니다. 

     

    비어있는 칸들의 사진들이 몰?루 인 이유는 위에서 제가 언급한 defaultTexture를 Inventory 블루프린트에서 몰루 사진으로 설정해놓았기 때문입니다. 아마도 성공적으로 배포하는 날이 온다면.. 저 사진은 다른 사진으로 교체해야겠습니다. 

     

     

     

    사실 인벤토리 기능은 개발한지 며칠 되었기 때문에 살짝 까먹을 뻔 했는데 블로그를 통해 다시 정리하면서 제 머리속의 기억들도 다시 재정비를 할 수 있었습니다. 다른 개발자들이 왜 블로그를 운영하는지 알 수 있었네요. 
    이만 줄이겠습니다. 다시한번 인벤토리 기능구현을 도와준 별빛상영관님께 감사드립니다.

Designed by Tistory.