Hiya Guys,
I have spent the past couple days re-writing my game serialization so it supports objects at runtime. I’ve got this entirely working within the Editor but last night I thought I had needed to get ready for a playtest, I packaged my build and the game is executing in a different order.
All of the game data is being read correctly however I did not realise the order of executions changed in a packaged game.
Could anyone help out with this problem? Below I have included all the code of the relevant class.
void AClimbingInCPPGameMode::SetSlotName(FString NewSlotName)
{
// Ignore empty name
if (NewSlotName.Len() == 0)
{
return;
}
CurrentSlotName = NewSlotName;
}
void AClimbingInCPPGameMode::WriteSaveGame()
{
Debug::Print(“Running write save game”);
Debug::Print(CurrentSlotName);
auto GameInstance = Cast(UGameplayStatics::GetGameInstance(GetWorld()));
if (GameInstance == nullptr)
{
// Warn about failure to save?
Debug::Print(“FAILURE TO SAVE : GAME STATE == NULL”);
return;
}
if (UGameplayStatics::DoesSaveGameExist(CurrentSlotName, 0))
{
if (CurrentSaveGame)
{
CurrentSaveGame->SavedActors.Empty();
}
}
// Iterate the entire world of actors
for (FActorIterator It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
if (Actor->Implements())
{
FActorSaveData ActorData;
ActorData.ActorName = Actor->GetFName();
ActorData.Transform = Actor->GetActorTransform();
ActorData.ActorClass = Actor->GetClass();
FString LevelName;
LevelName = GetWorld()->GetMapName();
// Pass the array to fill with data from Actor
FMemoryWriter MemWriter(ActorData.ByteData);
FObjectAndNameAsStringProxyArchive Ar(MemWriter, true);
// Find only variables with UPROPERTY(SaveGame)
Ar.ArIsSaveGame = true;
// Converts Actor's SaveGame UPROPERTIES into binary array
Actor->Serialize(Ar);
//Debug::Print("Object added to save data: " + ActorData.ActorName.ToString());
CurrentSaveGame->SavedActors.Add(ActorData);
CurrentSaveGame->LevelName = LevelName;
}
}
if (UGameplayStatics::DoesSaveGameExist(CurrentSlotName, 0))
{
UGameplayStatics::SaveGameToSlot(CurrentSaveGame, CurrentSlotName, 0);
UpdateCurrentSaveGameData();
Debug::Print("Game saved");
}
}
void AClimbingInCPPGameMode::LoadSaveGame()
{
FString LevelName;
LevelName = GetWorld()->GetMapName();
auto GameInstance = Cast(UGameplayStatics::GetGameInstance(GetWorld()));
// Update slot name first if specified, otherwise keeps default name
FString ProjectPath = FPlatformMisc::ProjectDir();
SavePath = ProjectPath + “Saved/SaveGames/” +GameInstance->CurrentlyLoadedSaveState;
CurrentSlotName = SavePath + LevelName;
Debug::Print(SavePath + LevelName + “: This is the current save slot”, 10,10);
if (UGameplayStatics::DoesSaveGameExist(CurrentSlotName, 0))
{
CurrentSlotName.RemoveFromStart(“Current”, ESearchCase::CaseSensitive);
auto LoadedSave = UGameplayStatics::LoadGameFromSlot(CurrentSlotName, 0);
CurrentSaveGame = Cast(LoadedSave);
if (!CurrentSaveGame)
{
Debug::Print(“Failed to load SaveGame Data.”);
return;
}
TArray allActors;
for (FActorIterator It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
FName ActorName = Actor->GetFName();
allActors.Add(ActorName);
}
for (FActorSaveData& ActorData : CurrentSaveGame->SavedActors)
{
if (!allActors.Contains(ActorData.ActorName))
{
if (!ActorData.ActorName.ToString().Contains("ClimbCharacter"))
{
AActor* newlySpawned = GetWorld()->SpawnActor(ActorData.ActorClass);
newlySpawned->SetActorTransform(ActorData.Transform);
Debug::Print( "Name of Actor" +newlySpawned->GetName() + "Location" + newlySpawned->GetActorLocation().ToString(),FMath::RandRange(0, 100000000),10);
}
}
}
// Iterate the entire world of actors
for (FActorIterator It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
// Only interested in our 'gameplay actors'
if (Actor->Implements<USaveInterface>())
{
for (FActorSaveData ActorData : CurrentSaveGame->SavedActors)
{
if (ActorData.ActorName == Actor->GetFName())
{
Actor->SetActorTransform(ActorData.Transform);
FMemoryReader MemReader(ActorData.ByteData);
FObjectAndNameAsStringProxyArchive Ar(MemReader, true);
Ar.ArIsSaveGame = true;
// Convert binary array back into actor's variables
Actor->Serialize(Ar);
ISaveInterface::Execute_OnActorLoaded(Actor);
break;
}
}
}
}
//OnSaveGameLoaded.Broadcast(CurrentSaveGame);
}
else
{
NewSaveGameData(CurrentSlotName, GetWorld()->GetMapName(), GameInstance->CurrentlyLoadedSaveState);
}
}
void AClimbingInCPPGameMode::NewSaveGameData(FString SlotName, FString MapName, FString DataSlot)
{
// Create the directory
CurrentSaveGame = Cast(UGameplayStatics::CreateSaveGameObject(UCustomSaveGame::StaticClass()));
if (DataSlot.IsEmpty())
{
CurrentSlotName = SlotName + “/” + “Current”;
CurrentSaveGame->LevelName = nullptr;
}
else
{
CurrentSlotName = SlotName;
CurrentSaveGame->LevelName = MapName;
}
UGameplayStatics::SaveGameToSlot(CurrentSaveGame, CurrentSlotName, 0);
Debug::Print(“No save game found : Created new save game data”);
}
void AClimbingInCPPGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
if (ShouldRun)
{
FString SelectedSaveSlot = UGameplayStatics::ParseOption(Options, “SaveGame”);
CurrentSlotName = SelectedSaveSlot;
LoadSaveGame();
UpdateCurrentSaveGameData();
Debug::Print(“Init game data”, 2, 1);
}
}
void AClimbingInCPPGameMode::PostInitializeComponents()
{
Super::PostInitializeComponents();
}
void AClimbingInCPPGameMode::BeginPlay()
{
Super::BeginPlay();
}
void AClimbingInCPPGameMode::UpdateCurrentSaveGameData()
{
auto GameInstance = Cast(UGameplayStatics::GetGameInstance(GetWorld()));
auto tempSlot = GameInstance->CurrentlyLoadedSaveState;
CurrentSlotName = SavePath + “Current”;
//Debug::Print(CurrentSlotName);
FString LevelName = GetWorld()->GetMapName();
if (UGameplayStatics::DoesSaveGameExist(CurrentSlotName, 0))
{
CurrentSaveGame->LevelName = LevelName;
UGameplayStatics::SaveGameToSlot(CurrentSaveGame, SavePath+LevelName, 0);
UGameplayStatics::SaveGameToSlot(CurrentSaveGame, CurrentSlotName, 0);
Debug::Print(“Game saved”);
//Debug::Print(SavePath+LevelName, 0, 5);
}
}