-
블루아카이브 루트슈터 팬게임 프로젝트(20) - 인벤토리 아이템 판매 시스템루트슈터 프로젝트 2024. 1. 21. 22:52
void APlayerCharacter::OpenInventory() //인벤토리 열기 { APlayerController* con = Cast<APlayerController>(Controller); if(con && !isOpenInv && inventoryUI) { inventoryUI->SetVisibility(ESlateVisibility::Visible); con->SetShowMouseCursor(true); con->SetInputMode(FInputModeUIOnly()); isOpenInv = true; //con->SetPause(true); } else if(con && isOpenInv && inventoryUI){ inventoryUI->SetVisibility(ESlateVisibility::Hidden); con->SetShowMouseCursor(false); con->SetInputMode(FInputModeGameOnly()); cursorNum = 100; isOpenInv = false; //con->SetPause(false); } }
안녕하세요. 제 게임에서는 지금까지 플레이어가 아이템을 자동으로 줍고, 필요없는 아이템은 처분할 방법이 없어서 인벤토리가 꽉 차면 처분할 방법이 없었습니다. 그래서 이번에 아이템 판매 기능을 만들어서 인벤토리를 정리할 수 있게 하였습니다.
제 게임에서 인벤토리의 아이템 판매 방법은 2가지가 있습니다.
1. 인벤토리 슬롯에 커서를 대고 F키를 누르면 1개씩 판매
2. L키를 눌러 등급이 낮은 아이템 일괄 판매
일단 먼저 플레이어의 아이템을 판매하는 기능부터 만들도록 하겠습니다.
PlayerCharacter.h
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) int64 money = 0; int cursorNum = 100; void SellItem(int invSlot); void AutoSell();
먼저 플레이어 클래스에 새로 추가된 변수와 함수들입니다. 플레이어의 소지금을 나타내는 변수와 특정 인벤토리 슬롯에 있는 아이템을 판매하는 함수, 그리고 등급이 낮은 아이템들을 자동으로 판매하는 함수입니다. cursorNum이 무엇인지에 대해서는 후술하겠습니다.
void APlayerCharacter::SellItem(int invSlot) { if(0 > invSlot || invSlot >= 40 || itemInventory[invSlot].itemType == EItemType::EIT_DEFAULT ) return; int sellMult = 1; switch(itemInventory[invSlot].itemRarity) { case(ERarity::ER_NORMAL): sellMult = 1; break; case(ERarity::ER_UNCOMMOM): sellMult = 2; break; case(ERarity::ER_RARE): sellMult = 4; break; case(ERarity::ER_LEGENDARY): sellMult = 8; break; } FItemData blank = FItemData::FItemData(); int itemvalue; itemvalue = itemInventory[invSlot].itemLevel * 100 * sellMult; money = FMath::Clamp(itemvalue + money, 0, 9999999); itemInventory[invSlot] = blank; RefreshInventory(); } void APlayerCharacter::AutoSell() { for(int i=0; i < 40; i++) { if(itemInventory[i].itemRarity == ERarity::ER_NORMAL || itemInventory[i].itemRarity == ERarity::ER_UNCOMMOM) SellItem(i); } }
아이템 판매 기능 함수입니다. 만약 슬롯 숫자가 배열 범위를 벗어나는 숫자거나 비어있는 슬롯이면 리턴을 하고 아니라면 판매 절차를 진행합니다. 아이템 가격공식은 간단하게 아이템 레벨 * 100 * 희귀도 별 배율 로 하였습니다.
그리고 자동 판매 함수는 아이템 인벤토리를 순회하여 노말등급과 언커먼 등급의 아이템들을 전부 팔아버립니다.
UISlot.h
protected: virtual void NativeOnMouseEnter(const FGeometry & InGeometry, const FPointerEvent & InMouseEvent) override; virtual void NativeOnMouseLeave(const FPointerEvent & InMouseEvent) override; //FEventReply OnKeyDown(FGeometry MyGeometry, FKeyEvent InKeyEvent); //virtual FReply NativeOnPreviewKeyDown(const FGeometry & InGeometry, const FKeyEvent & InKeyEvent) override;
여기서 UI 처리를 하는데 있어서 하나의 문제가 발생하는데, 위젯에는 다양한 이벤트 함수가 있는데 OnKeyDown 함수는 마우스 커서가 올라와 있는 위젯이 아닌 최근에 클릭한 위젯에 대해 처리를 하기 하기 때문에, 제가 원하는 방법대로 처리해주는 함수가 아니었습니다. 따라서 제가 주목한 이벤트 함수는 마우스커서가 위젯에 들어갔을때와 나왔을때 처리되는 함수 NativeOnMouseEnter와 NativeOnMouseLeave 에 주목했습니다.
UISlot..cpp
void UUISlot::NativeOnMouseEnter(const FGeometry &InGeometry, const FPointerEvent &InMouseEvent) { player->cursorNum = slotNum; } void UUISlot::NativeOnMouseLeave(const FPointerEvent &InMouseEvent) { player->cursorNum = 100; }
따라서 이 함수를 통해 마우스 커서가 인벤토리 슬롯에 들어왔을때 그 슬롯이 몇번인지에 대해 변수로 저장하고, 빠져나오면 일부러 100으로 저장하여 인벤토리 갯수의 범위 밖으로 설정하게 하였습니다.
GameUI클래스
protected: //버튼 처리에 대한 함수 virtual FReply NativeOnKeyDown(const FGeometry & InGeometry, const FKeyEvent & InKeyEvent) override;
FReply UUGameUI::NativeOnKeyDown(const FGeometry &InGeometry, const FKeyEvent &InKeyEvent) { if(InKeyEvent.GetKey() == EKeys::I) { if(player)player->OpenInventory(); return FReply::Handled(); } else if(InKeyEvent.GetKey() == EKeys::F) { if(player)player->SellItem(player->cursorNum); return FReply::Handled(); } else if(InKeyEvent.GetKey() == EKeys::L) { if(player)player->AutoSell(); return FReply::Handled(); } return FReply::Unhandled(); }
그렇게 하여 UI가 활성화 되었을때 특정 키가 눌린다면 함수가 실행 될 수 있도록 하였습니다. I키로 인벤토리를 닫고, F키로 특정 슬롯의 아이템을 팔고, L키를 통해 모든 저등급 아이템을 처분할 수 있게 하였습니다.
https://tarox7unrealentertainment.hatenablog.com/entry/2020/06/17/205251
NativeOnKeyDown 함수에 대해서는 일본의 웹사이트를 참고하였습니다.
void APlayerCharacter::OpenInventory() //인벤토리 열기 { APlayerController* con = Cast<APlayerController>(Controller); if(con && !isOpenInv && inventoryUI) { inventoryUI->SetVisibility(ESlateVisibility::Visible); con->SetShowMouseCursor(true); con->SetInputMode(FInputModeUIOnly()); isOpenInv = true; } else if(con && isOpenInv && inventoryUI){ inventoryUI->SetVisibility(ESlateVisibility::Hidden); con->SetShowMouseCursor(false); con->SetInputMode(FInputModeGameOnly()); cursorNum = 100; isOpenInv = false; } }
한가지 주의할 점으로 UI의 조작이 필요한 경우 SetInputMode를 통해 UI입력만 받도록 조정하고, 게임으로 돌아가면서 게임의 input을 받을 수 있도록 조정해야 한다는 점 입니다. 이것을 설정하지 않으면 위젯이 입력을 받지 못합니다.
또 한가지 주의할 점으로 원하는 위젯이 키보드 입력을 받기 위해서는 포커스 가능 여부에 true 체크를 꼭 해야 한다는 것입니다. 여기까지 완수하면 위젯에서 키보드 입력을 받을 수 있습니다.
결과물입니다. 물건을 팔면 인벤토리가 비워지고 그만큼의 돈이 들어오는 것을 볼 수 있습니다.
다음으로 인벤토리 일괄 처분 기능입니다. 파란색과 하얀색 바탕의 아이템들은 한꺼번에 처리되면서 돈도 같이 오르는 것을 확인할 수 있습니다.
이제 정말 RPG 게임으로서 기본중의 기본적인 기능들은 얼추 완성된 것 같으니 이만 줄이겠습니다. 다음에는 메인 화면 기능을 만들어서 메인화면과 웨이브 게임모드로 왔다갔다 할 수 있도록 해봐야겠습니다.
'루트슈터 프로젝트' 카테고리의 다른 글
블루아카이브 루트슈터 팬게임 프로젝트(22) - 메인화면 구현 (1) 2024.01.25 블루아카이브 루트슈터 팬게임 프로젝트(21) - 게임 저장 시스템 (1) 2024.01.23 블루아카이브 루트슈터 팬게임 프로젝트(19) - 체력 회복 시스템 (1) 2024.01.21 블루아카이브 루트슈터 팬게임 프로젝트(18) - 적 스폰 시스템 & 웨이브 게임모드(3) (0) 2024.01.20 블루아카이브 루트슈터 팬게임 프로젝트(18) - 적 스폰 시스템 & 웨이브 게임모드(2) (0) 2024.01.19