Screen.resolutions breaks when on a dual monitor setup.

Hi

I have a script that I’m using for a “settings menu” for my game, to allow the player to set the resolution. This script gathers the display’s supported resolutions and lists them out in a text field, but it also “filters” out multiples of the same resolution at lower refresh rates. I’ve done this because otherwise it just creates a mess of resolutions like 1920x1080@59hz, 1920x1080@60hz, 1920x1080@75hz and so on.

This works properly when I launch it on my native monitor, but as soon as I close the game, switch it to my other monitor, and then launch it again, it gives an “Index was out of range” error.

The thing is, my native monitor supports refresh rates up to 144hz, and the external monitor up to 60hz. I think this causes the issue as Unity runs the game, saves those resolutions as a list in the player prefs, and the external monitor reads them but doesn’t support any of those 144hz resolutions, which is why it returns 0 “filtered resolutions”.

Disclaimer: I’m not a coder and cobbled this together through various tutorials and the help of a coder friend, so the script might be a bit messy. You may also ignore the vsync and fullscreen parts.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class OptionsQualitySettings5B : MonoBehaviour
{
    #region Attributes

    #region Player Pref Key Constants

    private const string RESOLUTION_PREF_KEY = "resolution";

    #endregion

    #region Resolution

    [SerializeField]
    private TMP_Text resolutionText;
    public Toggle fullscreenToggle, vsyncToggle;

    private Resolution[] resolutions;

    private int currentResolutionIndex = 0;
    private List<Resolution> filteredResolutions;
    private float currentRefreshRate;

    #endregion

    #endregion



    void Start()
    {
        resolutions = Screen.resolutions;
        filteredResolutions = new List<Resolution>();
        currentRefreshRate = Screen.currentResolution.refreshRate;

        CreateFilteredListOfResolutions();

        //currentResolutionIndex = PlayerPrefs.GetInt(RESOLUTION_PREF_KEY, 0);
        //SetResolutionText(filteredResolutions[currentResolutionIndex]);
        //SetAndApplyResolution(currentResolutionIndex);

        currentResolutionIndex = GetTheResolutionIndex();

        if (currentResolutionIndex > -1)
        {
            SetResolutionText(filteredResolutions[currentResolutionIndex]);
            SetAndApplyResolution(currentResolutionIndex);
        }

        fullscreenToggle.isOn = Screen.fullScreen;

        if (QualitySettings.vSyncCount == 0)
        {
            vsyncToggle.isOn = false;
        }
        else
        {
            vsyncToggle.isOn = true;
        }
        foreach (var res in resolutions)
        {
            Debug.Log(res.width + "x" + res.height + " : " + res.refreshRate);
        }
    }

    int GetTheResolutionIndex()
    {
        // load index from saved settings (0 if there are none)
        int index = PlayerPrefs.GetInt(RESOLUTION_PREF_KEY, 0);

        if (filteredResolutions.Count == 0)
        {
            Debug.LogError($"No resolutions detected by Unity so there is none what we can set.");
            return -1;
        }
        else if (index > filteredResolutions.Count)
        {
            Debug.LogWarning($"Resolution loaded from memory had bigger number that how many were detected by Unity ({filteredResolutions.Count}), so it was changed to index 0 (first one in detected list).");
            index = 0;
        }

        return index;
    }

    #region Resolution Cycling

    private void SetResolutionText(Resolution resolution)
    {
        resolutionText.text = resolution.width + "x" + resolution.height + " @" + resolution.refreshRate + " Hz"; ;
    }

    public void SetNextResolution()
    {
        if (currentResolutionIndex < 0)
        {
            ShowResolutionIndexError();
            return;
        }

        currentResolutionIndex = GetNextWrappedIndex(filteredResolutions, currentResolutionIndex);
        SetResolutionText(filteredResolutions[currentResolutionIndex]);

    }

    public void SetPreviousResolution()
    {
        if (currentResolutionIndex < 0)
        {
            ShowResolutionIndexError();
            return;
        }

        currentResolutionIndex = GetPreviousWrappedIndex(filteredResolutions, currentResolutionIndex);
        SetResolutionText(filteredResolutions[currentResolutionIndex]);
    }

    void ShowResolutionIndexError()
    {
        Debug.LogError("ResolutionIndex is -1 what means that no resolutions were detected and this function can't do anything.");
    }


    #endregion

    #region Apply Resolution

    private void SetAndApplyResolution(int newResolutionIndex)
    {
        if (currentResolutionIndex < 0)
        {
            ShowResolutionIndexError();
            return;
        }

        currentResolutionIndex = newResolutionIndex;
        ApplyCurrentResolution();
    }

    private void ApplyCurrentResolution()
    {
        ApplyResolution(filteredResolutions[currentResolutionIndex]);
    }

    private void ApplyResolution(Resolution resolution)
    {
        SetResolutionText(resolution);

        Screen.SetResolution(resolution.width, resolution.height, fullscreenToggle.isOn);
        PlayerPrefs.SetInt(RESOLUTION_PREF_KEY, currentResolutionIndex);

        if (vsyncToggle.isOn)
        {
            QualitySettings.vSyncCount = 1;
        }
        else
        {
            QualitySettings.vSyncCount = 0;
        }
    }

    #endregion


    #region Misc Helpers

    #region Index Wrap Helpers

    private int GetNextWrappedIndex<T>(IList<T> collection, int currentIndex)

    {
        if (collection.Count < 1) return 0;
        return (currentIndex + 1) % collection.Count;
    }

    private int GetPreviousWrappedIndex<T>(IList<T> collection, int currentIndex)

    {
        if (collection.Count < 1) return 0;
        if ((currentIndex - 1) < 0) return collection.Count - 1;
        return (currentIndex - 1) % collection.Count;
    }



    #endregion
    #endregion

    public void ApplyChanges()
    {
        SetAndApplyResolution(currentResolutionIndex);
    }

    // Filtering of resolutions by current refreshrate
    void CreateFilteredListOfResolutions()
    {
        for (int i = 0; i < resolutions.Length; i++)
        {
            if (resolutions[i].refreshRate == currentRefreshRate)
            {
                filteredResolutions.Add(resolutions[i]);
            }
        }
    }

}

There was also this comment on one of the tutorials that might help:

So is this a known problem with Unity? Is it fixable at all? Thanks for any help!

Fixed it with a possibly unusual solution. The debug log showed that the current refresh rate was often one number (ex: 60) but the refresh rate attached with the resolutions was something else (144) This cause a mismatch as the index only had 144 resolutions, but the “filter” was searching for resolutions with a refresh rate = current refresh rate, which were none.

I solved it by creating a separate situation for every refresh rate manually. Again, not a coder so this is messy, and might have issues, but it works in my case and what I need it for. Changed CreateFilteredListOfResolutions at line 198 with:

void CreateFilteredListOfResolutions()
    {
        if (resolutions[1].refreshRate == 60)
        {
            for (int i = 0; i < resolutions.Length; i++)
            {


                if (resolutions[i].refreshRate == 60)
                {
                    filteredResolutions.Add(resolutions[i]);


                }
                Debug.Log("Resolution: " + resolutions[i]);

            }
        }

        else if (resolutions[1].refreshRate == 59)
        {
            for (int i = 0; i < resolutions.Length; i++)
            {

                if (resolutions[i].refreshRate == 59)
                {
                    filteredResolutions.Add(resolutions[i]);

                }

            }
        }

        else if (resolutions[1].refreshRate == 59.9)
        {
            for (int i = 0; i < resolutions.Length; i++)
            {

                if (resolutions[i].refreshRate == 59.9)
                {
                    filteredResolutions.Add(resolutions[i]);

                }

            }
        }

        else if (resolutions[1].refreshRate == 75)
        {
            for (int i = 0; i < resolutions.Length; i++)
            {

                if (resolutions[i].refreshRate == 75)
                {
                    filteredResolutions.Add(resolutions[i]);

                }

            }
        }

        else if (resolutions[1].refreshRate == 144)
        {
            for (int i = 0; i < resolutions.Length; i++)
            {

                if (resolutions[i].refreshRate == 144)
                {
                    filteredResolutions.Add(resolutions[i]);

                }

            }
        }

        else if (resolutions[1].refreshRate == 143)
        {
            for (int i = 0; i < resolutions.Length; i++)
            {

                if (resolutions[i].refreshRate == 143)
                {
                    filteredResolutions.Add(resolutions[i]);

                }

            }
        }

        else
        {
            for (int i = 0; i < resolutions.Length; i++)
            {


                filteredResolutions.Add(resolutions[i]);

                Debug.Log("Resolution: " + resolutions[i]);

            }
        }