sinbad/SUQS

Question about SaveGame approach

mwise opened this issue · 2 comments

mwise commented

I have a probably very stupid question about how to construct my USaveGame subclass to store the QuestProgression object.

I have read the docs page about Saving and it seems simple enough. However, whenever my game attempts to load a save game with the persisted progression data, it explodes in Serialize because the USuqsQuestProgression* QuestProgression has an invalid memory address (all zeroes).

I'm storing my game's progression object on a LocalPlayerSubsystem, which seems to work fine, including assigning it to the SaveGame during the save process.

So, my question is this: How should I set up my save game so that I can be sure that the quest progression is always valid?

Here is my subclass content that's exploding on load:

// UMySaveGame.h
class MYGAME_API UMySaveGame: public USaveGame
{
	GENERATED_BODY()

public:
	UMySaveGame();
	
	UPROPERTY(VisibleAnywhere, SaveGame, BlueprintReadOnly, Category = "Campaign|Quests")
	USuqsProgression* QuestProgression;

private:
	virtual void Serialize(FArchive& Ar) override;
}	
// UMySaveGame.cpp
UMySaveGame::UMySaveGame()
{
	QuestProgression = NewObject<USuqsProgression>(this, "QuestProgression");
}

void UMySaveGame::Serialize(FArchive& Ar)
{
	Super::Serialize(Ar);
	QuestProgression->Serialize(Ar);
}

And here is the code in the LocalPlayerSubsystem subclass that creates the SaveGame and assigns the progression:

// in MyLocalPlayerSubsystem::WriteSaveGame
CurrentSaveGame = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass()));
CurrentSaveGame->QuestProgression = QuestProgression;

The subsystem then keeps this save game in memory, assigning new values to its properties before saving it when the user saves the game.

I have loved using SUQS to build out my quests, so I'm eager to figure out what dumb thing I'm doing and move on to the next one! Thanks for any insight you can provide.

sinbad commented

Hi, glad you like SUQS! The issue is probably that UE actually doesn't call the basic no-argument constructor in most circumstances, so on restore you're not actually getting that NewObject called in the UMySaveGame constructor. Instead a constructor with the argument FObjectInitializer& ObjectInitializer is being called, which doesn't have that NewObject in it.

It's a bit odd, but unless you're always constructing UObjects yourself (in which case the no-arg constructor is fine), you should always implement a constructor which takes a FObjectInitializer& ObjectInitializer argument, because that's the route UE will go through when constructing your classes.

mwise commented

Thanks for the info and quick reply. I've got it working now using the FObjectInitializer argument, but also with a bunch of checks on initialization of the quest progression object that I feel like I shouldn't need. I've also had a few crashes that I'm trying to nail down. I'm going to close this out, since it's definitely not a problem with SUQS, per se. If I can nail down exactly what the issue turns out to be, I'll post back for posterity..