Filtering resolutions in dropdown according to aspect ratio

I try to filter Screen.resolutions according to aspect ratio but it create empty array.

Here’s my code:

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


public class SettingsMenu : MonoBehaviour
{
    public AudioMixer audioMixer;
    public TMP_Dropdown resolutionDropdown,aspectRatioDropdown;

    Resolution[] resolutions, resolutions16_9, resolutions16_10, resolutions4_3;
    string[] ratios;

    float currentRatio;
    float ratio;
    int CurrentResolutionIndex = 0;
    int CurrentRatioIndex = 0;

    List<string> resolutionsOptions = new List<string>();
    List<string> aspectRatios = new List<string>();

    private void Start()
    {
        // this work fine
        resolutions = Screen.resolutions.Where(resolution => resolution.refreshRate == Screen.currentResolution.refreshRate).ToArray();

        //adding ratios to array and list
        // this work fine
        aspectRatioDropdown.ClearOptions();

        ratios = new string[] { "4:3", "16:10", "16:9" };
        currentRatio = (float)Screen.width / (float)Screen.height;
       
        aspectRatios.Add("4:3");
        aspectRatios.Add("16:10");
        aspectRatios.Add("16:9");

        if (currentRatio == 4f / 3f)
        {
            CurrentRatioIndex = 0;
        }
        else if (currentRatio == 16f / 10f)
        {
            CurrentRatioIndex = 1;
        }
        else if (currentRatio == 16f / 9f)
        {
            CurrentRatioIndex = 2;
        }

        aspectRatioDropdown.AddOptions(aspectRatios);
        aspectRatioDropdown.value = CurrentRatioIndex;
        aspectRatioDropdown.RefreshShownValue();

        Debug.Log(CurrentRatioIndex);
        Debug.Log(Screen.width);
        Debug.Log(Screen.height);
        Debug.Log(currentRatio);
       
        // there is a problem
        if (CurrentRatioIndex == 0)
        {
            //resolutions4_3 = resolutions.Where(resolution => (float)resolution.width / (float)resolution.height == 4f / 3f).ToArray();
            resolutions = Screen.resolutions.Where(resolution => resolution.refreshRate == Screen.currentResolution.refreshRate && (float)resolution.width / (float)resolution.height == 4f / 3f).ToArray();
            Debug.Log("4:3");
        }
        else if (CurrentRatioIndex == 1)
        {
            //resolutions16_10 = resolutions.Where(resolution => (float)resolution.width / (float)resolution.height == 16f / 10f).ToArray();
            resolutions = Screen.resolutions.Where(resolution => resolution.refreshRate == Screen.currentResolution.refreshRate && (float)resolution.width / (float)resolution.height == 16f / 10f).ToArray();
            Debug.Log("16:10 ");
        }
        else if (CurrentRatioIndex == 2)
        {
            //resolutions16_9 = resolutions.Where(resolution => (float)resolution.width / (float)resolution.height == 16f / 9f).ToArray();
            resolutions = Screen.resolutions.Where(resolution => resolution.refreshRate == Screen.currentResolution.refreshRate && (float)resolution.width / (float)resolution.height == 16f / 9f).ToArray();
            Debug.Log("16:9 ");
        }
       
        Debug.Log(resolutions.Length);
        //Debug.Log("length 4:3: " + resolutions4_3.Length);
        //Debug.Log("length 16:10: " + resolutions16_10.Length);
        //Debug.Log("lenght 16:9: " + resolutions16_9.Length);
       
        // this work fine
        resolutionDropdown.ClearOptions();
        resolutionsOptions.Clear();


        for (int i = 0; i < resolutions.Length; i++)
        {
            string resolutionOption = resolutions[i].width + " x " + resolutions[i].height;
            resolutionsOptions.Add(resolutionOption);
       
            if (resolutions[i].width == Screen.width && resolutions[i].height == Screen.height)
            {
                CurrentResolutionIndex = i;
            }
           
        }

        resolutionDropdown.AddOptions(resolutionsOptions);
        resolutionDropdown.value = CurrentResolutionIndex;
        resolutionDropdown.RefreshShownValue();

    }

In general, you can’t directly compare two floating point values unless whatever computes them does the same thing with exact same values. This is because floating point formats are lossy at extremely small values and are likely to jitter around, leading to numerical instability.

If you’re comparing two approximately similar values, the recommended approach is to introduce an epsilon, a small mathematical value that can be represented by float, which is then used as a safe margin for comparison.

Unity gives you Mathf.Approximately(float a, float b) to do this, but given that it doesn’t let you define the epsilon, I typically end up using my own IsCloseToZero extension approach where I normalize the expression so that it yields a near zero, and work with that, instead of comparing two arbitrary values on the fly.

// This goes to some static class that is reused for similar extensions
static bool IsCloseToZero(this float value, float epsilon = 1E-6f)
  => Mathf.Abs(value) < epsilon;

And then you might do

bool areSimilarRatios(float ratio1, float ratio2) => (ratio1 - ratio2).IsCloseToZero(1E-4f);
if(areSimilarRatios(currentRatio, someOtherRatio)) { ... }
1 Like

However, in this particular case, I wouldn’t do this because ratios are highly sensitive.
(Oh and besides, you cannot compare them in a linear fashion, as the meaning of the epsilon has to vary between the ratios with lower denominators and ratios with higher denominators.)

I would instead populate all known resolutions to mankind (or at least the ones I care about the most), classifying them manually to constants that have ratio-based names but do not have to correspond necessarily (we’re talking potential pixels of difference, as every vendor has these discrepancies). And then would add a support for undefined resolutions, to either fit the ones that were detected into a known category, or define them as a new category if a similar cannot be found.

Thanks a lot for reply. I change type from float on double and now it’s work.

if (CurrentRatioIndex == 0)
        {
           
            resolutions = Screen.resolutions.Where(resolution => resolution.refreshRate == Screen.currentResolution.refreshRate && (double)resolution.width / (double)resolution.height == 4d / 3d).ToArray();
           
        }
        else if (CurrentRatioIndex == 1)
        {
           
            resolutions = Screen.resolutions.Where(resolution => resolution.refreshRate == Screen.currentResolution.refreshRate && (double)resolution.width / (double)resolution.height == 16d / 10d).ToArray();
           
        }
        else if (CurrentRatioIndex == 2)
        {
           
            resolutions = Screen.resolutions.Where(resolution => resolution.refreshRate == Screen.currentResolution.refreshRate && (double)resolution.width / (double)resolution.height == 16d / 9d).ToArray();
         
        }

Checking for resolutions that exactly match the desired aspect ratio is probably not a good idea. First, because comparing floating-point types for exact equality is almost always a bad idea, and secondly, because some hardware has unusual aspect ratios, and so the native supported resolutions might not match the common values. You might get some code that works perfectly on all your test machines but that makes the game unplayable for 2% of your customers who have weird hardware, who can’t choose any resolution because you filtered them all out.

This isn’t a good solution precisely because of everything I told you before, and Antistone confirmed it.