1
\$\begingroup\$

I would like to know some better coding patterns to load assets based on player settings. Say that users are allowed to select 1 of many skins for a weapon. Now when I load that weapon object, I want the assets for that skin to be loaded instead of the default.

My question is: what is a good coding pattern to store the user's skin choice, and to read/use this stored data to programmatically load a prefab or texture later?

Right now, what I have is a scroll view with buttons that show each unique skin. When the button is pressed, it saves the skin texture's file name to JSON. Then, later, when I want to figure out what to use, I read that field from JSON and call Resources.Load() on the file name.

Is there a better way to do this? This is very manual and error prone since I have to specify the texture's file name in my code for each button. Right now I'm trying to grab the texture's file name via Image.sprite.name, but this also has a level of manual work involved. It requires knowing which subdirectory of the Resources folder the sprite is in.

\$\endgroup\$
0

1 Answer 1

3
\$\begingroup\$

When the button is pressed, it saves the skin texture's file name to JSON.

Is the preferred skin a preference of the user? Then did you consider to use PlayerPrefs instead? It provides you with a simple, persistent key-value store.

Then, later, when I want to figure out what to use, I read that field from JSON and call Resources.Load() on the file name.

This sounds like a perfect use-case for Addressables.

With Addressables, you get an input on top of the inspector of every asset where you can specify a key for that asset:

Image stolen from https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/unity-addressables-its-never-too-big-to-fit-r5189/

This can be the filename, or it can be any other string. You can then load that asset at runtime using that key. Advantages of addressables over Resources is:

  • The assets don't need to be in a folder named Resources. You can make any asset in your project addressable. You can even move those assets around in your project. As long as you don't change the key, the addressable system will still find them.
  • Addressables handle asset bundles in a transparent matter. When you registered asset bundles which contain addressable assets, then you can just use Addressables.LoadAssetAsync and it will automatically figure out which asset bundle contains the asset you want.
  • You can even put asset bundles on the web. The addressable system will then download those asset bundles automatically.
  • You can also tag addressables with Labels. This allows you to mark all your weapon skin textures and then load them all at runtime. When you need the keys of those assets, then you might want to use a two-step process where you first use Addressables.LoadResourceLocationsAsync to get the asset locations and then load each asset with Addressables.LoadAssetAsync(location). The resource locations contain the key of the addressable, so it allows you to associate each addressable with its key. A code example can be found in the documentation article loading multiple assets, subsection "Correlating loaded assets to their keys".

So tl;dr:

  1. Make the skins addressables. Give each addressable an unique key, and assign the label "weapon_skin" to all of them.
  2. In the menu, load all the addressables with the label "weapon_skin", so you can instantiate a button for each of them.
  3. In the menu, store the key of the chosen skins in PlayerPrefs.
  4. In the game, load the addressable with the key you retrieve from PlayerPrefs.
\$\endgroup\$
7
  • 2
    \$\begingroup\$ The JSON solution is good if the player can have multiple save slots/files with different skin customizations, rather than one global setting that always applies. \$\endgroup\$
    – DMGregory
    Commented Aug 16, 2022 at 12:45
  • \$\begingroup\$ Hey thanks for the answer. I've switched my project to use Addressables and loaded all the skins using LoadAssetsAsync (note the plural in Assets). And then I passed a callback to LoadAssetsAsync() to create a button prefab and add it to the scrollview that I'm displaying all the skins in. My question now is: is there a way to implement a specific order? As of now there's basically no guarantee in the weapon skin display order, it could be random. It essentially depends on the behavior of the async handle and which textures load first. \$\endgroup\$
    – WestaAlger
    Commented Aug 17, 2022 at 8:40
  • \$\begingroup\$ @WestaAlger You could sort the buttons whenever you add a new one. You can change the order of the buttons by calling transform.SetSiblingIndex on them. If you need a more detailed answer, please open a new question. \$\endgroup\$
    – Philipp
    Commented Aug 17, 2022 at 9:04
  • \$\begingroup\$ Thanks. Could you also edit the answer to specify exactly what you mean by "unique key"? How do I set this in the Unity editor? And how can I programmatically read and store this from within the a script for a button? I'll accept the answer after these clarifications. \$\endgroup\$
    – WestaAlger
    Commented Aug 17, 2022 at 9:13
  • 1
    \$\begingroup\$ Myself, I'd make a ScriptableObject holding info about each skin (display name, UI icon/preview if needed, addressable for the texture asset). Then you can store a list of these SOs in your menu script in any order or categorization you choose, without loading the texture asset itself until you want to. That lets you load one at a time, in case you have so many skins that loading them all creates a noticeable hitch or eats too much budget. \$\endgroup\$
    – DMGregory
    Commented Aug 17, 2022 at 10:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .