Hiya Guys, I am finishing up my save game system but I have come into a bug that I cannot seem to fix.
My character loads perfectly fine in the editor however when I package the game it has a completely different behaviour. My character will always spawn at the Player Start no matter what.
I have attempted manually setting the actor transform in my save game however I cannot get it working.
Everything else works perfectly fine, I just can’t for the life of me figure out how to properly load player state.
Any help would be appreciated!
// Copyright Epic Games, Inc. All Rights Reserved.
#include "ClimbingInCPPGameMode.h"
#include "ClimbingInCPPCharacter.h"
#include "EngineUtils.h"
#include "Kismet/BlueprintTypeConversions.h"
#include "Kismet/GameplayStatics.h"
#include "Serialization/ObjectAndNameAsStringProxyArchive.h"
#include "Serilize/CustomGameInstance.h"
#include "Serilize/CustomSaveGame.h"
#include "Serilize/SaveInterface.h"
#include "UObject/ConstructorHelpers.h"
AClimbingInCPPGameMode::AClimbingInCPPGameMode()
{
}
void AClimbingInCPPGameMode::StartPlay()
{
Super::StartPlay();
}
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<UCustomGameInstance>(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<USaveInterface>())
{
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 Actors 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<UCustomGameInstance>(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<UCustomSaveGame>(LoadedSave);
if (!CurrentSaveGame)
{
//Debug::Print("Failed to load SaveGame Data.");
return;
}
TArray<FName> allActors;
for (FActorIterator It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
FName ActorName = Actor->GetFName();
allActors.Add(ActorName);
allWorldActors.Add(Actor);
}
for (FActorSaveData& ActorData : CurrentSaveGame->SavedActors)
{
if (!allActors.Contains(ActorData.ActorName))
{
if (!ActorData.ActorName.ToString().Contains("ClimbCharacter"))
{
AActor* newlySpawned = GetWorld()->SpawnActor(ActorData.ActorClass);
ActorData.ActorName = FName(*newlySpawned->GetName());
allActors.Add(FName(*newlySpawned->GetName()));
ActorData.RuntimeActor = true;
allWorldActors.Add(newlySpawned);
//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 actors variables
Actor->Serialize(Ar);
ISaveInterface::Execute_OnActorLoaded(Actor);
}
}
}
}
//OnSaveGameLoaded.Broadcast(CurrentSaveGame);
}
else
{
NewSaveGameData(CurrentSlotName, GetWorld()->GetMapName(), GameInstance->CurrentlyLoadedSaveState);
}
}
void AClimbingInCPPGameMode::NewSaveGameData(FString SlotName, FString MapName, FString DataSlot)
{
// Create the directory
CurrentSaveGame = Cast<UCustomSaveGame>(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");
}
int FindUniqueId(TArray<int> Array)
{
int UniqueID = FMath::RandRange(0, 1000000000);
while (Array.Contains(UniqueID))
{
UniqueID = FMath::RandRange(0, 1000000000);
}
return UniqueID;
}
void AClimbingInCPPGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
FString SelectedSaveSlot = UGameplayStatics::ParseOption(Options, "SaveGame");
CurrentSlotName = SelectedSaveSlot;
}
void AClimbingInCPPGameMode::MakeUniqueID()
{
for (FActorIterator It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
if (Cast<AInteractionComponent>(Actor))
{
if (Cast<AInteractionComponent>(Actor)->UniqueActorID == 0)
{
int tempID = FindUniqueId(UsedUniqueIDs);
Cast<AInteractionComponent>(Actor)->UniqueActorID = tempID;
//Debug::Print(FString::SanitizeFloat(Cast<AInteractionComponent>(Actor)->UniqueActorID),FMath::RandRange(1000, 100000000),5);
}
}
}
}
AActor* AClimbingInCPPGameMode::FindUniqueID(int IDToFind)
{
for (FActorIterator It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
if (Cast<AInteractionComponent>(Actor))
{
if (Cast<AInteractionComponent>(Actor)->UniqueActorID == IDToFind)
{
return Actor;
}
}
}
Debug::Print("No actor with that unique ID found");
return nullptr;
}
void AClimbingInCPPGameMode::PostInitializeComponents()
{
Super::PostInitializeComponents();
}
void AClimbingInCPPGameMode::BeginPlay()
{
Super::BeginPlay();
if (ShouldRun)
{
LoadSaveGame();
UpdateCurrentSaveGameData();
MakeUniqueID();
//Debug::Print("Init game data", 2, 1);
}
}
void AClimbingInCPPGameMode::UpdateCurrentSaveGameData()
{
auto GameInstance = Cast<UCustomGameInstance>(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);
}
}