UE4 C++ saving inventory TArray of item pointers across multiple levels - c++

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:
https://answers.unrealengine.com/questions/35618/savingloading-an-array-of-objects.html
https://www.ue4community.wiki/legacy/savegame-pointers-and-structs-8wlg0qms
Another approach to saving game data

Related

UE4 How to use UMG ListView to display a list of Structs?

I apologize if this seems trivial, but I've searched for an answer for a while now, and I can't seem to find a solution.
I have a list of structs (TArray of structs to be exact.) that represent high scores in my game. (Each struct represents a high score, the fields are something like "Name, Date, Player Score, Game Mode etc" I use this SaveGame method to save a load my array of high scores to and from a file.
I used this with mock values and the system works, now I want to create a UMG widget that will display the list of high scores and this is where I ran into a snag.
I want to use a ListView to show each struct as a ListViewEntry. As far as I understand(I was following this tutorial), A UMG List View needs it's entry widgets to implement the IUserObjectListEntry specifically one has to implement the OnListItemObjectSet (UObject* ListItemObject) method. This method is responsible for assigning an object to the Listview entry and mapping its fields to the various visual widgets. You can see my problem now, I have a list of UStructs and this method needs a UObject pointer.
I'm really at a loss at what I need to do to make this work with a UStruct. Short of creating a dummy UObject that's pretty much identical to my struct and before passing the struct to this function I need to copy its fields into the dummy UObject and pass it instead. I think this method is very inelegant. There has to be a better way. Do you know any?
I wanted to avoid creating a dummy UObject just for the sake of passing it to this function.
I tried to use an array of UObjects instead of an array of Structs but the problem is, an array of UObjects is always an array of pointers, and when it gets saved, the pointers getting saved and not the actual data, so when it's loaded the data is useless.
Maybe there is a Struct-specific interface one can implement for a ListViewEntry widget? Or maybe there is a way to dereference the pointers of the array of Uobjects before saving them?
TL;DR
I have the following stuct:
c++
USTRUCT()
class FHighScoreEntry
{
GENERATED_BODY()
public:
//Player name
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString PlayerName;
//Player Score
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 PlayerScore;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FDateTime CurrentDateTime;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TEnumAsByte<EGameType> GameType;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 AccuracyTrialMaxTries;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 TimeTrialMaxTime;
}
In the following array;
c++
TArray<FHighScoreEntry> HighScores;
I want to show the array of high scores in a UMG ListView. The ListView requires its entries to implement the User List Object interface, which has this function:
As you can see, the event only accepts UObjects. Hence my problem.
This was asked 8 months ago, so may no longer be useful to you. But this post is the only thing I could find when searching for this issue on the Internet, so I am posting my solution for the next person researching this.
At a high level, create a UObject wrapper for the struct.
In my USaveGame class I have an array of structs because as you mentioned, an array of UObject pointers does not actually save any data. I created an UObject derived class that simply contains the same struct as the sole UPROPERTY.
UCLASS(Blueprintable, BlueprintType)
class PORTALTEST_API UHighScoreObject : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Score")
FHighScore HighScore;
};
In my Game Instance class, I have an array of pointers to this UObject
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Score")
TArray<UHighScoreObject*> HighScoreArray;
I use this array of UObject pointers for the List View of the widget.
HighScoreWidgetBlueprint
In the Save function of my Game Instance class I clear the struct of arrays and fill it with with the data contained in the array of UObject pointers. (I am only keeping the top ten high scores, so this seemed more efficient than keeping track of changes in both arrays.)
bool UMyGameInstance::SaveHighScore()
{
// Call SaveGameToSlot to serialize and save our SaveHighScoreObject with
//name HighScoreSaveSlot.sav
// Retrieve save values
SaveHighScoreObject->HighScoreArray.Empty();
for (auto HighScore : HighScoreArray)
{
SaveHighScoreObject->HighScoreArray.Add(HighScore->HighScore);
}
// Save game to file
const bool IsSaved = UGameplayStatics::SaveGameToSlot(SaveHighScoreObject,
UNIQUE_HIGHSCORE_SLOT, 0);
return IsSaved;
}
And in the Load function of my game instance I read in the array of structs and populate the array of UObjects.
bool UMyGameInstance::LoadHighScore()
{
// Try to load a saved game file with "HighScoreSaveSlot.sav if it exists
USaveGame* LoadedHighScore =
UGameplayStatics::LoadGameFromSlot(UNIQUE_HIGHSCORE_SLOT, 0);
SaveHighScoreObject = Cast<UHighScoreSaveGame>(LoadedHighScore);
//If the file does not exist, create a new one
if (!SaveHighScoreObject)
{
// Instantiate a new SaveGame object
SaveHighScoreObject = Cast<UHighScoreSaveGame>
(UGameplayStatics::CreateSaveGameObject(UHighScoreSaveGame::StaticClass()));
// Call SaveGameToSlot to serialize and save our game object with name
// "HighScoreSaveSlot.sav"
const bool IsSaved =
UGameplayStatics::SaveGameToSlot(SaveHighScoreObject, UNIQUE_HIGHSCORE_SLOT, 0);
return IsSaved;
}
else
{
for (auto HighScore : SaveHighScoreObject->HighScoreArray)
{
UHighScoreObject* HighScoreObj = NewObject<UHighScoreObject>
((UObject*)GetTransientPackage(), UHighScoreObject::StaticClass());
HighScoreObj->HighScore = HighScore;
HighScoreArray.Add(HighScoreObj);
}
return true;
}
}
ListView is made to represent unique objects, so its items need to be UObject, that’s the way the list view class is made.
That’s because adding/removing/looking up the widget for an item needs to be very fast. An object pointer is just a memory address, so it’s fast to find an item, and you can be sure they’re unique (your list won’t accidentally show two widget for the same object). Structs on the other hand, are any arbitrary data, which can be very long (depends on what they contain). So unless you make a hashing algorithm, it’s very expensive to look up if a struct is already in the list.
So for your needs, you can use objects instead of structs to show high scores. For example, objects for each player, since the players are probably already objects. The widget can then cast to the player class when on item object set, and take the high score variable of the player to show it.
If you want to use structs though, you can create a custom widget to show the high scores. To make a vertical list, just make a VerticalBox in your parent widget and a number of widgets for each item in your list, using the create widget from class node (or NewObject in cpp). Then, add your widgets as children of vertical box using the add child to vertical box function.

Populating a vector of objects within a class

and I've hit my first wall on my coding project/assignment.
I'm to implement functionality into code that's been done to some stage, and I cannot alter the given code so I have to work around the given structure.
The code, in a nutshell, reads family relations from a text file and populates database with the family relation data and later on allows user to print out information he wants to access.
What I'm having trouble with is understanding how I can and how I have to utilize a struct given to me in the assignment. The struct is
struct Person
{
std::string id_ = NO_ID;
int height_ = NO_HEIGHT;
std::vector<Person*> parents_{nullptr, nullptr};
std::vector<Person*> children_;
};
and I'm using it at least in the initialization phase of the data structure.
I start by calling the process in main.cpp with
database->addRelation(it->child_, it->parents_, std::cout);
In the naming/height adding phase I'd simply do it with
MyPerson.id_ = id;
MyPerson.height_ = height;
where MyPerson is defined by Person MyPerson;
but as far as I can tell, I have to somehow access the object pointers to be able to populate the vectors for when I want to add children/parents to the person.
The class functions that are called when initializing person's name, height and family relations are these two:
void Familytree::addNewPerson(const string &id, const int &height, ostream &output)
{
MyPerson.id_ = id;
MyPerson.height_ = height;
}
void Familytree::addRelation(const string &child,
const std::vector<string>
&parents, ostream &output)
{
}
The addRelation fuction is what I'm having a hard time getting to work. Simply appending the strings to it won't work since it expects Person* -objects, which are, as far as I can tell, just pointers to the other Persons, but I'm not sure how I can access them.
Also, let me know if anything here is excessive or if I'm missing anything crucial, I'll edit it to the best of my ability
Editing with additional information:
The only things I've added myself that can be seen here is
Person MyPerson;
and the contents of the class function addNewPerson. The other snippets I can not change in any shape or form.
Edit#2
Current progress, debatable whether I'm closer or further from the goal
My persons map is using Personmap = std::map<std::string, Person >;
and I'm using it in addNewPerson with
persons_[id] = id;
persons_[id] = height;
, but I'm still randomly trying different things to try and make it work for the next phase where I need to somehow add the objects to the vectors.
The biggest problem I have is the fact that I do now know how to play around the difference of *Person and Person

A little confused on scope and how to create nested structs for directory tree

I'm trying to show a directory tree UI of an unknown amount of nested folders that is read from a flat file. My idea was to use a struct that could point to children and parents like so:
struct audioSelectTreeItem {
FString folderName;
FString folderPath;
struct audioSelectTreeItem* folderParentItem;
TArray<struct audioSelectTreeItem*> childFolderItems;
};
When I read the file of saved directories I save any subfolders in childFolderItems and the parent in folderParentItem. That way in my interface if someone wants to go up a folder I look at folderParentItem and then list out it's subfolders with childFolderItems.
The problem I'm running into is how to save this. I made a function that loops through folders and children and can create all these structs and save them. However the issue I think is if I create a struct in a loop then save a pointer to it in the previous struct (children folders) I cannot access that from anywhere else in the program.
Is there a way to accomplish creating these more permanent, and also how would I go about cleaning up when done?
Edit: Wrote as if I'm reading live directories. Was a text file with directory information I need to put into a tree interface.
Update:
Here's a smaller example of what the loop does:
void AMyPlayerController::showAudioPicks() {
for (int counter = startCount; counter < fileStringArray.Num(); counter++) { //startcount 3
audioSelectTreeItem newTreeItem;
newTreeItem.folderPath = fileStringArray[counter];
mainListTree.childFolderItems.Add(&newTreeItem);
...
So my issue lies with creating these new audioSelectTreeItem's to add nested in structs but keep them accessible elsewhere in the program.
What you are doing here is creating a pointer to a stack variable which is scoped to the for loop and destroyed on every loop:
audioSelectTreeItem newTreeItem;
newTreeItem.folderPath = fileStringArray[counter];
mainListTree.childFolderItems.Add(&newTreeItem);
If you want to create an object (or structure) on the heap instead of the stack, you have to use new:
audioSelectTreeItem *newTreeItem = new audioSelectTreeItem;
newTreeItem->folderPath = fileStringArray[counter];
mainListTree.childFolderItems.Add(newTreeItem);
But if you do so, you have to make sure the object is freed with delete. Typically this is done in the destructor of the audioSelectTreeItem class (or structure):
class audioSelectTreeItem {
public:
~audioSelectTreeItem()
{
for (int i = 0; i < childFolderItems.Num(); i++)
delete childFolderItems[i];
}
FString folderName;
FString folderPath;
class audioSelectTreeItem* folderParentItem;
TArray<class audioSelectTreeItem*> childFolderItems;
};
The parent is the owner of its children, so there is no sense in deleting folderParentItem. As soon as you delete the root its destructor is deleting all of his children and so on.
In current C++ there are safer solutions for trees like this. For example you could use managed pointers instead of plain pointers. This creates a bit of an overhead because of reference counting, but you never have to free the heap yourself:
#include <memory>
class audioSelectTreeItem
{
public:
FString folderName;
FString folderPath;
std::weak_ptr<audioSelectTreeItem> folderParentItem;
TArray<std::shared_ptr<audioSelectTreeItem>> childFolderItems;
};
Do not use a shared_ptr for the folderParentItem reference. This would lead to cyclic dependency (parent references child and child references parent, so none of them can be deleted)
And a personal opinion: Type names should start with an upper case letter.

text adventure - how to add items to 'inventory' struct/class without defining each in advance?

So far this is the most awkward thing I've come about. I have it set for integers to mark how many potions, keys, a player has, but I'm not sure exactly how I can get random items, like rocks, CPU (in the case of Dunnet), stick, shovel, etc.
I don't want to have to figure out every item in the game and assign it a variable. There has to be an easier way. I thought of using two arrays, one a string and one an int, to do the job - but this wont work for a variety of reasons one being I can't do string stringname[10], I see problems associating the two, and... the list goes on, I'm sure it just wont work that way.
Everything else is a class btw, I don't like using structs (but this is going to be used throughout the code, and accessed everywhere), so far my code is:
struct Inventory{
int Keys;
int Potions;
int getinventory() const { return Keys, Potions; }
void addkey(int amt){ Keys += amt; }
void addpotion(int amt){ Potions += amt; }
void usepotion(){Potions -= 1;}
void usekey()
{
if (Keys >> 0)
{
Keys -= 1;
}
else if (Keys << 1)
{
cout << "You do not have a key!" << endl;
}
}
};
I'm definitely still working on the getinventory(), because well, I'm not sure what I'm doing with this code, or even if I'm using it. is the only way I'm going to get this to work, to define EACH variable as I create it in the game and add it in?
I was going to handle weapons and monsters this way... but it just sucks not having a dynamic system for an inventory. I'd like to focus on parsing user input and not have to go back into the header where my main classes are consistently... plus I haven't even fully written the story yet, so I don't know whats happening...
The way this is addressed in LPMuds (and similar) is to create a generic object template. The generic template would have things like a short description, long description, define weight, value, etc.
Specific object types then inherit this class. For example, a potion is an object with all of those attributes but it also has additional actions (functions) that can be taken and possibly different attributes... Taste and color, for example.
Weapons can inherit from that general class, defining things like damage and hit percentage as a generalized notion. A sword can then inherit this weapon (that inherits the generic object) and can be further refined.
In this way, you simply need your inventory to be able to handle a generic object. The objects themselves may define additional attributes and actions. This also means that you don't need to predefine every single object as its own unique variable.
What about creating a structure like this:
struct InventoryItem
{
enum { Key, Potion, Rock, Stick, Shovel } type_;
unsigned int num_;
}
and then have Inventory contain something like a std::vector of InventoryItem.

Multi-site execution : Getting rid of virtual table with inheritance (legacy code)

I've been stuck for some time on this problem, and I need your help.
My C++ application is running on multiple exec sites. My problem is that I cannot pass objects holding a virtual table, because sites do not share memory (thus a virtual method from a given object will lead to an undefined behaviour). By "I cannot pass" I mean : I do not want any virtual table.
The fun thing is there's not only inheritance, but also templates and eerie conception...
Here is the code
// "Main" code
List< Animals, 5 > list;
List< Animals, 8 > list2;
list.concatenate( list2 );
// GenericList.hpp
template< Type >
class GenericList
{
virtual getBuffer(void) = 0;
virtual getSize(void) = 0;
void concatenate( GenericList<Type> gList)
{
int size = gList.getSize(); // Call to the child...
...getBuffer()...
// processing, etc.
}
}
// List.hpp
template< Type, Size_ >
class List : public GenericList< Type >
{
int getSize()
{
return Size_;
}
Type * getBuffer()
{
return buffer;
}
Type buffer[Size_];
}
How can I get rid of inheritance ?
EDIT/ In light of the first few answers, I can tell you that I cannot implement a better serialization, the code being private.
If you just want to get rid of virtual tables, you don't have to get rid of inheritance. You have to get rid of virtual functions. Looking at the code you post, maybe you can make a few changes so that getSize and getBuffer are in GenericList, so you can make them non-virtual, but that really depends on the rest of your code.
The first question is, however, why would you worry about virtual tables in the first place? When you serialize the objects, you should serialize their data in order to preserve their state, and the state is the only thing you should pass around.
I think you are blaming the wrong part of the problem there... if you have a distributed system, you have to make sure that the serialized data that is sent on the wire contains enough information to rebuild the state of the object on the other end of the connection.
I believe that the problem you are facing is that you are sending raw data over the wire, while you should have a serialization mechanism that is able to encode the actual type of the object being sent and rebuild the object on the opposite end with the exact same type. In the case of an object belonging to a class with virtual functions, that will mean that the two objects are not equal bitwise, as on each end of the connection the pointer to the vtable will refer to a different location in memory, but they will be semantically equal, which is what you need to be able to process the objects on the other end.