Internal database of collectible notes and how to implement it

I’m trying to find an easy way to achieve this.

I want to have notes and documents spread around the game world. But in order to avoid creating many different interactable notes, I thought of creating just 1 and link it to a numeric tag. Every time I place an instance of the note I choose the tag I want to link it to

When the player finds a note, I would then search its tag inside an internal “database” and flag the record as “found”. When a record is found, it can be displayed in the UI widget (basically the widget always stores all notes that are flagged as found. When you find a new note, the widget is alerted that it should refresh its list of notes)

Point is, I don’t really know what structures are more adequate for storing all these notes without having to create a dedicated CPP class that is loaded on game start. It would be a static piece of data and I definitely don’t want to store it in a gigantic array. I read of data tables but I’d like to ask to you knowledgeable people if they are adequate for my purpose or if there’s something simpler I could use

What’s wrong with a TMap ?

I would need to store it in a class and then add new entries as development goes on. I would need to reference this class during gameplay and moreover, it would be a map of structs containing name of the note, content, path to the texture associated and the Boolean flag.

I was hoping in something more structured, like a file that is loaded on game start.

TMap is a valid type of a UPROPERTY and I think will serialize to a package just fine. The key would be your note id and I think you can make the value type a USTRUCT that has properties for the name / content / texture reference etc. You can fill it out in the editor through a blueprint and then use the map at runtime to look up the properties for a given note.

1 Like

Have a look at DataTables

Data Tables aren’t great for runtime modification and savegames, though.

I second the suggestion to use a TMap. It can be configured in blueprint, or in C++.
You should probably hook it up to the player state, or the game state, or maybe the game instance if it’s only ever going to be a single-player game.

If you have many levels that each have their own set of notes, another option is to make an actor of a known class, whose job it is to contain the note information, and add one actor of this class to each level.

Separately, don’t be afraid of creating actor instances for each note. You could even have the “has been found” state on the actor itself, assuming you hook it up to savegame. To find all notes, just use “find all actors of class” and use your BP_Note class as the argument. If you still need a separate list of all the notes, make that list in the game state or whatever on begin play, and after re-loading the savegame.

1 Like

I guess I could hook a map to the GameInstance. It’s a single player game so it wouldn’t be an issue, although having a giant map amidst other relevant stuff that I need to keep in the GameInstance was something I didn’t really want.

Although I could simplify it using the second suggestion you gave (an actor linked to that level’s notes) but then I’d still need to “send” the notes held by this actor to the game instance once the level ends, since the player would need to keep notes in between levels.

I didn’t want to use an actor for each note because I was thinking of maybe using DataAssets instead. And because I didn’t want many different actors BP_Notes all virtually identical except for the text and maybe sometimes the texture that needs to be displayed.

So all in all I guess I could for:

  • Each instance of BP_Note contains a numeric tag, the index of the Notes_DataAssets
  • GameInstance holds all the available Notes_DataAssets
  • When a BP_Note is found, the GameInstance registers that index of Notes_DataAsset as “found”
  • When the WidgetInventory is opened, it requests all the Notes_DataAssets marked as found and displays them

It doesn’t feel very optimized though. I can’t search the map very fast since I need to scroll through each record to find the ones marked as found, and there’s the impracticality of having to define each note in the GameInstance and then use its index in my BP_Note. I avoid replicating the DataAsset between GameInstance and the BP_Notes actor but now the BP_Notes instance isn’t “readable”, I have to go back to the GameInstance every time to know what that BP_Note actually is.

  • DataAsset table that contains the note id, and the text and whatever other info
  • game instance or wherever you put your save data has a TSet prop that contains a list of all the NoteIds that have been collected
  • When a BP_Note is found (or if time is of the essence, cache whatever info you need from the DataAsset on BeginPlay in the note), grab the data from the DataAsset, mark it found in the TSet
  • When opening the data assets, go through the TSet and search the DataAsset for the ones found.
1 Like

And this would make iterating through the set simpler and more direct. I’ll look into this, seems exactly what I needed

“Giant” compared to what? The number of actors in a level will go to tens or even hundreds of thousands. The number of vertices or indices in a single mesh is certainly higher than the number of notes you will place across your levels. If you’re worried about “size” in the sense of memory consumption, I don’t think you need to worry.

You can have an actor that creates a note instance from each DataAsset if you want. But how do you know where those notes will go? How do you place them in the editor? Anything with a position, placeable in the editor, pretty much is an Actor. That’s kind-of the Unreal Way.

That’s exactly what actor instances are for! You edit the properties of each instanced actor to be the text and texture changes you need, after you place them in the level.

“Optimized” in what sense? Unreal won’t ever run on an 8-bit Arduino. Typically, you optimize for artist ability and developer time, and nothing that you place manually, will be placed in enough counts that their number will at all show up in a memory profile in a modern game. You don’t have enough hours in the day to place manual instances enough that it matters :slight_smile: (Now, automatic placement, like foliage/grass, procedural rocks, leaves on a tree, and so on – that’s a different matter!)

I guess I don’t understand what you’re “optimizing” for. What real problem are you actually trying to solve?

1 Like

Another option to consider in terms of where to load and access the note map would be a UGameInstanceSubsystem UGameInstanceSubsystem | Unreal Engine Documentation It shares the lifetime of the GameInstance so will persist across worlds, but doesn’t modify the GameInstance object itself. More info about subsystems here: Programming Subsystems | Unreal Engine 4.27 Documentation

Your project sounds great! To store and manage these notes efficiently without overloading memory, you might consider using data tables or similar structured data formats. They can help organize your notes by tagging and managing their state (like found or not found) effectively. You can also look up some online note websites for references on structuring data for efficient retrieval. Good luck with your game project!