I have a basic inventory system in UE4 using a TArray of pointers to my custom Item class. It works fine in individual levels, but when I open a new level, the inventory disappears. I've looked at multiple tutorials and posts about this issue and tried various solutions including migrating my inventory array to the Game Instance, and creating a SaveGame class that holds a copy of the array that saves before and loads after opening a level
After all these, inventory still disappears. My code has changed a lot so it's probably not that helpful but here are some snippets of my current solution.
Declaration in character header
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<AItem*> Inventory_Space;
Declaration in SaveGame
UPROPERTY(SaveGame)
TArray<AItem*> Inventory_Save;
Save and load functions in character implementation
void ABatteryManPlayer::SaveInventory()
{
UBatteryMan_SaveGame* SaveInstance = Cast<UBatteryMan_SaveGame>
(UGameplayStatics::CreateSaveGameObject(UBatteryMan_SaveGame::StaticClass()));
for (int i = 0; i < INVENTORY_SIZE; i++) {
SaveInstance->Inventory_Save[i] = Inventory_Space[i];
}
UGameplayStatics::SaveGameToSlot(SaveInstance, TEXT("Slot0"), 0);
}
void ABatteryManPlayer::LoadInventory()
{
UBatteryMan_SaveGame* SaveInstance = Cast<UBatteryMan_SaveGame>
(UGameplayStatics::CreateSaveGameObject(UBatteryMan_SaveGame::StaticClass()));
UBatteryMan_SaveGame* SaveInstance = Cast<UBatteryMan_SaveGame>
(UGameplayStatics::LoadGameFromSlot("Slot0",0));
for (int i = 0; i < INVENTORY_SIZE; i++) {
Inventory_Space[i] = SaveInstance->Inventory_Save[i];
}
}
Saving after game timer goes to 0 (character implementation)
CurrentTime--;
if (CurrentTime == 0) {
SaveInventory();
Instance->Levels_Complete++;
if (Instance->Levels_Complete < Instance->NUM_LEVELS) {
FName Level_Name = FName(TEXT("Level_" + FString::FromInt(++Instance->Levels_Complete)));
UGameplayStatics::OpenLevel(this, Level_Name, false);
}
Loading back into player inventory in GameMode
void ABatteryMan_GameMode::BeginPlay() {
Super::BeginPlay();
ABatteryManPlayer* Player = Cast<ABatteryManPlayer>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
Player->LoadInventory();
FTimerHandle UnusedHandle;
GetWorldTimerManager().SetTimer(
UnusedHandle, this, &ABatteryMan_GameMode::SpawnPlayerRecharge, FMath::RandRange(2,5), true);
}
I believe that you may have found out why the issue is occurring from our discussion in the comments, but I will try to finish our discussion with this answer. The problem is that you are trying to save an array of pointers to actors in a map. However, the actors in the map get destroyed once you call UGameplayStatics::OpenLevel
in order to change the map. As a result, the pointers in that array end up pointing to garbage data, which is why your game is crashing.
Now, there are many ways to go about this, but you're ultimately going to have to save information about the actors and respawn them. What I have found on Unreal Engine forums is that a common approach is to create a custom struct of type FArchive
for information about these actors, in your case about instances of AItem
. For example, a struct called AItemInfo
which will store info such as the actor's class, the actor's transform, the actor's name, etc., as well as a TArray
member representing a serialized bytestream of other data from an actor (AItem
). Then, serialize the actor into that struct's TArray
member variable using a FMemoryWriter
object. Note that typically you wouldn't serialize all the information about actor, only specific variables/properties marked with the SaveGame
property specifier when you set the ArIsSaveGame
variable in your struct to true. After doing that for each AItem
instance you want to keep track of, you can store each instance of this AItemInfo
struct in an array defined in your custom USaveGame
class. In your case, it's UBatteryMan_SaveGame
. Then, you can call UGameplayStatics::SaveGameToSlot
on your UBatteryMan_SaveGame
instance that contains the array of information structs. When you load that UBatteryMan_SaveGame
instance, you can deserialize the array/sequence of bytes in each AItemInfo
struct in the array with a FMemoryReader
object in order to get the actor information in addition to the other stuff already in the struct and use all of that information to recreate each actor you need from the original map.
Here are a couple of good links that can help you get started: