|
| 1 | +using UnityEngine; |
| 2 | +using UnityEngine.UI; |
| 3 | + |
| 4 | +[RequireComponent(typeof(Text))] |
| 5 | +public class DynamicResolution : MonoBehaviour |
| 6 | +{ |
| 7 | + /// <summary> |
| 8 | + /// Tells if the text has to be visible (true) or not (false). |
| 9 | + /// </summary> |
| 10 | + public bool EnableText = true; |
| 11 | + /// <summary> |
| 12 | + /// Disables everything when true. |
| 13 | + /// </summary> |
| 14 | + public bool DisableResolutionChangesAndEvaluation = false; |
| 15 | + /// <summary> |
| 16 | + /// Overrides the default resolution (1 means normal, 0.9f is 90%, and so on) |
| 17 | + /// </summary> |
| 18 | + public float StartingRatioOverride = 1; |
| 19 | + /// <summary> |
| 20 | + /// Setting to say that the current scene has static resolution (basically disables the script except for StartingRatioOverride) |
| 21 | + /// </summary> |
| 22 | + public bool StaticResolution = false; |
| 23 | + /// <summary> |
| 24 | + /// Setting to decide if the resolution has to be changed based on scene average fps or based on instant fps. |
| 25 | + /// </summary> |
| 26 | + public bool BasedOnSceneAvg = true; |
| 27 | + /// <summary> |
| 28 | + /// Setting to decide how many times a scene can chenge the resolution |
| 29 | + /// 0 = false; -1 = true (until minimum); |
| 30 | + /// 1 means 1 time, 2 means 2 times and so on. |
| 31 | + /// </summary> |
| 32 | + public int InvokeRecursively = 1; // 0 = false; -1 = true; 1 means 1 time, 2 means 2 times and so on. |
| 33 | + /// <summary> |
| 34 | + /// Setting to tell if the resolution has to be changed at screen change or as soon as possible. |
| 35 | + /// Enabled by default since resolution changes are visible. |
| 36 | + /// Still changing all the time (false) can be useful if you have a scene dedicate to hardware estimation. |
| 37 | + /// </summary> |
| 38 | + public bool OnlyOnSceneChange = true; |
| 39 | + /// <summary> |
| 40 | + /// Resolution will go lower if FPS (or average FPS ot the scene, depending on settings) drop below this value |
| 41 | + /// </summary> |
| 42 | + public int InferiorFpsLimit = 29; |
| 43 | + /// <summary> |
| 44 | + /// Resolution will go higher if FPS (or average FPS ot the scene, depending on settings) goes above this value |
| 45 | + /// </summary> |
| 46 | + public int SuperiorFpsLimit = 49; |
| 47 | + /// <summary> |
| 48 | + /// Sets the maximum FPS of the scene (60 is usually the maximum allowed on smartphones) |
| 49 | + /// </summary> |
| 50 | + public int TargetFps = 60; |
| 51 | + |
| 52 | + private const float startingPeriod = 0.5f; |
| 53 | + private const float regimePeriod = 2.0f; |
| 54 | + private const float avgFpsStartPeriod = 2.0f; // better be a multiple of regimePeriod |
| 55 | + private bool startAvg = false; |
| 56 | + |
| 57 | + private static float fpsMeasurePeriod; |
| 58 | + private int fpsAccumulator = 0; |
| 59 | + private float fpsNextPeriod = 0; |
| 60 | + private int currentFps = 0; |
| 61 | + private int avgFpsAccumulator = 0; |
| 62 | + private float totalPeriodSinceStart = 0; |
| 63 | + private int currentAvgFps = 0; |
| 64 | + |
| 65 | + private static int lastSceneAvgFps = 0; |
| 66 | + |
| 67 | + const string display = "{0} FPS - {8} Avg FPS\nResolution at next refresh: {1}x{2}\nOriginal resolution: {3}x{4}\nCurrent res and modifier: {5}x{6} * {7}"; |
| 68 | + private Text text; |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | + private static int originalwidth; |
| 73 | + private static int originalheight; |
| 74 | + private static int currentwidth; |
| 75 | + private static int currentheight; |
| 76 | + private int minimalwidth; |
| 77 | + private int minimalheight; |
| 78 | + |
| 79 | + public static bool resized { get; private set; } |
| 80 | + |
| 81 | + void Start() |
| 82 | + { |
| 83 | + this.gameObject.SetActive(!DisableResolutionChangesAndEvaluation); |
| 84 | + |
| 85 | + // initialization |
| 86 | + resized = resized ? true : false; |
| 87 | + fpsMeasurePeriod = startingPeriod; |
| 88 | + originalwidth = (originalwidth == 0) ? Screen.width : originalwidth; |
| 89 | + originalheight = (originalheight == 0) ? Screen.height : originalheight; |
| 90 | + |
| 91 | + currentwidth = (currentwidth == 0) ? originalwidth : currentwidth; |
| 92 | + currentheight = (currentheight == 0) ? originalheight : currentheight; |
| 93 | + |
| 94 | + minimalwidth = (int)(originalwidth * 0.69f); |
| 95 | + minimalheight = (int)(originalheight * 0.69f); |
| 96 | + |
| 97 | + float ratioOverride = (!resized) ? StartingRatioOverride : 1; |
| 98 | + |
| 99 | + fpsNextPeriod = Time.realtimeSinceStartup + fpsMeasurePeriod; |
| 100 | + |
| 101 | + text = GetComponent<Text>(); |
| 102 | + |
| 103 | + // decisions |
| 104 | + if (EnableText) |
| 105 | + updateOverlay(); |
| 106 | + else |
| 107 | + text.text = ""; |
| 108 | + |
| 109 | + if (ratioOverride != 1 && (!resized || StaticResolution)) resize((int)(currentwidth * ratioOverride), (int)(currentheight * ratioOverride)); |
| 110 | + else if (resized && OnlyOnSceneChange) |
| 111 | + { |
| 112 | + resize(currentwidth, currentheight); |
| 113 | + lastSceneAvgFps = 0; |
| 114 | + } |
| 115 | + if (!StaticResolution) |
| 116 | + if (InvokeRecursively == -1 || InvokeRecursively > 0) |
| 117 | + { |
| 118 | + Invoke("refreshResolution", regimePeriod); |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + void Update() |
| 123 | + { |
| 124 | + // measuring frames per second in the fpsMeasurePeriod |
| 125 | + fpsAccumulator++; |
| 126 | + avgFpsAccumulator++; |
| 127 | + |
| 128 | + if (Time.realtimeSinceStartup > fpsNextPeriod) |
| 129 | + { |
| 130 | + totalPeriodSinceStart += fpsNextPeriod; |
| 131 | + int appFps = currentFps; |
| 132 | + currentFps = (int)(fpsAccumulator / fpsMeasurePeriod); |
| 133 | + if (appFps > currentFps) |
| 134 | + startAvg = true; |
| 135 | + fpsAccumulator = 0; |
| 136 | + fpsNextPeriod += fpsMeasurePeriod; |
| 137 | + |
| 138 | + //Debug.Log("Time Since Level Load " + Time.timeSinceLevelLoad); |
| 139 | + if (BasedOnSceneAvg) |
| 140 | + { |
| 141 | + if (startAvg && Time.timeSinceLevelLoad > avgFpsStartPeriod) |
| 142 | + { |
| 143 | + currentAvgFps = (int)(avgFpsAccumulator / Time.timeSinceLevelLoad); |
| 144 | + lastSceneAvgFps = currentAvgFps; |
| 145 | + } |
| 146 | + else |
| 147 | + { |
| 148 | + //Debug.Log("Not yet started recording avg fps"); |
| 149 | + currentAvgFps = currentFps; |
| 150 | + avgFpsAccumulator = (int)(currentAvgFps * Time.timeSinceLevelLoad); |
| 151 | + } |
| 152 | + } |
| 153 | + if (EnableText) |
| 154 | + updateOverlay(); |
| 155 | + else |
| 156 | + text.text = ""; |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + private void resize(int width, int height) |
| 161 | + { |
| 162 | + UnityEngine.Debug.Log(string.Format("resizing to {0}x{1}", width, height)); |
| 163 | + Screen.SetResolution(width, height, true, TargetFps); |
| 164 | + resized = true; |
| 165 | + } |
| 166 | + |
| 167 | + private void refreshResolution() |
| 168 | + { |
| 169 | + // bases resolution change on the selected option, so scene average or current fps |
| 170 | + float fpsToUse = (BasedOnSceneAvg) ? (lastSceneAvgFps > 0 ? lastSceneAvgFps : currentAvgFps) : currentFps; |
| 171 | + bool changed = false; |
| 172 | + if (fpsToUse < InferiorFpsLimit && fpsToUse > 0 && currentheight > minimalheight) |
| 173 | + { |
| 174 | + float ratio = (resized) ? 0.92f : 0.82f; |
| 175 | + currentheight = (int)(currentheight * ratio); |
| 176 | + if (currentheight < minimalheight) currentheight = minimalheight; |
| 177 | + currentwidth = (int)(currentwidth * ratio); |
| 178 | + if (currentwidth < minimalwidth) currentwidth = minimalwidth; |
| 179 | + |
| 180 | + // if it's realtime dynamic resolution applies the resize |
| 181 | + if (!OnlyOnSceneChange) resize(currentwidth, currentheight); |
| 182 | + changed = true; |
| 183 | + } |
| 184 | + else if (resized && fpsToUse >= SuperiorFpsLimit && currentheight < originalheight) |
| 185 | + { |
| 186 | + currentheight = (int)(currentheight * 1.1f); |
| 187 | + if (currentheight > originalheight) currentheight = originalheight; |
| 188 | + currentwidth = (int)(currentwidth * 1.1f); |
| 189 | + if (currentwidth > originalwidth) currentwidth = originalwidth; |
| 190 | + |
| 191 | + // if it's realtime dynamic resolution applies the resize |
| 192 | + if (!OnlyOnSceneChange) resize(currentwidth, currentheight); |
| 193 | + changed = true; |
| 194 | + } |
| 195 | + |
| 196 | + resized = true; |
| 197 | + |
| 198 | + if (InvokeRecursively > 0 && changed) |
| 199 | + { |
| 200 | + InvokeRecursively--; |
| 201 | + changed = false; |
| 202 | + } |
| 203 | + if (InvokeRecursively == -1 || InvokeRecursively > 0) |
| 204 | + { |
| 205 | + Invoke("refreshResolution", regimePeriod); |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + private void updateOverlay() |
| 210 | + { |
| 211 | + string settingsString = "\nMode: " + |
| 212 | + (OnlyOnSceneChange ? "only at scene change" : "can change during scene") + |
| 213 | + " " + |
| 214 | + (InvokeRecursively == -1 ? "recursively" : (InvokeRecursively + " times") + |
| 215 | + "\n " + |
| 216 | + (BasedOnSceneAvg ? "based on scene average":"")); |
| 217 | + text.text = string.Format(display + settingsString, |
| 218 | + currentFps, currentwidth, currentheight, originalwidth, originalheight, Screen.width, Screen.height, |
| 219 | + !resized ? StartingRatioOverride : 1, currentAvgFps); |
| 220 | + Debug.Log(text.text); |
| 221 | + } |
| 222 | +} |
| 223 | + |
0 commit comments