| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- using UnityEngine.Serialization;
- #pragma warning disable 0618 // disable "never assigned" warnings
- namespace TheraBytes.BetterUi
- {
- [HelpURL("https://documentation.therabytes.de/better-ui/ResolutionMonitor.html")]
- public class ResolutionMonitor : SingletonScriptableObject<ResolutionMonitor>
- {
- static string FilePath { get { return "TheraBytes/Resources/ResolutionMonitor"; } }
- #region Obsolete
- [Obsolete("Use 'GetOptimizedResolution()' instead.")]
- public static Vector2 OptimizedResolution
- {
- get { return ResolutionMonitor.Instance.optimizedResolutionFallback; }
- set
- {
- if (ResolutionMonitor.Instance.optimizedResolutionFallback == value)
- return;
- ResolutionMonitor.Instance.optimizedResolutionFallback = value;
- CallResolutionChanged();
- }
- }
- [Obsolete("Use 'GetOptimizedDpi()' instead.")]
- public static float OptimizedDpi
- {
- get { return ResolutionMonitor.Instance.optimizedDpiFallback; }
- set
- {
- if (ResolutionMonitor.Instance.optimizedDpiFallback == value)
- return;
- ResolutionMonitor.Instance.optimizedDpiFallback = value;
- CallResolutionChanged();
- }
- }
- #endregion
- public static Vector2 CurrentResolution
- {
- get
- {
- if (lastScreenResolution == Vector2.zero)
- {
- lastScreenResolution = new Vector2(Screen.width, Screen.height);
- }
- return lastScreenResolution;
- }
- }
- public static float CurrentDpi
- {
- get
- {
- if (lastDpi == 0)
- {
- lastDpi = Instance.dpiManager.GetDpi();
- }
- return lastDpi;
- }
- }
- public string FallbackName { get { return fallbackName; } set { fallbackName = value; } }
- public static Vector2 OptimizedResolutionFallback { get { return ResolutionMonitor.Instance.optimizedResolutionFallback; } }
- public static float OptimizedDpiFallback { get { return ResolutionMonitor.Instance.optimizedDpiFallback; } }
- [FormerlySerializedAs("optimizedResolution")]
- [SerializeField]
- Vector2 optimizedResolutionFallback = new Vector2(1080, 1920);
- [FormerlySerializedAs("optimizedDpi")]
- [SerializeField]
- float optimizedDpiFallback = 96;
- [SerializeField]
- string fallbackName = "Portrait";
- [SerializeField]
- StaticSizerMethod[] staticSizerMethods = new StaticSizerMethod[5];
- [SerializeField]
- DpiManager dpiManager = new DpiManager();
- ScreenTypeConditions currentScreenConfig;
- [SerializeField]
- List<ScreenTypeConditions> optimizedScreens = new List<ScreenTypeConditions>()
- {
- new ScreenTypeConditions("Landscape", typeof(IsCertainScreenOrientation)),
- };
- public List<ScreenTypeConditions> OptimizedScreens { get { return optimizedScreens; } }
- static Dictionary<string, ScreenTypeConditions> lookUpScreens = new Dictionary<string, ScreenTypeConditions>();
- #region Screen Tags
- static HashSet<string> screenTags = new HashSet<string>();
- public static IEnumerable<string> CurrentScreenTags { get { return screenTags; } }
- public static bool AddScreenTag(string screenTag)
- {
- if(screenTags.Add(screenTag))
- {
- isDirty = true;
- Update();
- return true;
- }
- return false;
- }
- public static bool RemoveScreenTag(string screenTag)
- {
- if (screenTags.Remove(screenTag))
- {
- isDirty = true;
- Update();
- return true;
- }
- return false;
- }
- public static void ClearScreenTags()
- {
- screenTags.Clear();
- isDirty = true;
- Update();
- }
- #endregion
- #if UNITY_EDITOR && UNITY_2018_3_OR_NEWER
- static UnityEditor.SceneManagement.StageHandle currentStage;
- #endif
- public static ScreenTypeConditions CurrentScreenConfiguration
- {
- get
- {
- #if UNITY_EDITOR
- if (simulatedScreenConfig != null)
- {
- return simulatedScreenConfig;
- }
- #endif
- return ResolutionMonitor.Instance.currentScreenConfig;
- }
- }
- public static ScreenTypeConditions GetConfig(string name)
- {
- if (lookUpScreens.Count == 0)
- {
- foreach (var config in ResolutionMonitor.Instance.optimizedScreens)
- {
- lookUpScreens.Add(config.Name, config);
- }
- }
- if (!(lookUpScreens.ContainsKey(name)))
- {
- var config = ResolutionMonitor.Instance.optimizedScreens.FirstOrDefault(o => o.Name == name);
- if (config != null)
- {
- lookUpScreens.Add(name, config);
- return config;
- }
- else
- {
- return null;
- }
- }
- return lookUpScreens[name];
- }
- public static ScreenInfo GetOpimizedScreenInfo(string name)
- {
- if (string.IsNullOrEmpty(name))
- {
- return new ScreenInfo(OptimizedResolutionFallback, OptimizedDpiFallback);
- }
- return GetConfig(name).OptimizedScreenInfo;
- }
- public static IEnumerable<ScreenTypeConditions> GetCurrentScreenConfigurations()
- {
- foreach (ScreenTypeConditions config in ResolutionMonitor.Instance.optimizedScreens)
- {
- if (config.IsActive)
- yield return config;
- }
- }
- static Vector2 lastScreenResolution;
- static float lastDpi;
- static bool isDirty;
- #if UNITY_EDITOR
- static Type gameViewType = null;
- static UnityEditor.EditorWindow gameViewWindow = null;
- static Version unityVersion;
- static ScreenTypeConditions simulatedScreenConfig;
- public static ScreenTypeConditions SimulatedScreenConfig
- {
- get { return simulatedScreenConfig; }
- set
- {
- if (simulatedScreenConfig != value)
- isDirty = true;
- simulatedScreenConfig = value;
- }
- }
- void OnEnable()
- {
- RegisterCallbacks();
- }
- void OnDisable()
- {
- UnregisterCallbacks();
- }
- static void RegisterCallbacks()
- {
- unityVersion = UnityEditorInternal.InternalEditorUtility.GetUnityVersion();
- isDirty = true;
- UnityEditor.EditorApplication.update += Update;
- #if UNITY_5_6_OR_NEWER
- UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += SceneOpened;
- #endif
- #if UNITY_2018_0_OR_NEWER
- UnityEditor.EditorApplication.playModeStateChanged += PlayModeStateChanged;
- #else
- UnityEditor.EditorApplication.playmodeStateChanged += PlayModeStateChanged;
- #endif
- }
- static void UnregisterCallbacks()
- {
- UnityEditor.EditorApplication.update -= Update;
- #if UNITY_5_6_OR_NEWER
- UnityEditor.SceneManagement.EditorSceneManager.sceneOpened -= SceneOpened;
- #endif
- #if UNITY_2018_0_OR_NEWER
- UnityEditor.EditorApplication.playModeStateChanged -= PlayModeStateChanged;
- #else
- UnityEditor.EditorApplication.playmodeStateChanged -= PlayModeStateChanged;
- #endif
- }
- #if UNITY_5_6_OR_NEWER
- private static void SceneOpened(UnityEngine.SceneManagement.Scene scene, UnityEditor.SceneManagement.OpenSceneMode mode)
- {
- isDirty = true;
- Update();
- }
- #endif
- private static void PlayModeStateChanged()
- {
- if(!UnityEditor.EditorApplication.isPlaying)
- {
- ClearScreenTags();
- }
- Instance.ResolutionChanged();
- }
- #else
- void OnEnable()
- {
- ResolutionChanged();
- }
- #endif
- public static float InvokeStaticMethod(ImpactMode mode, Component caller, Vector2 optimizedResolution, Vector2 actualResolution, float optimizedDpi, float actualDpi)
- {
- int idx = 0;
- switch (mode)
- {
- case ImpactMode.StaticMethod1: idx = 0; break;
- case ImpactMode.StaticMethod2: idx = 1; break;
- case ImpactMode.StaticMethod3: idx = 2; break;
- case ImpactMode.StaticMethod4: idx = 3; break;
- case ImpactMode.StaticMethod5: idx = 4; break;
- default: throw new ArgumentException();
- }
- return (ResolutionMonitor.HasInstance && Instance.staticSizerMethods[idx] != null)
- ? Instance.staticSizerMethods[idx].Invoke(caller, optimizedResolution, actualResolution, optimizedDpi, actualDpi)
- : 1;
- }
- public static void MarkDirty()
- {
- ResolutionMonitor.isDirty = true;
- }
- public static float GetOptimizedDpi(string screenName)
- {
- if (string.IsNullOrEmpty(screenName) || screenName == Instance.fallbackName)
- return OptimizedDpiFallback;
- var s = Instance.optimizedScreens.FirstOrDefault(o => o.Name == screenName);
- if (s == null)
- {
- Debug.LogError("Screen Config with name " + screenName + " could not be found.");
- return OptimizedDpiFallback;
- }
- return s.OptimizedDpi;
- }
- public static Vector2 GetOptimizedResolution(string screenName)
- {
- if (string.IsNullOrEmpty(screenName) || screenName == Instance.fallbackName)
- return OptimizedResolutionFallback;
- var s = GetConfig(screenName);
- if (s == null)
- return OptimizedResolutionFallback;
- return s.OptimizedResolution;
- }
- public static bool IsOptimizedResolution(int width, int height)
- {
- if ((int)OptimizedResolutionFallback.x == width && (int)OptimizedResolutionFallback.y == height)
- return true;
- foreach (var config in Instance.optimizedScreens)
- {
- ScreenInfo si = config.OptimizedScreenInfo;
- if (si != null && (int)si.Resolution.x == width && (int)si.Resolution.y == height)
- return true;
- }
- return false;
- }
- public static void Update()
- {
- #if UNITY_EDITOR
- // check if file was deleted
- if(!HasInstance)
- {
- UnregisterCallbacks();
- return;
- }
- #endif
- isDirty = isDirty
- #if UNITY_EDITOR // should never change in reality...
- || (Instance.GetCurrentDpi() != lastDpi)
- #endif
- || GetCurrentResolution() != lastScreenResolution;
- #if UNITY_EDITOR && UNITY_2018_3_OR_NEWER
- if(!isDirty)
- {
- var stage = UnityEditor.SceneManagement.StageUtility.GetCurrentStageHandle();
- if (stage != currentStage)
- {
- currentStage = stage;
- isDirty = true;
- }
- }
- #endif
- if (isDirty)
- {
- CallResolutionChanged();
- isDirty = false;
- }
- }
- public static void CallResolutionChanged()
- {
- Instance.ResolutionChanged();
- }
- public void ResolutionChanged()
- {
- lastScreenResolution = GetCurrentResolution();
- lastDpi = GetCurrentDpi();
- currentScreenConfig = null;
- bool foundConfig = false;
- foreach (var config in optimizedScreens)
- {
- if (config.IsScreenType() && !(foundConfig))
- {
- currentScreenConfig = config;
- foundConfig = true;
- }
- }
- if (ResolutionMonitor.HasInstance) // preserve calling too early
- {
- foreach (IResolutionDependency rd in AllResolutionDependencies())
- {
- if (!(rd as Behaviour).isActiveAndEnabled)
- continue;
- rd.OnResolutionChanged();
- }
- }
- #if UNITY_EDITOR
- if (IsZoomPossible())
- {
- FindAndStoreGameView();
- if (gameViewWindow != null)
- {
- var method = gameViewType.GetMethod("UpdateZoomAreaAndParent",
- System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
- try
- {
- if (method != null)
- method.Invoke(gameViewWindow, null);
- }
- catch (Exception) { }
- }
- }
- #endif
- }
- static IEnumerable<IResolutionDependency> AllResolutionDependencies()
- {
- var allObjects = GetAllEditableObjects();
- // first update the "override screen properties", because other objects rely on them
- foreach (GameObject go in allObjects)
- {
- var resDeps = go.GetComponents<OverrideScreenProperties>();
- foreach (IResolutionDependency comp in resDeps)
- {
- yield return comp;
- }
- }
- // then update all other objects
- foreach (GameObject go in allObjects)
- {
- var resDeps = go.GetComponents<Behaviour>().OfType<IResolutionDependency>();
- foreach (IResolutionDependency comp in resDeps)
- {
- if (comp is OverrideScreenProperties)
- continue;
- yield return comp;
- }
- }
- }
- static IEnumerable<GameObject> GetAllEditableObjects()
- {
- var allObjects =
- #if UNITY_2022_2_OR_NEWER
- UnityEngine.Object.FindObjectsByType<GameObject>(FindObjectsInactive.Include, FindObjectsSortMode.None);
- #else
- UnityEngine.Object.FindObjectsOfType<GameObject>();
- #endif
- foreach (GameObject go in allObjects)
- yield return go;
- #if UNITY_EDITOR && UNITY_2018_3_OR_NEWER
- var prefabStage =
- #if UNITY_2021_2_OR_NEWER
- UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage();
- #else
- UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage();
- #endif
- if (prefabStage != null)
- {
- foreach(GameObject root in prefabStage.scene.GetRootGameObjects())
- {
- foreach (GameObject go in IterateHierarchy(root))
- {
- yield return go;
- }
- }
- }
- #endif
- }
- static IEnumerable<GameObject> IterateHierarchy(GameObject root)
- {
- yield return root;
- foreach (Transform child in root.transform)
- {
- foreach (GameObject subChild in IterateHierarchy(child.gameObject))
- {
- yield return subChild;
- }
- }
- }
- static Vector2 GetCurrentResolution()
- {
- #if UNITY_EDITOR
- FindAndStoreGameView();
- System.Reflection.MethodInfo GetSizeOfMainGameView = gameViewType.GetMethod("GetSizeOfMainGameView",
- System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
- object res = GetSizeOfMainGameView.Invoke(null, null);
- return (Vector2)res;
- #else
- return new Vector2(Screen.width, Screen.height);
- #endif
- }
- float GetCurrentDpi()
- {
- #if UNITY_EDITOR
- if (IsZoomPossible())
- {
- Vector2 scale = Vector2.one;
- FindAndStoreGameView();
- if (gameViewWindow != null)
- {
- var zoomArea = gameViewType.GetField("m_ZoomArea",
- System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
- .GetValue(gameViewWindow);
- scale = (Vector2)zoomArea.GetType().GetField("m_Scale",
- System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
- .GetValue(zoomArea);
- }
- return Screen.dpi / scale.y;
- }
- #endif
- return dpiManager.GetDpi();
- }
- #if UNITY_EDITOR
- static void FindAndStoreGameView()
- {
- if (gameViewType == null)
- {
- gameViewType = Type.GetType("UnityEditor.GameView,UnityEditor");
- }
- if (gameViewWindow == null)
- {
- gameViewWindow = Resources.FindObjectsOfTypeAll(gameViewType)
- .FirstOrDefault() as UnityEditor.EditorWindow;
- }
- }
- public static bool IsZoomPossible()
- {
- #if UNITY_2018_3_OR_NEWER // minimum officially supported version
- return true;
- #else
- return unityVersion.Major > 5
- || (unityVersion.Major == 5 && unityVersion.Minor >= 4);
- #endif
- }
- public void SetOptimizedResolutionFallback(Vector2 resolution)
- {
- this.optimizedResolutionFallback = resolution;
- }
- #endif
- }
- }
- #pragma warning restore 0618
|