References to Scriptable Object in build are null

Issue #286 resolved
Keenan Woodall created an issue

I have a scriptable object, SoundGroup. A few instances exist in the project. A class called AudioManager has a list of references to them. The references work in the editor but are lost in builds. I tried having the SoundGroup inherit from SerializableScriptableObject but that didn't fix anything.

I looked into AOT and didn't fully understand it but I scanned the project, added references to the attached files (AudioManager/Sound, SoundGroup) and built the dll but it still didn't work. Untitled.png Untitled.png Untitled.png Untitled.png

I'm using version 1.0.6.0 but that wasn't an option below.

The code is attached, but I'll paste it here as well. It should work on it's own if you need to test it. The Sound struct is at the bottom of the AudioManager file.


using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;

namespace Fivestone.Audio
{
    [CreateAssetMenu (menuName = "Audio/Sound Group", fileName = "New Sound Group")]
    public class SoundGroup : SerializedScriptableObject
    {
        public List<Sound> sounds;
    }
}

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Audio;

namespace Fivestone.Audio
{
    public class AudioManager : MonoSingleton<AudioManager>
    {
        private const string INIT_ERROR_MSG = "Audio Manager hasn't been initialized yet. To be safe, don't access it until Start.";

        [SerializeField]
        private bool initializeOnAwake = true;
        [SerializeField]
        private bool logInitializationErrors;
        public bool updateSourcesEveryFrame = true;
#if UNITY_EDITOR
        [Range (0f, 1f)]
        public float editorVolumeMultplier = 1f;
#endif
        [SerializeField]
        private AudioMixerGroup fallbackMixer;
        [Header ("Start Data")]
        [SerializeField]
        private List<Sound> sounds;
        [SerializeField]
        private List<SoundGroup> soundGroups;

        private bool audioInitialized;

        private Dictionary<Sound, AudioSource> sourceSoundPairs = new Dictionary<Sound, AudioSource> ();

        protected override void Initialize ()
        {
            if (initializeOnAwake)
                InitializeAudio ();
        }

        private void Update ()
        {
            if (updateSourcesEveryFrame)
                UpdateSources ();
        }

        public void InitializeAudio ()
        {
            if (audioInitialized)
                return;
            audioInitialized = true;

            if (sounds != null)
                for (int i = 0; i < sounds.Count; i++)
                    AddSound (sounds[i]);
            if (soundGroups != null)
            { 
                for (int i = 0; i < soundGroups.Count; i++)
                {
                    if (soundGroups[i] == null)
                    {
                        Debug.LogError ("Sound group " + i + " is null.");
                        continue;
                    }
                    for (int j = 0; j < soundGroups[i].sounds.Count; j++)
                    {
                        if (soundGroups[i].sounds[j] == null)
                        {
                            Debug.LogError ("Sound " + j + " of group " + i + " is null.");
                            continue;
                        }
                        AddSound (soundGroups[i].sounds[j]);
                    }
                }
            }
        }

        private void DestroySources ()
        {
            if (sourceSoundPairs != null)
                foreach (var source in sourceSoundPairs.Values)
                    Destroy (source.gameObject);
            sourceSoundPairs = new Dictionary<Sound, AudioSource> ();
        }

        public void UpdateSource (AudioSource source, Sound sound, bool isNewSound = false)
        {
            source.spatialBlend = 0f;
            source.volume = sound.volume;
#if UNITY_EDITOR
            source.volume *= editorVolumeMultplier;
#endif
            source.pitch = sound.pitch;
            source.loop = sound.loop;
            source.playOnAwake = sound.playOnAwake;
            source.clip = sound.clip;
            source.outputAudioMixerGroup = (sound.mixer == null) ? fallbackMixer : sound.mixer;

            if (sound.playOnAwake && isNewSound && !source.isPlaying)
                source.Play ();
        }
        public void UpdateSource (Sound sound)
        {
            var source = GetSourceFromSound (sound);
            UpdateSource (source, sound);
        }
        public void UpdateSource (AudioSource source)
        {
            var sound = GetSoundFromSource (source);
            UpdateSource (source, sound);
        }
        public void UpdateSource (KeyValuePair<Sound, AudioSource> sourceSoundPair, bool isNewSound = false)
        {
            UpdateSource (sourceSoundPair.Value, sourceSoundPair.Key, isNewSound);
        }

        public void UpdateSources ()
        {
            foreach (var pair in sourceSoundPairs)
                UpdateSource (pair);
        }

        public AudioSource GetSourceFromSound (Sound sound, bool addIfNotFound = true)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return null;
            }

            foreach (var pair in sourceSoundPairs)
                if (pair.Key == sound)
                    return pair.Value;
            Debug.LogError (string.Format ("Sound, {0}, not found.{1}", sound.name, addIfNotFound ? " Adding sound." : string.Empty));
            if (addIfNotFound)
                return AddSound (sound).Value;
            return null;
        }
        public AudioSource GetSourceFromClip (AudioClip clip, bool addIfNotFound = true)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return null;
            }

            foreach (var pair in sourceSoundPairs)
                if (pair.Key.clip == clip)
                    return pair.Value;
            Debug.LogError (string.Format ("Clip, {0}, not found.{1}", clip.name, addIfNotFound ? " Adding sound." : string.Empty));
            if (addIfNotFound)
                return AddSound (clip).Value;
            return null;
        }
        public AudioSource GetSourceFromName (string name)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return null;
            }

            return sourceSoundPairs.First (pair => string.Equals (pair.Key.name, name)).Value;
        }
        public Sound GetSoundFromSource (AudioSource source)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return null;
            }

            foreach (var pair in sourceSoundPairs)
                if (pair.Value == source)
                    return pair.Key;
            Debug.LogError (string.Format ("Source, {0}, not found", source.name));
            return null;
        }

        public Sound GetSoundFromName (string name)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return null;
            }

            foreach (var pair in sourceSoundPairs)
                if (string.Equals (pair.Key.name, name))
                    return pair.Key;
            Debug.LogError (string.Format ("Name, {0}, not found", name));
            return null;
        }

        public List<Sound> GetSounds ()
        {
            return sourceSoundPairs.Keys.ToList ();
        }

        public List<AudioSource> GetSources ()
        {
            return sourceSoundPairs.Values.ToList ();
        }

        public KeyValuePair<Sound, AudioSource> AddSound (Sound sound)
        {
            var newSource = new GameObject (sound.name).AddComponent<AudioSource> ();
            newSource.transform.SetParent (transform);
            sourceSoundPairs.Add (sound, newSource);

            UpdateSource (newSource, sound, true);

            return sourceSoundPairs.Last ();
        }
        public KeyValuePair<Sound, AudioSource> AddSound (AudioClip clip)
        {
            return AddSound (new Sound (clip.name, clip));
        }

        public void PlaySound (Sound sound)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromSound (sound);
            UpdateSource (source, sound);
            source.Play ();
        }
        public void PlaySound (AudioClip clip)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromClip (clip);
            var sound = GetSoundFromSource (source);
            UpdateSource (source, sound);
            source.Play ();
        }
        public void PlaySound (string name)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromName (name);
            var sound = GetSoundFromSource (source);
            UpdateSource (source, sound);
            source.Play ();
        }
        public void StopSound (Sound sound)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromSound (sound);
            UpdateSource (source, sound);
            source.Stop ();
        }
        public void StopSound (AudioClip clip)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromClip (clip);
            var sound = GetSoundFromSource (source);
            UpdateSource (source, sound);
            source.Stop ();
        }
        public void StopSound (string name)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromName (name);
            var sound = GetSoundFromSource (source);
            UpdateSource (source, sound);
            source.Stop ();
        }
        public void PauseSound (Sound sound)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromSound (sound);
            UpdateSource (source, sound);
            source.Pause ();
        }
        public void PauseSound (AudioClip clip)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromClip (clip);
            var sound = GetSoundFromSource (source);
            UpdateSource (source, sound);
            source.Pause ();
        }
        public void PauseSound (string name)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromName (name);
            var sound = GetSoundFromSource (source);
            UpdateSource (source, sound);
            source.Pause ();
        }
        public void ResumeSound (string name)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromName (name);
            var sound = GetSoundFromSource (source);
            if (!source.isPlaying)
                PlaySound (sound);
            else
                UpdateSource (source, sound);
            source.UnPause ();
        }
        public void ResumeSound (Sound sound)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromSound (sound);
            if (!source.isPlaying)
                PlaySound (sound);
            else
                UpdateSource (source, sound);
            source.UnPause ();
        }
        public void ResumeSound (AudioClip clip)
        {
            if (!audioInitialized)
            {
                if (logInitializationErrors)
                    Debug.LogError (INIT_ERROR_MSG);
                return;
            }

            var source = GetSourceFromClip (clip);
            var sound = GetSoundFromSource (source);
            if (!source.isPlaying)
                PlaySound (sound);
            else
                UpdateSource (source, sound);
            source.UnPause ();
        }
    }

    [System.Serializable]
    public class Sound
    {
        [Space]
        public string name;
        [Space]
        [Range (0f, 1f)]
        public float volume;
        [Range (0.1f, 3f)]
        public float pitch;
        public bool playOnAwake;
        public bool loop;

        [Space]
        public AudioClip clip;
        public AudioMixerGroup mixer;

        public Sound (string name, AudioClip clip, float volume = 1f, float pitch = 1f, bool loop = false, bool playOnAwake = false, AudioMixerGroup mixer = null)
        {
            this.name = name;
            this.volume = volume;
            this.pitch = pitch;
            this.loop = loop;
            this.playOnAwake = playOnAwake;
            this.clip = clip;
            this.mixer = mixer;
        }

        public static bool operator == (Sound a, Sound b)
        {
            return a.clip == b.clip && a.loop == b.loop && a.mixer == b.mixer && string.Equals (a.name, b.name) && Mathf.Approximately (a.pitch, b.pitch) && a.playOnAwake == b.playOnAwake && Mathf.Approximately (a.volume, b.volume);
        }
        public static bool operator != (Sound a, Sound b)
        {
            return !(a == b);
        }

        public override bool Equals (object obj)
        {
            return this == obj as Sound;
        }
    }
}

Comments (6)

  1. mSkull001

    Sorry for the late reply.

    I'm not sure what is happening here, or why this isn't working for you. But from what I can tell then there's nothing here that is serialized by Odin.

    Since Unity is perfectly capable of serializing the Sound class by itself, Odin leaves it alone. Effectively there's no need to have SerializedScriptableObject on your SoundGroup class. You could try and enforce Odin serialization by adding [OdinSerialize, NonSerialized] to the Sound list in the SoundGroup class, but can't say I if that would work or not.

    I'd recommend going to the Unity forums with this issue. All I can really say is that I've had issues with AudioMixer references in the past, so it seems there's some kind of bug in Unity here.

  2. Antonio Rafael Antunes Miranda

    @KeenanWoodall I know its a bit of a late answer but where you able to resolve the issue? If so, I’m going to mark this issue as resolved.

  3. Keenan Woodall

    Hey, Antonio I’m responding from my personal account as the original post was made from an old work account I don’t have access to any more.

    I don't remember if this was ever resolved, but feel free to mark it as resolved or do whatever. I wrote all this stuff at a previous job so I don’t have access to it anymore.

  4. Log in to comment