GameViewSizeHelper.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. using System;
  2. using System.Collections;
  3. using System.Reflection;
  4. using UnityEngine;
  5. #if UNITY_EDITOR
  6. using UnityEditor;
  7. #endif
  8. namespace AppUI
  9. {
  10. public static class GameViewSizeHelper
  11. {
  12. public const int PortraitWidth = ScreenOrientationHelper.PortraitWidth;
  13. public const int PortraitHeight = ScreenOrientationHelper.PortraitHeight;
  14. public const int LandscapeWidth = ScreenOrientationHelper.LandscapeWidth;
  15. public const int LandscapeHeight = ScreenOrientationHelper.LandscapeHeight;
  16. const string CustomSizeLabelPrefix = "SB_Fixed_";
  17. #if UNITY_EDITOR
  18. static GameViewPrePlaySnapshot _prePlaySnapshot;
  19. public struct GameViewPrePlaySnapshot
  20. {
  21. public bool HasValue;
  22. public int GroupType;
  23. public int SizeIndex;
  24. public int ViewportWidth;
  25. public int ViewportHeight;
  26. public int ConfigWidth;
  27. public int ConfigHeight;
  28. public string SizeTypeName;
  29. }
  30. public static void CapturePrePlaySnapshot()
  31. {
  32. _prePlaySnapshot = default;
  33. Vector2 vp = Handles.GetMainGameViewSize();
  34. _prePlaySnapshot.ViewportWidth = Mathf.RoundToInt(vp.x);
  35. _prePlaySnapshot.ViewportHeight = Mathf.RoundToInt(vp.y);
  36. if (TryGetEditorSizeContext(out Type gameViewType, out _, out _, out object group,
  37. out Type groupClass, out EditorWindow gameView))
  38. {
  39. _prePlaySnapshot.GroupType = GetActiveGameViewSizeGroupType(gameViewType);
  40. var indexField = gameViewType.GetField(
  41. "m_SizeSelectionID",
  42. BindingFlags.Instance | BindingFlags.NonPublic);
  43. if (indexField != null)
  44. _prePlaySnapshot.SizeIndex = (int)indexField.GetValue(gameView);
  45. if (TryReadGameViewSizeAtIndex(groupClass, group, _prePlaySnapshot.SizeIndex,
  46. out Vector2 cfg, out string typeName))
  47. {
  48. _prePlaySnapshot.ConfigWidth = Mathf.RoundToInt(cfg.x);
  49. _prePlaySnapshot.ConfigHeight = Mathf.RoundToInt(cfg.y);
  50. _prePlaySnapshot.SizeTypeName = typeName ?? "";
  51. }
  52. }
  53. _prePlaySnapshot.HasValue = true;
  54. Debug.Log(
  55. $"[AppUI.GameViewSizeHelper] 记录 Play 前 Game 视图: " +
  56. $"视口={_prePlaySnapshot.ViewportWidth}x{_prePlaySnapshot.ViewportHeight}, " +
  57. $"index={_prePlaySnapshot.SizeIndex}, group={_prePlaySnapshot.GroupType}, " +
  58. $"配置={_prePlaySnapshot.ConfigWidth}x{_prePlaySnapshot.ConfigHeight} ({_prePlaySnapshot.SizeTypeName})");
  59. }
  60. public static void RestorePrePlaySnapshot()
  61. {
  62. if (!ScreenOrientationEditorSettings.IsEnabled || !_prePlaySnapshot.HasValue)
  63. return;
  64. CancelEnforce();
  65. ScheduleRestorePrePlay(120);
  66. EditorApplication.delayCall += () => TryRestorePrePlaySnapshot(logResult: true);
  67. }
  68. public static void CancelEnforce()
  69. {
  70. GameViewEnforceState.Cancel();
  71. }
  72. static void ScheduleRestorePrePlay(int editorFrames)
  73. {
  74. if (!_prePlaySnapshot.HasValue)
  75. return;
  76. GameViewEnforceState.RequestRestore(_prePlaySnapshot, editorFrames);
  77. }
  78. static bool TryRestorePrePlaySnapshot(bool logResult = false)
  79. {
  80. if (!_prePlaySnapshot.HasValue)
  81. return false;
  82. var snap = _prePlaySnapshot;
  83. bool restored = false;
  84. if (TryGetEditorSizeContext(out Type gameViewType, out _, out _, out object group,
  85. out Type groupClass, out EditorWindow gameView))
  86. {
  87. int count = (int)groupClass.GetMethod("GetTotalCount", BindingFlags.Instance | BindingFlags.Public)
  88. .Invoke(group, null);
  89. if (snap.SizeIndex >= 0 && snap.SizeIndex < count)
  90. restored = ApplyGameViewSizeIndex(gameViewType, gameView, snap.SizeIndex, snap.GroupType);
  91. }
  92. if (!IsViewportMatchingTarget(snap.ViewportWidth, snap.ViewportHeight))
  93. {
  94. if (snap.SizeTypeName == "FixedResolution" && snap.ConfigWidth > 0 && snap.ConfigHeight > 0)
  95. restored |= TrySetGameViewResolution(snap.ConfigWidth, snap.ConfigHeight, logResult: false);
  96. else
  97. restored |= TrySetGameViewResolution(snap.ViewportWidth, snap.ViewportHeight, logResult: false);
  98. }
  99. else
  100. {
  101. restored = true;
  102. }
  103. if (logResult)
  104. {
  105. Vector2 vp = Handles.GetMainGameViewSize();
  106. Debug.Log(
  107. $"[AppUI.GameViewSizeHelper] 恢复 Play 前 Game 视图: " +
  108. $"目标视口={snap.ViewportWidth}x{snap.ViewportHeight}, " +
  109. $"当前视口={Mathf.RoundToInt(vp.x)}x{Mathf.RoundToInt(vp.y)}, ok={restored}");
  110. }
  111. return restored;
  112. }
  113. static bool NeedsRestorePrePlaySnapshot()
  114. {
  115. if (!_prePlaySnapshot.HasValue)
  116. return false;
  117. return !IsViewportMatchingTarget(_prePlaySnapshot.ViewportWidth, _prePlaySnapshot.ViewportHeight);
  118. }
  119. public static IEnumerator EditorApplyPortraitCoroutine(float maxWaitSeconds = 2f)
  120. {
  121. if (!ScreenOrientationEditorSettings.IsEnabled)
  122. yield break;
  123. yield return EditorSetResolutionCoroutine(PortraitWidth, PortraitHeight, maxWaitSeconds);
  124. }
  125. public static IEnumerator EditorSwapOrientationCoroutine(bool toLandscape, float maxWaitSeconds = 3f)
  126. {
  127. if (!ScreenOrientationEditorSettings.IsEnabled)
  128. yield break;
  129. int w = toLandscape ? LandscapeWidth : PortraitWidth;
  130. int h = toLandscape ? LandscapeHeight : PortraitHeight;
  131. yield return EditorSetResolutionCoroutine(w, h, maxWaitSeconds);
  132. }
  133. public static void ApplyPortraitResolution()
  134. {
  135. if (!ScreenOrientationEditorSettings.IsEnabled)
  136. return;
  137. ScheduleEnforce(PortraitWidth, PortraitHeight, 90);
  138. }
  139. public static void ScheduleEnforce(int width, int height, int editorFrames = 60)
  140. {
  141. if (!ScreenOrientationEditorSettings.IsEnabled)
  142. return;
  143. GameViewEnforceState.Request(width, height, editorFrames);
  144. }
  145. public static bool IsViewportMatchingTarget(int width, int height)
  146. {
  147. Vector2 vp = Handles.GetMainGameViewSize();
  148. return Mathf.RoundToInt(vp.x) == width && Mathf.RoundToInt(vp.y) == height;
  149. }
  150. public static bool NeedsConfigEnforce(int width, int height)
  151. {
  152. if (IsViewportMatchingTarget(width, height))
  153. return false;
  154. if (TryGetSelectedFixedConfigSize(out Vector2 config))
  155. {
  156. return Mathf.RoundToInt(config.x) != width || Mathf.RoundToInt(config.y) != height;
  157. }
  158. return true;
  159. }
  160. static IEnumerator EditorSetResolutionCoroutine(int width, int height, float maxWaitSeconds)
  161. {
  162. bool done = false;
  163. bool ok = false;
  164. ScheduleEnforce(width, height, Mathf.CeilToInt(maxWaitSeconds * 60f));
  165. EditorApplication.delayCall += () =>
  166. {
  167. ok = TrySetGameViewResolution(width, height, logResult: true);
  168. done = true;
  169. };
  170. while (!done)
  171. yield return null;
  172. float elapsed = 0f;
  173. while (elapsed < maxWaitSeconds)
  174. {
  175. if (ok && (width > height ? Handles.GetMainGameViewSize().x >= Handles.GetMainGameViewSize().y
  176. : Handles.GetMainGameViewSize().y >= Handles.GetMainGameViewSize().x))
  177. break;
  178. elapsed += Time.unscaledDeltaTime;
  179. yield return null;
  180. }
  181. yield return null;
  182. }
  183. public static bool TrySetGameViewResolution(int width, int height, bool logResult = false)
  184. {
  185. if (!ScreenOrientationEditorSettings.IsEnabled)
  186. return false;
  187. if (IsViewportMatchingTarget(width, height))
  188. return true;
  189. try
  190. {
  191. if (!TryGetEditorSizeContext(out Type gameViewType, out object sizesInstance, out Type sizesType,
  192. out object group, out Type groupClass, out EditorWindow gameView))
  193. {
  194. Debug.LogWarning("[AppUI.GameViewSizeHelper] 无法获取 GameView 上下文");
  195. return false;
  196. }
  197. int groupType = GetActiveGameViewSizeGroupType(gameViewType);
  198. string label = CustomSizeLabelPrefix + width + "x" + height;
  199. int index = FindFixedResolutionIndexByLabel(groupClass, group, label);
  200. if (index < 0)
  201. index = FindFixedResolutionIndex(groupClass, group, width, height);
  202. if (index < 0)
  203. index = AddCustomFixedSize(sizesType, sizesInstance, groupClass, group, width, height, label);
  204. if (index < 0)
  205. {
  206. Debug.LogWarning($"[AppUI.GameViewSizeHelper] 无法创建 Fixed {width}x{height}");
  207. return false;
  208. }
  209. int count = (int)groupClass.GetMethod("GetTotalCount", BindingFlags.Instance | BindingFlags.Public)
  210. .Invoke(group, null);
  211. if (index < 0 || index >= count)
  212. {
  213. Debug.LogWarning($"[AppUI.GameViewSizeHelper] index={index} 越界 (count={count})");
  214. return false;
  215. }
  216. if (!ApplyGameViewSizeIndex(gameViewType, gameView, index, groupType))
  217. {
  218. Debug.LogWarning($"[AppUI.GameViewSizeHelper] 应用 index={index} 失败");
  219. return false;
  220. }
  221. TrySetPlayModeTargetSize(gameView, gameViewType, width, height);
  222. gameView.Repaint();
  223. EditorApplication.QueuePlayerLoopUpdate();
  224. bool verified = VerifyAppliedFixedSize(gameViewType, gameView, groupClass, group, width, height,
  225. out Vector2 applied, out string appliedType);
  226. if (logResult)
  227. {
  228. Vector2 viewport = Handles.GetMainGameViewSize();
  229. Debug.Log(
  230. $"[AppUI.GameViewSizeHelper] 设置 {label} 目标={width}x{height} group={groupType} index={index}/{count - 1}, " +
  231. $"下拉项={Mathf.RoundToInt(applied.x)}x{Mathf.RoundToInt(applied.y)} ({appliedType}) verified={verified}, " +
  232. $"视口={Mathf.RoundToInt(viewport.x)}x{Mathf.RoundToInt(viewport.y)}");
  233. }
  234. return true;
  235. }
  236. catch (Exception ex)
  237. {
  238. Debug.LogWarning($"[AppUI.GameViewSizeHelper] 异常: {ex}");
  239. return false;
  240. }
  241. }
  242. static bool TryGetSelectedFixedConfigSize(out Vector2 size)
  243. {
  244. size = default;
  245. if (!TryGetRawSelectedConfigSize(out size, out string typeName))
  246. return false;
  247. return typeName == "FixedResolution";
  248. }
  249. static bool TryGetRawSelectedConfigSize(out Vector2 size, out string sizeTypeName)
  250. {
  251. size = default;
  252. sizeTypeName = "";
  253. try
  254. {
  255. if (!TryGetEditorSizeContext(out Type gameViewType, out _, out _, out object group,
  256. out Type groupClass, out EditorWindow gameView))
  257. return false;
  258. var indexField = gameViewType.GetField(
  259. "m_SizeSelectionID",
  260. BindingFlags.Instance | BindingFlags.NonPublic);
  261. if (indexField == null)
  262. return false;
  263. int index = (int)indexField.GetValue(gameView);
  264. return TryReadGameViewSizeAtIndex(groupClass, group, index, out size, out sizeTypeName);
  265. }
  266. catch
  267. {
  268. return false;
  269. }
  270. }
  271. static bool TryGetEditorSizeContext(
  272. out Type gameViewType,
  273. out object sizesInstance,
  274. out Type sizesType,
  275. out object group,
  276. out Type groupClass,
  277. out EditorWindow gameView)
  278. {
  279. gameViewType = null;
  280. sizesInstance = null;
  281. sizesType = null;
  282. group = null;
  283. groupClass = null;
  284. gameView = null;
  285. var editorAssembly = typeof(Editor).Assembly;
  286. gameViewType = editorAssembly.GetType("UnityEditor.GameView");
  287. sizesType = editorAssembly.GetType("UnityEditor.GameViewSizes");
  288. if (gameViewType == null || sizesType == null)
  289. return false;
  290. sizesInstance = GetGameViewSizesInstance(editorAssembly, sizesType);
  291. if (sizesInstance == null)
  292. return false;
  293. gameView = GetActiveGameViewWindow(gameViewType);
  294. if (gameView == null)
  295. return false;
  296. int groupType = GetActiveGameViewSizeGroupType(gameViewType);
  297. group = sizesType.GetMethod("GetGroup")?.Invoke(sizesInstance, new object[] { groupType });
  298. if (group == null)
  299. return false;
  300. groupClass = group.GetType();
  301. return true;
  302. }
  303. static EditorWindow GetActiveGameViewWindow(Type gameViewType)
  304. {
  305. var editorAssembly = typeof(Editor).Assembly;
  306. var playModeViewType = editorAssembly.GetType("UnityEditor.PlayModeView");
  307. if (playModeViewType != null)
  308. {
  309. var getPlayModeView = playModeViewType.GetMethod(
  310. "GetPlayModeView",
  311. BindingFlags.NonPublic | BindingFlags.Static);
  312. if (getPlayModeView != null)
  313. {
  314. var playView = getPlayModeView.Invoke(null, new object[] { gameViewType }) as EditorWindow;
  315. if (playView != null)
  316. return playView;
  317. }
  318. }
  319. var all = Resources.FindObjectsOfTypeAll(gameViewType);
  320. if (all != null && all.Length > 0)
  321. return all[0] as EditorWindow;
  322. return EditorWindow.GetWindow(gameViewType, false, null, false);
  323. }
  324. static void TrySetPlayModeTargetSize(EditorWindow gameView, Type gameViewType, int width, int height)
  325. {
  326. var prop = gameViewType.GetProperty(
  327. "targetSize",
  328. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  329. if (prop != null && prop.CanWrite)
  330. prop.SetValue(gameView, new Vector2(width, height));
  331. gameViewType.GetMethod(
  332. "UpdateZoomAreaDimensions",
  333. BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(gameView, null);
  334. }
  335. static bool ApplyGameViewSizeIndex(Type gameViewType, EditorWindow gameView, int index, int groupType)
  336. {
  337. var indexField = gameViewType.GetField(
  338. "m_SizeSelectionID",
  339. BindingFlags.Instance | BindingFlags.NonPublic);
  340. if (indexField != null)
  341. indexField.SetValue(gameView, index);
  342. var sizeSelectionCallback = gameViewType.GetMethod(
  343. "SizeSelectionCallback",
  344. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  345. if (sizeSelectionCallback != null)
  346. sizeSelectionCallback.Invoke(gameView, new object[] { index, gameView });
  347. gameViewType.GetMethod(
  348. "UpdateZoomAreaDimensions",
  349. BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(gameView, null);
  350. try
  351. {
  352. var groupUpdated = gameViewType.GetMethod(
  353. "GameViewSizeGroupUpdated",
  354. BindingFlags.Instance | BindingFlags.NonPublic);
  355. if (groupUpdated != null)
  356. {
  357. var ps = groupUpdated.GetParameters();
  358. if (ps.Length == 0)
  359. groupUpdated.Invoke(gameView, null);
  360. else if (ps.Length == 1)
  361. groupUpdated.Invoke(gameView, new object[] { groupType });
  362. }
  363. }
  364. catch
  365. {
  366. }
  367. return indexField != null || sizeSelectionCallback != null;
  368. }
  369. static bool VerifyAppliedFixedSize(
  370. Type gameViewType, EditorWindow gameView, Type groupClass, object group,
  371. int width, int height, out Vector2 applied, out string appliedType)
  372. {
  373. applied = default;
  374. appliedType = "";
  375. var indexField = gameViewType.GetField(
  376. "m_SizeSelectionID",
  377. BindingFlags.Instance | BindingFlags.NonPublic);
  378. if (indexField == null)
  379. return false;
  380. int index = (int)indexField.GetValue(gameView);
  381. if (!TryReadGameViewSizeAtIndex(groupClass, group, index, out applied, out appliedType))
  382. return false;
  383. return appliedType == "FixedResolution" &&
  384. Mathf.RoundToInt(applied.x) == width &&
  385. Mathf.RoundToInt(applied.y) == height;
  386. }
  387. static bool TryReadGameViewSizeAtIndex(
  388. Type groupClass, object group, int index, out Vector2 size, out string sizeTypeName)
  389. {
  390. size = default;
  391. sizeTypeName = "";
  392. int count = (int)groupClass.GetMethod("GetTotalCount", BindingFlags.Instance | BindingFlags.Public)
  393. .Invoke(group, null);
  394. if (index < 0 || index >= count)
  395. return false;
  396. var gameViewSize = groupClass.GetMethod("GetGameViewSize", BindingFlags.Instance | BindingFlags.Public)
  397. .Invoke(group, new object[] { index });
  398. var sizeObjType = gameViewSize.GetType();
  399. int w = (int)sizeObjType.GetProperty("width", BindingFlags.Instance | BindingFlags.Public)
  400. .GetValue(gameViewSize);
  401. int h = (int)sizeObjType.GetProperty("height", BindingFlags.Instance | BindingFlags.Public)
  402. .GetValue(gameViewSize);
  403. var sizeTypeEnum = sizeObjType.GetProperty("sizeType", BindingFlags.Instance | BindingFlags.Public)
  404. .GetValue(gameViewSize);
  405. sizeTypeName = sizeTypeEnum?.ToString() ?? "";
  406. size = new Vector2(w, h);
  407. return true;
  408. }
  409. static object GetGameViewSizesInstance(Assembly editorAssembly, Type sizesType)
  410. {
  411. var scriptableSingletonOpen = editorAssembly.GetType("UnityEditor.ScriptableSingleton`1");
  412. if (scriptableSingletonOpen == null)
  413. return null;
  414. var closed = scriptableSingletonOpen.MakeGenericType(sizesType);
  415. return closed.GetProperty("instance", BindingFlags.Public | BindingFlags.Static)
  416. ?.GetValue(null, null);
  417. }
  418. static int GetActiveGameViewSizeGroupType(Type gameViewType)
  419. {
  420. var method = gameViewType.GetMethod(
  421. "GetCurrentGameViewSizeGroupType",
  422. BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
  423. if (method != null)
  424. return (int)method.Invoke(null, null);
  425. switch (EditorUserBuildSettings.activeBuildTarget)
  426. {
  427. case BuildTarget.Android:
  428. return 3;
  429. case BuildTarget.iOS:
  430. return 2;
  431. default:
  432. return 0;
  433. }
  434. }
  435. static int FindFixedResolutionIndex(Type groupClass, object group, int width, int height)
  436. {
  437. int count = (int)groupClass.GetMethod("GetTotalCount", BindingFlags.Instance | BindingFlags.Public)
  438. .Invoke(group, null);
  439. for (int i = 0; i < count; i++)
  440. {
  441. if (!TryReadGameViewSizeAtIndex(groupClass, group, i, out Vector2 s, out string typeName))
  442. continue;
  443. if (typeName != "FixedResolution")
  444. continue;
  445. if (Mathf.RoundToInt(s.x) == width && Mathf.RoundToInt(s.y) == height)
  446. return i;
  447. }
  448. return -1;
  449. }
  450. static int FindFixedResolutionIndexByLabel(Type groupClass, object group, string label)
  451. {
  452. int count = (int)groupClass.GetMethod("GetTotalCount", BindingFlags.Instance | BindingFlags.Public)
  453. .Invoke(group, null);
  454. for (int i = 0; i < count; i++)
  455. {
  456. var gameViewSize = groupClass.GetMethod("GetGameViewSize", BindingFlags.Instance | BindingFlags.Public)
  457. .Invoke(group, new object[] { i });
  458. var sizeObjType = gameViewSize.GetType();
  459. var displayText = sizeObjType.GetProperty("displayText", BindingFlags.Instance | BindingFlags.Public)
  460. ?.GetValue(gameViewSize) as string;
  461. var baseText = sizeObjType.GetProperty("baseText", BindingFlags.Instance | BindingFlags.Public)
  462. ?.GetValue(gameViewSize) as string;
  463. if (label.Equals(displayText, StringComparison.Ordinal) ||
  464. label.Equals(baseText, StringComparison.Ordinal))
  465. return i;
  466. }
  467. return -1;
  468. }
  469. static int AddCustomFixedSize(
  470. Type sizesType, object sizesInstance, Type groupClass, object group,
  471. int width, int height, string label)
  472. {
  473. var editorAssembly = typeof(Editor).Assembly;
  474. var gameViewSizeEnumType = editorAssembly.GetType("UnityEditor.GameViewSizeType");
  475. var gameViewSizeType = editorAssembly.GetType("UnityEditor.GameViewSize");
  476. var fixedResolution = Enum.Parse(gameViewSizeEnumType, "FixedResolution");
  477. object newSize = null;
  478. foreach (var ctor in gameViewSizeType.GetConstructors())
  479. {
  480. var p = ctor.GetParameters();
  481. if (p.Length == 4)
  482. newSize = ctor.Invoke(new object[] { fixedResolution, width, height, label });
  483. else if (p.Length == 3)
  484. newSize = ctor.Invoke(new object[] { fixedResolution, width, height });
  485. if (newSize != null)
  486. break;
  487. }
  488. if (newSize == null)
  489. return -1;
  490. int countBefore = (int)groupClass.GetMethod("GetTotalCount", BindingFlags.Instance | BindingFlags.Public)
  491. .Invoke(group, null);
  492. groupClass.GetMethod("AddCustomSize", BindingFlags.Instance | BindingFlags.Public)
  493. .Invoke(group, new[] { newSize });
  494. sizesType.GetMethod("SaveToHDD", BindingFlags.Instance | BindingFlags.Public)
  495. ?.Invoke(sizesInstance, null);
  496. var indexOfMethod = groupClass.GetMethod("IndexOf", BindingFlags.Instance | BindingFlags.Public);
  497. if (indexOfMethod != null)
  498. {
  499. int idx = (int)indexOfMethod.Invoke(group, new[] { newSize });
  500. if (idx >= 0)
  501. return idx;
  502. }
  503. int countAfter = (int)groupClass.GetMethod("GetTotalCount", BindingFlags.Instance | BindingFlags.Public)
  504. .Invoke(group, null);
  505. if (countAfter == countBefore + 1)
  506. return countAfter - 1;
  507. return FindFixedResolutionIndexByLabel(groupClass, group, label);
  508. }
  509. sealed class GameViewEnforceState
  510. {
  511. static int _targetW;
  512. static int _targetH;
  513. static int _framesLeft;
  514. static bool _hooked;
  515. static bool _restoreMode;
  516. public static void Request(int width, int height, int frames)
  517. {
  518. _restoreMode = false;
  519. _targetW = width;
  520. _targetH = height;
  521. _framesLeft = Mathf.Max(_framesLeft, frames);
  522. EnsureHooked();
  523. }
  524. public static void RequestRestore(GameViewPrePlaySnapshot snapshot, int frames)
  525. {
  526. _restoreMode = true;
  527. _targetW = snapshot.ViewportWidth;
  528. _targetH = snapshot.ViewportHeight;
  529. _framesLeft = Mathf.Max(_framesLeft, frames);
  530. EnsureHooked();
  531. }
  532. public static void Cancel()
  533. {
  534. _framesLeft = 0;
  535. _restoreMode = false;
  536. if (_hooked)
  537. {
  538. EditorApplication.update -= OnEditorUpdate;
  539. _hooked = false;
  540. }
  541. }
  542. static void EnsureHooked()
  543. {
  544. if (!_hooked)
  545. {
  546. EditorApplication.update += OnEditorUpdate;
  547. _hooked = true;
  548. }
  549. }
  550. static void OnEditorUpdate()
  551. {
  552. if (_framesLeft <= 0 || !ScreenOrientationEditorSettings.IsEnabled)
  553. {
  554. if (_framesLeft <= 0 && _hooked)
  555. {
  556. EditorApplication.update -= OnEditorUpdate;
  557. _hooked = false;
  558. _restoreMode = false;
  559. }
  560. return;
  561. }
  562. _framesLeft--;
  563. if (_restoreMode)
  564. {
  565. if (NeedsRestorePrePlaySnapshot())
  566. TryRestorePrePlaySnapshot(logResult: false);
  567. }
  568. else if (NeedsConfigEnforce(_targetW, _targetH))
  569. {
  570. TrySetGameViewResolution(_targetW, _targetH, logResult: false);
  571. }
  572. }
  573. }
  574. #endif
  575. }
  576. }