0

Cannot resolve and find solution. I want to create easy-extend structure and save data to ScriptableObjects. For example I create 2 AudioSettings (assets) based on SettingsSO: DefaultSettings, CurrentSettings, then I'm going to set up Default on UnityEditor and store user's settings in Current, if Current == null then Current = CreateInstance<Default>. So any setting group has 2 refs. In future I want add another setting groups in SettingManager and be able to add all of them in some list or dictionary and then foreach do something common. For example load default settings.

SettingsManager.cs

public class SettingsManager
{
    [SerializeField] private SettingGroup<VideoSettings> _videoSettings;
    [SerializeField] private SettingGroup<AudioSettings> _audioSettings;

    private Dictionary<Type, SettingGroup> _settingDictionary;

    public void Init()
    {
        _settingDictionary = new Dictionary<Type, SettingGroup>
        {
            { _audioSettings.GetType(), _audioSettings },
            { _videoSettings.GetType(), _videoSettings }
        };
    }

    private void LoadDefaultSetting()
    {
        foreach (var pair in _settingDictionary)
        {
            //var eachSettingGroup = (pair.Key)pair.Value;              //Error Cannot resolve symbol 'pair'
            //var eachSettingGroup = pair.Value as pair.Key;            //Error Cannot resolve symbol 'pair'
            //var eachSettingGroup = (typeof(pair.Key))pair.Value;    //Error Cannot resolve symbol 'pair'
            var eachSettingGroup = I need settings.Default HelpMeGuys!! (;
            eachSettingGroup.Init();
        }
    }
}

SettingGroup.cs

public class SettingGroup<T> : SettingGroup where T : SettingsSO
    {
        public T Default => _default;
        public T Current => _current;
        public Type SettingsType => typeof(T);


        [SerializeField] private T _default;
        [SerializeField] private T _current;
        private T _lastSaved;

        public void Init()
        {
            if (_current == null)
            {
                Debug.LogWarning("Current settings reference not found. Default settings using instead without saving.");
                _current = ScriptableObject.CreateInstance<T>();
                _default.CopyDataTo(_current);
            }

            _lastSaved = ScriptableObject.CreateInstance<T>();
            _current.CopyDataTo(_lastSaved);
        }
    }

AudioSettings.cs

public class AudioSettings : SettingsSO
{
   public bool MusicEnabled;
   public bool SoundEnabled;
   public float MusicVolume;
   public float SoundVolume;
}

SettingsSO.cs

public abstract class SettingsSO : ScriptableObject
{
    public string Description;
}
  • There doesn't seem to be a `Default` in `SettingGroup` and for actual type casting you need to define the type at compile time, it cannot be a runtime value. You might attempt to do something with reflection, although I'm not sure Unity supports reflection on all platforms – UnholySheep Aug 12 '22 at 13:07
  • You just want to iterate your dictionary and don`t know how to do it? – Red Star Aug 12 '22 at 13:40
  • As for me, you can create in your SettingsSO method, named "GetDefaultSetting" and override this method in both Audio and Video settings – Red Star Aug 12 '22 at 13:49
  • Thank you guys for your help. I found solution and add as answer. And there is very interesting solution from @Xerillio but too complex for me. – Aleksei Burov Aug 14 '22 at 05:02

2 Answers2

1

It's a little unclear what capabilities you need for your SettingGroup<T> and if there's anything important in SettingGroup, which we can't see. But you can solve the immediate problem by introducing an interface with a covariant generic type:

// Notice the 'out' keyword    vvv
public interface ISettingGroup<out T> where T : SettingsSO
{
    public T Default { get; }
    public T Current { get; }
    public Type SettingsType { get; }
    
    public void Init();
}

Then implement that interface in your SettingGroup:

public class SettingGroup<T> : ISettingGroup<T> where T : SettingsSO
{ /* ... */ }

In your SettingsManager we can now change the type of the dictionary's values:

public class SettingsManager
{
    [SerializeField] private SettingGroup<VideoSettings> _videoSettings;
    [SerializeField] private SettingGroup<AudioSettings> _audioSettings;

    // Now uses the interface vvvvvvvvvvvvvvvvvvvvvvv
    private Dictionary<Type,  ISettingGroup<SettingsSO>> _settingDictionary;

    public void Init()
    {
        _settingDictionary = new Dictionary<Type, SettingGroup>
        {
            { _audioSettings.GetType(), _audioSettings },
            { _videoSettings.GetType(), _videoSettings }
        };
    }

    private void LoadDefaultSetting()
    {
        foreach (var pair in _settingDictionary)
        {
            // No need for any type casting now...
            var settingGroup = pair.Value;
            var settingsDefault = settingGroup.Default;
            settingGroup.Init();
        }
    }
}
Xerillio
  • 4,855
  • 1
  • 17
  • 28
  • Thanks for your solution. It is very interesting unknown for me solution. Unfortunately it is too difficult for me at the moment and I can't to understand how it works. – Aleksei Burov Aug 13 '22 at 19:59
0

I found solution, I forget that those setting groups will be use same methods and I can replace these methods to interface. And I don't need to know which of type this is setting group.

SettingGroup.cs

    public interface SettingGroup
    {
        void Init();
    }

    [Serializable]
    public class SettingGroup<T> : SettingGroup where T : SettingsSO
    {
        [SerializeField] private T _default;
        [SerializeField] private T _saved;
        [SerializeField] private T _current;

        public T CurrentSettings  => _current;

        public void Init()
        {
            if (_saved == null)
            {
                Debug.LogWarning(
                    "Current settings reference not found. Default settings using instead without saving.");
                _saved = ScriptableObject.CreateInstance<T>();
                _saved.CopyDataFrom(_default);
            }

            _current = ScriptableObject.CreateInstance<T>();
            _current.CopyDataFrom(_saved);
        }
    }
}

SettingsManager.cs

    [Serializable]
    public class SettingsManager
    {
        [SerializeField] private SettingGroup<VideoSettings> _videoSettings;
        [SerializeField] private SettingGroup<AudioSettings> _audioSettings;

        public VideoSettings Video => _videoSettings.CurrentSettings;
        public AudioSettings Audio => _audioSettings.CurrentSettings;

        private List<SettingGroup> _settingList;

        public void Init()
        {
            //TODO don't forget to add all above SettingsGroups to list 
            _settingList = new List<SettingGroup>
            {
                _audioSettings, 
                _videoSettings
            };

            foreach (var settingGroup in _settingList)
            {
                settingGroup.Init();
            }
        }
   }