I have a series of levels in my game that contain varied amounts of a collectible item, some levels contain ~50 whereas some levels can contain upwards of 100.
I need to save how many of the collectibles the player has picked up in each level, but more importantly which exact collectibles they have picked up in each level.
I need to know which exact collectibles have been picked up because I want the ‘un-collected’ collectibles to still be in their correct position should the user leave and re-enter the level at a later time to collect them.
I heard ‘player.Prefs’ is only for things like highscores and shouldn’t be used often so I don’t think I can use that. If anyone can point me in the right direction with the best way I can go about achieving something like this it would be much appreciated
A way to creating a unique ID for each collectable;
A flag to set if the item is collected;
A position to save where the item is found in the world;
Data persistence to save and load the state of each collectable.
In the case where some collectables may be spawned by dynamic actions, you may need further indentifiers, but lets leave that for now.
These are excellent resources.
…
I won’t suggest my solution to the problem below is the best approach but I will describe it to give you food for thought:
I have a ScriptableObject that represents each level, this holds different Lists of collectables that can be collected in my game. On Awake my level manager clears any collectables that may be left in the scene then reads the Level data from a text file and loads them into the each level object. I then Instantiate each collectable in the current scene but only the ones that don’t have the “isCollected” flag set to true are set to Active (some optimisation could be used here such as pooling).
Each of my collectables has a component attached to it that holds the unique ID. When ever I collect a collectable I update the list stored in my Level object to reflect the new state by looking up the list by ID and setting the item collected boolean.
When saving, I just serialize the Levels data using JsonUtility and write it to the text file.
For editing collectables, I built a little command line interface (this could easily replaced with Unity UI buttons) this allows me to do simple quality of life commands while running the project such as resetting all collectables in the text file, updating the text file with newly added collectables or clearing the data. This involves generating new unique IDs for each collectable, as I detest doing anything manually where i can help it.
It’s worth noting that I also hold a boolean called isUsed because in my game collectables can be used to perform actions such as using a Key collectable to open a door that is then no longer available in the game.
Separating collectables by level this way makes it easier for me to quickly count how may have been collected or have yet to be collected to display in Menus. I can see that there is possibly more efficient ways of doing the same thing, perhaps storing all collectables in a single list and include IDs for the collectable type and level.
I appreciate the detailed response. It has made it a lot more clear to me what kind of processes need to be performed to achieve this kind of tracking system, thanks very much
One key factor for me, is: don’t use colliders! They will slow down your game. You could potentially activate a set of colliders in a batch.
Instead, I use a dictionary that maps positions to collectibles because lookups by key are very fast. (e.g. Dictionary<Vector3, Collectible>). Then you can just use the player’s position to query the dictionary for collectibles. I actually use a loop based on the distance from the character, pull out all collectibles in that range (in order to show/hide) and use that newly formed collection to query for the player passing through the location of the collectible.