I am working on Inventory System, which is divided into Inventory Component and UI, it works fine, until Items started to clean up from my Inventory suddenly (Checked this while playing editor, after certain item manipulations like equipping/unequipping or even dropping item) all items just gone.
Here’s the code of inventory (Component and UI)
#include "InventoryComponent.h"
#include "GameFramework/Actor.h"
#include "PlayerCharacter.h"
#include "PlayerCharacterHUD.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/Engine.h"
UInventoryComponent::UInventoryComponent()
{
PrimaryComponentTick.bCanEverTick = false;
InventorySize = 16;
}
void UInventoryComponent::AddItem(AAbstractItemActor* Item)
{
if (Item)
{
if (Items.Contains(Item))
{
UE_LOG(LogTemp, Warning, TEXT("Item already exists in inventory (pointer)!"));
return;
}
if (this->bIsInventoryFull())
{
bool bCanStack = !this->bIsCellsInInventoryFull();
if (!bCanStack)
{
UE_LOG(LogTemp, Warning, TEXT("Sorry, your inventory is full! :/"));
return;
}
}
TArray<int32> Indexes = this->FindItemIndexes(Item);
TArray<AAbstractItemActor*> ItemsInInventory = this->GetItems();
if (Indexes.Num() <= 0)
{
if (!this->bIsInventoryFull())
{
this->Items.Add(Item);
Item->SetActorHiddenInGame(true);
Item->SetActorEnableCollision(false);
Item->SetLifeSpan(1.0f);
}
return;
}
for (int32 i = 0; i < Indexes.Num(); ++i)
{
int32 InvIndex = Indexes[i];
uint8 AmountInInventory = ItemsInInventory[InvIndex]->GetCurrentItemAmount();
uint8 MaxStack = ItemsInInventory[InvIndex]->GetMaxItemAmount();
uint8 AmountOnGround = Item->GetCurrentItemAmount();
if (AmountInInventory < MaxStack)
{
uint8 SpaceLeft = MaxStack - AmountInInventory;
if (AmountOnGround <= SpaceLeft)
{
ItemsInInventory[InvIndex]->SetCurrentItemAmount(AmountInInventory + AmountOnGround);
Item->SetActorHiddenInGame(true);
Item->SetActorEnableCollision(false);
Item->SetLifeSpan(1.0f);
return;
}
else
{
ItemsInInventory[InvIndex]->SetCurrentItemAmount(MaxStack);
Item->SetCurrentItemAmount(AmountOnGround - SpaceLeft);
}
}
if (Item->GetCurrentItemAmount() == 0)
{
Item->SetActorHiddenInGame(true);
Item->SetActorEnableCollision(false);
Item->SetLifeSpan(1.0f);
return;
}
}
if (Item->GetCurrentItemAmount() > 0 && !this->bIsInventoryFull())
{
this->Items.Add(Item);
Item->SetActorHiddenInGame(true);
Item->SetActorEnableCollision(false);
Item->SetLifeSpan(1.0f);
}
else
{
return;
}
}
}
void UInventoryComponent::RemoveItem(AAbstractItemActor* Item)
{
if (!Item) return;
int32 FoundIndex = Items.IndexOfByPredicate([&](AAbstractItemActor* InItem){return InItem == Item;});
if (FoundIndex != INDEX_NONE)
{
Items.RemoveAtSwap(FoundIndex);
}
}
void UInventoryComponent::RemoveItemAtIndex(int32 Index)
{
if (Items.IsValidIndex(Index))
{
Items.RemoveAtSwap(Index);
}
}
TArray<AAbstractItemActor*>& UInventoryComponent::GetItems()
{ return Items;
}
void UInventoryComponent::PrintInventory()
{
if (Items.Num() == 0)
{
UE_LOG(LogTemp, Display, TEXT("Inventory is empty"));
return;
}
for (int32 Index = 0; Index < Items.Num(); ++Index)
{
AAbstractItemActor* Item = Items[Index];
if (GEngine && Item)
{
// Using -1 as a key to allow multiple messages without overwriting
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("[%d] Item name: %s"), Index, *Item->GetItemName()));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("\tItem description: %s"), *Item->GetItemDescription()));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, FString::Printf(TEXT("\tItem Amount: %d"), Item->GetCurrentItemAmount()));
}
}
}
bool UInventoryComponent::bIsInventoryFull()
{
return Items.Num()>=InventorySize;
}
bool UInventoryComponent::bIsCellsInInventoryFull()
{
bool IsSingularCellsAreFull = true;
for (int32 Index = 0; Index < Items.Num(); ++Index)
{
if (Items[Index]->GetCurrentItemAmount()<Items[Index]->GetMaxItemAmount())
{
IsSingularCellsAreFull = false;
}
}
return IsSingularCellsAreFull;
}
TArray<int32> UInventoryComponent::FindItemIndexes(AAbstractItemActor* Item)
{
TArray<int32> FoundIndexes;
if (!Item) return FoundIndexes;
UClass* ItemClass = Item->GetClass();
for (int32 Index = 0; Index < Items.Num(); ++Index)
{
if (Items[Index] && Items[Index]->GetClass() == ItemClass)
{
FoundIndexes.Add(Index);
}
}
return FoundIndexes;
}
int32 UInventoryComponent::GetMaxSizeInventory()
{
return InventorySize;
}
int64 UInventoryComponent::GetMoneyAmount()
{
AActor* Player = UGameplayStatics::GetActorOfClass(GetWorld(), APlayerCharacter::StaticClass());
if (Player)
{
if (IPlayerCharacterInterface* PlayerInterface = Cast<IPlayerCharacterInterface>(Player))
{
return PlayerInterface->GetMoney();
}
}
return 0;
}
AAbstractItemActor* UInventoryComponent::GetItemAtIndex(int32 Index)
{
if (Items.IsValidIndex(Index))
{
return Items[Index];
}
return nullptr;
}
// Fill out your copyright notice in the Description page of Project Settings.
#include "PlayerCharacterHUD.h"
#include "Components/WrapBox.h"
#include "Components/Overlay.h"
#include "GameFramework/PlayerController.h"
#include "AbstractItemActor.h"
#include "Animation/WidgetAnimation.h"
#include "UMG.h"
#include "MovieScene.h"
#include "Templates/Casts.h"
#include "Components/Widget.h"
#include "PlayerCharacter.h"
#include "PlayerCharacterInterface.h"
#include "Blueprint/UserWidget.h"
APlayerCharacterHUD::APlayerCharacterHUD() : Super()
{
ConstructorHelpers::FClassFinder<UUserWidget> PlayerHUD(TEXT("/Game/Blueprints/PlayerCharacterWidget"));
ConstructorHelpers::FClassFinder<UUserWidget> SlotClass(TEXT("/Game/Blueprints/InventorySlot"));
MainHUD = PlayerHUD.Class;
InventorySlotClass = SlotClass.Class;
}
void APlayerCharacterHUD::BeginPlay()
{
Super::BeginPlay();
UUserWidget* CharacterPlayerWidget = CreateWidget(GetWorld(), MainHUD);
CharacterPlayerWidget->AddToViewport();
InventoryOverlay = Cast<UOverlay>(CharacterPlayerWidget->GetWidgetFromName(FName("InventoryOverlay")));
ActionOverlay = Cast<UOverlay>(CharacterPlayerWidget->GetWidgetFromName(FName("ActionOverlay")));
InventoryBox = Cast<UWrapBox>(CharacterPlayerWidget->GetWidgetFromName(FName("InventoryBox")));
EquipButton = Cast<UButton>(CharacterPlayerWidget->GetWidgetFromName(FName("EquipButton")));
MoneyInfo = Cast<UTextBlock>(CharacterPlayerWidget->GetWidgetFromName(FName("MoneyInfo")));
if (InventoryOverlay)
{
InventoryOverlay->SetVisibility(ESlateVisibility::Hidden);
if (InventoryBox)
{
InventoryBox->ClearChildren();
InventoryBox->InvalidateLayoutAndVolatility();
InventoryBox->SetVisibility(ESlateVisibility::Hidden);
}
}
if (ActionOverlay)
{
ActionOverlay->SetVisibility(ESlateVisibility::Hidden);
}
if (EquipButton)
{
EquipButton->OnClicked.AddDynamic(this, &APlayerCharacterHUD::OnEquipButtonClicked);
}
}
void APlayerCharacterHUD::ToggleInventory(UInventoryComponent* Inventory)
{
if (!InventoryOverlay || !Inventory) return;
isShouldShow = InventoryOverlay->GetVisibility() != ESlateVisibility::Visible;
InventoryOverlay->SetVisibility(isShouldShow ? ESlateVisibility::Visible : ESlateVisibility::Hidden);
InventoryBox->SetVisibility(InventoryOverlay->GetVisibility());
APlayerController* PC = Cast<APlayerController>(GetOwningPlayerController());
if (!PC) return;
if (isShouldShow)
{
UpdateInventoryUI(Inventory);
PC->bShowMouseCursor = true;
FInputModeGameAndUI InputMode;
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
PC->SetInputMode(InputMode);
MoneyInfo->SetText(FText::Format(NSLOCTEXT("Inventory", "MoneyDisplay", "Money: {0}"), FText::AsNumber(Inventory->GetMoneyAmount())));
ActionOverlay->SetVisibility(CurrentlySelectedSlot ? ESlateVisibility::Visible : ESlateVisibility::Hidden);
}
else
{
PC->bShowMouseCursor = false;
PC->SetInputMode(FInputModeGameOnly());
ActionOverlay->SetVisibility(ESlateVisibility::Hidden);
if (CurrentlySelectedSlot)
{
CurrentlySelectedSlot->SetSelectedItem(false);
CurrentlySelectedSlot = nullptr;
}
SelectedItemRef = nullptr;
}
}
void APlayerCharacterHUD::UpdateInventoryUI(UInventoryComponent* Inventory)
{
if (!InventoryBox || !Inventory) return;
InventoryBox->ClearChildren();
for (AAbstractItemActor* Item : Inventory->GetItems())
{
if (!Item) continue;
if (UInventorySlot* Slot = CreateWidget<UInventorySlot>(GetWorld(), InventorySlotClass))
{
Slot->SetupSlot(Item);
Slot->OnSlotClicked.AddDynamic(this, &APlayerCharacterHUD::OnInventorySlotClicked);
Slot->SetVisibility(ESlateVisibility::Visible);
InventoryBox->AddChildToWrapBox(Slot);
}
}
InventoryBox->InvalidateLayoutAndVolatility();
}
void APlayerCharacterHUD::OnInventorySlotClicked(AAbstractItemActor* Item)
{
if (!Item) return;
if (SelectedItemRef == Item && CurrentlySelectedSlot)
{
CurrentlySelectedSlot->SetSelectedItem(false);
CurrentlySelectedSlot = nullptr;
SelectedItemRef = nullptr;
ActionOverlay->SetVisibility(ESlateVisibility::Hidden);
return;
}
if (CurrentlySelectedSlot)
{
CurrentlySelectedSlot->SetSelectedItem(false);
}
// Find the clicked slot by iterating through children
for (UWidget* Widget : InventoryBox->GetAllChildren())
{
if (UInventorySlot* Slot = Cast<UInventorySlot>(Widget))
{
if (Slot->GetItem() == Item)
{
CurrentlySelectedSlot = Slot;
break;
}
}
}
if (CurrentlySelectedSlot)
{
CurrentlySelectedSlot->SetSelectedItem(true);
SelectedItemRef = Item;
ActionOverlay->SetVisibility(ESlateVisibility::Visible);
}
else
{
ActionOverlay->SetVisibility(ESlateVisibility::Hidden);
SelectedItemRef = nullptr;
}
}
void APlayerCharacterHUD::OnEquipButtonClicked()
{
if (!SelectedItemRef) return;
AActor* Player = UGameplayStatics::GetActorOfClass(GetWorld(), APlayerCharacter::StaticClass());
if (!Player) return;
if (IPlayerCharacterInterface* PlayerInterface = Cast<IPlayerCharacterInterface>(Player))
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, TEXT("Item Equipped!"));
PlayerInterface->EquipItem(SelectedItemRef);
if (!PlayerInterface->GetEqupiedItem())
{
if (CurrentlySelectedSlot)
{
CurrentlySelectedSlot->SetSelectedItem(false);
}
SelectedItemRef = nullptr;
}
}
}
bool APlayerCharacterHUD::bIsHUDActive()
{
return isShouldShow;
}
I would glad for any explanation of why it’s not working properly (because I struggling with that for couple of day)