Search Unity

How to load 200+ Addressables while minimizing copy and paste?

Discussion in 'Scripting' started by _eternal, Jan 21, 2020.

  1. _eternal

    _eternal

    Joined:
    Nov 25, 2014
    Posts:
    304
    I'm trying to figure out the best way to work with the new Addressables system for my audio files. Right now I have all of my game's audio in an AudioManager class, and the AudioClips are loaded at launch from public AssetReferences that are assigned in the editor. This is great, but it requires a bit of copy/paste and I'm not sure if there's a better way to do it.

    Here's roughly what the class looks like:

    Code (CSharp):
    1. public class AudioManager : MonoBehaviour {
    2.     public AssetReference clickRef, swooshRef, swooshSlowRef, cartoonDescendRef; //the list goes on for around 200 items
    3.     static AudioClip click, swoosh, swooshSlow, cartoonDescend;
    4.  
    5.     Awake()
    6.     {
    7.         LoadAudioAsync();
    8.     }
    9.  
    10.     LoadAudioAsync()
    11.     {
    12.         clickRef.LoadAssetAsync<AudioClip>().Completed += (reference) => OnAsyncLoad(reference, ref click);
    13.         //copy and paste the above for all 200 items..... you see the issue here
    14.     }
    15.  
    16.     private void OnAsyncLoad(AsyncOperationHandle<AudioClip> reference, ref AudioClip theClip)
    17.     {      
    18.         if (reference.Result == null) Debug.LogError("you tried to load an AudioClip that doesn't exist!!");
    19.         //print("Loading clip " + reference.Result.name + " on frame " + Time.frameCount);
    20.         theClip = reference.Result;
    21.     }
    22. }
    Is there a better way of doing this?

    I tried putting all of the AudioClips in one array and all of the AssetReferences in another, but apparently you can't pass indexers as ref, so I think the OnAsyncLoad method won't work with that solution.

    This solution is good enough for my use case, but it's inconvenient if I have to change a chunk of them instead of just adding one or two. As they say, whenever you find yourself copy/pasting in programming, you're probably doing something wrong, so I thought I'd ask.
     
  2. WallaceT_MFM

    WallaceT_MFM

    Joined:
    Sep 25, 2017
    Posts:
    394
    You could pass the index itself, no need for a reference to an int:
    Code (csharp):
    1. public AssetReference[] soundRefs;
    2. static AudioClip[] clips;
    3.  
    4. Awake()
    5. {
    6.    clips = new AudioClip[soundRefs.length];
    7.    LoadAudioAsync();
    8. }
    9.  
    10. LoadAudioAsync()
    11. {
    12.    for(int i = 0; i < soundRefs.length; i++)
    13.    {
    14.       int index = i; // don't remember if you still need to do this, there was a time that closures handled iterator variables in a confusing way, so I still do this out of habbit
    15.       soudRefs[i].LoadAssetAsync<AudioClip>().Completed += (reference) => OnAsyncLoad(reference, index);
    16.    }
    17. }
    18.  
    19. private void OnAsyncLoad(AsyncOperationHandle<AudioClip> reference, int index)
    20. {
    21.    if (reference.Result == null) Debug.LogError("you tried to load an AudioClip that doesn't exist!!");
    22.    //print("Loading clip " + reference.Result.name + " on frame " + Time.frameCount);
    23.    clips[index] = reference.Result;
    24. }
     
  3. _eternal

    _eternal

    Joined:
    Nov 25, 2014
    Posts:
    304
    Ah interesting, thanks! So instead of passing in the AudioClip to be assigned, you just pass in the index and have OnAsyncLoad assign the loaded clip to the appropriate location in the array. I guess the only downside is that you'd only be able to access these clips by index, not by variable name, because they aren't declared separately from the array. But you can probably work around that with enums.
     
  4. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    What'd be more clear on the configuration side, IMO, is to associate the id and asset reference. You could also separate the audio service from the loading.

    Something like:
    Code (CSharp):
    1.     // Enum values that are serialized as part of an asset should always be explicitly assigned.
    2.     // Otherwise, inserting any new ones between existing ones will break all the existing data.
    3.     public enum AudioEffect
    4.     {
    5.         Foo = 101,
    6.         Bar = 102,
    7.         Baz = 103
    8.     }
    9.  
    10.     // Instead of a singleton, this could accessed through a service locator or directly referenced
    11.     public class AudioService
    12.     {
    13.         public static readonly AudioService Instance = new AudioService();
    14.        
    15.         private Dictionary<AudioEffect, AudioClip> map = new Dictionary<AudioEffect, AudioClip>();
    16.        
    17.         public void RegisterAudio(AudioEffect id, AudioClip clip)
    18.         {
    19.             map[id] = clip;
    20.         }
    21.  
    22.         public AudioClip GetAudio(AudioEffect id)
    23.         {
    24.             if (!map.ContainsKey(id))
    25.             {
    26.                 Debug.LogError($"Missing Audio: {id}!");
    27.                 return null;
    28.             }
    29.  
    30.             return map[id];
    31.         }
    32.     }
    33.  
    34.     public class AudioLoader : MonoBehaviour
    35.     {
    36.         [System.Serializable]
    37.         public class Entry
    38.         {
    39.             public AudioEffect id;
    40.             public AssetReference asset;
    41.         }
    42.  
    43.         [SerializeField] private Entry[] entries;
    44.  
    45.         void Awake()
    46.         {
    47.             foreach (var entry in entries)
    48.             {
    49.                 entry.asset.LoadAssetAsync<AudioClip>().Completed += result => OnAsyncLoad(result, entry.id);
    50.             }
    51.         }
    52.        
    53.         private void OnAsyncLoad(AsyncOperationHandle<AudioClip> loadOp, AudioEffect id)
    54.         {
    55.             if (loadOp.Result == null)
    56.             {
    57.                 Debug.LogError($"You tried to load an AudioClip that doesn't exist: {id}");
    58.                 return;
    59.             }
    60.            
    61.             AudioService.Instance.RegisterAudio(id, loadOp.Result);
    62.         }
    63.     }
     
    _eternal likes this.