| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- using System;
- using System.Collections;
- using AdaptUI;
- using UnityEngine;
- using UnityEngine.SceneManagement;
- using UnityEngine.UI;
- namespace AppUI
- {
- /// <summary>
- /// Entry / Login 竖屏,进 Home 横屏。真机用 Screen.orientation;编辑器用 Game 视图 1170×2532 ↔ 2532×1170。
- /// </summary>
- public static class ScreenOrientationHelper
- {
- public const int PortraitWidth = 1170;
- public const int PortraitHeight = 2532;
- public const int LandscapeWidth = 2532;
- public const int LandscapeHeight = 1170;
- const float BlackFadeInDuration = 0.2f;
- const float BlackFadeOutDuration = 0.45f;
- const float BlackHoldBeforeFadeOut = 0.08f;
- static Canvas _blackCanvas;
- static CanvasGroup _blackCanvasGroup;
- static Coroutine _transitionCoroutine;
- public static void ApplyPortraitForBoot()
- {
- Screen.orientation = ScreenOrientation.Portrait;
- Screen.autorotateToLandscapeRight = false;
- Screen.autorotateToLandscapeLeft = false;
- Screen.autorotateToPortrait = false;
- Screen.autorotateToPortraitUpsideDown = false;
- }
- public static IEnumerator SwitchToPortraitEditorAndWait(float maxWaitSeconds = 2f)
- {
- #if UNITY_EDITOR
- if (!ScreenOrientationEditorSettings.IsEnabled)
- yield break;
- yield return GameViewSizeHelper.EditorApplyPortraitCoroutine(maxWaitSeconds);
- #else
- yield break;
- #endif
- }
- public static IEnumerator SwitchToLandscapeAndWait(float maxWaitSeconds = 2f)
- {
- yield return FadeBlackOverlay(1f, BlackFadeInDuration);
- Screen.autorotateToLandscapeRight = true;
- Screen.autorotateToLandscapeLeft = true;
- Screen.autorotateToPortrait = false;
- Screen.autorotateToPortraitUpsideDown = false;
- Screen.orientation = ScreenOrientation.LandscapeLeft;
- #if UNITY_EDITOR
- if (ScreenOrientationEditorSettings.IsEnabled)
- yield return GameViewSizeHelper.EditorSwapOrientationCoroutine(true, maxWaitSeconds);
- #else
- float elapsed = 0f;
- while (elapsed < maxWaitSeconds)
- {
- if (Screen.width >= Screen.height)
- break;
- elapsed += Time.unscaledDeltaTime;
- yield return null;
- }
- yield return null;
- #endif
- DeviceTypeHelper.InvalidateDeviceTypeCache();
- }
- public static IEnumerator FadeOutBlackOverlay(float duration = BlackFadeOutDuration)
- {
- EnsureBlackOverlay();
- if (_blackCanvasGroup == null)
- yield break;
- _blackCanvas.gameObject.SetActive(true);
- if (_blackCanvasGroup.alpha < 0.01f)
- yield break;
- yield return FadeBlackOverlay(0f, duration);
- }
- /// <summary>
- /// 竖屏切横屏 → 黑屏 → 加载场景 → 淡出。协程跑在 DontDestroyOnLoad 上,避免 LoadScene 销毁原场景后无法淡出。
- /// </summary>
- public static void SwitchToLandscapeAndLoadScene(string sceneName)
- {
- var host = CoroutineRunnerHost.Instance;
- if (_transitionCoroutine != null)
- host.StopCoroutine(_transitionCoroutine);
- _transitionCoroutine = host.StartCoroutine(SwitchToLandscapeAndLoadSceneCoroutine(sceneName));
- }
- /// <summary>
- /// 异步激活场景后淡出黑屏(Entry 等场景会在激活后被卸载,须提前在 DontDestroyOnLoad 上调度)。
- /// </summary>
- public static void ScheduleFadeOutBlackOverlayAfterSceneLoad(string expectedSceneName)
- {
- var host = CoroutineRunnerHost.Instance;
- if (_transitionCoroutine != null)
- host.StopCoroutine(_transitionCoroutine);
- _transitionCoroutine = host.StartCoroutine(FadeOutAfterSceneLoadCoroutine(expectedSceneName));
- }
- static IEnumerator SwitchToLandscapeAndLoadSceneCoroutine(string sceneName)
- {
- yield return SwitchToLandscapeAndWait();
- SceneManager.LoadScene(sceneName, LoadSceneMode.Single);
- yield return WaitSceneReadyForFadeOut(sceneName);
- yield return FadeOutBlackOverlay();
- _transitionCoroutine = null;
- }
- static IEnumerator FadeOutAfterSceneLoadCoroutine(string expectedSceneName)
- {
- yield return WaitSceneReadyForFadeOut(expectedSceneName);
- yield return FadeOutBlackOverlay();
- _transitionCoroutine = null;
- }
- static IEnumerator WaitSceneReadyForFadeOut(string expectedSceneName)
- {
- while (SceneManager.GetActiveScene().name != expectedSceneName)
- yield return null;
- float holdUntil = Time.unscaledTime + BlackHoldBeforeFadeOut;
- while (Time.unscaledTime < holdUntil)
- yield return null;
- yield return new WaitForEndOfFrame();
- yield return new WaitForEndOfFrame();
- EnsureBlackOverlay();
- _blackCanvas.gameObject.SetActive(true);
- _blackCanvasGroup.alpha = 1f;
- _blackCanvasGroup.blocksRaycasts = true;
- }
- static void EnsureBlackOverlay()
- {
- if (_blackCanvas != null)
- return;
- var go = new GameObject("[OrientationBlackOverlay]");
- UnityEngine.Object.DontDestroyOnLoad(go);
- _blackCanvas = go.AddComponent<Canvas>();
- _blackCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
- _blackCanvas.sortingOrder = short.MaxValue;
- var scaler = go.AddComponent<CanvasScaler>();
- scaler.uiScaleMode = CanvasScaler.ScaleMode.ConstantPixelSize;
- go.AddComponent<GraphicRaycaster>();
- var imageGo = new GameObject("Black");
- imageGo.transform.SetParent(go.transform, false);
- var rect = imageGo.AddComponent<RectTransform>();
- rect.anchorMin = Vector2.zero;
- rect.anchorMax = Vector2.one;
- rect.offsetMin = Vector2.zero;
- rect.offsetMax = Vector2.zero;
- var image = imageGo.AddComponent<Image>();
- image.color = Color.black;
- image.raycastTarget = true;
- _blackCanvasGroup = go.AddComponent<CanvasGroup>();
- _blackCanvasGroup.alpha = 0f;
- _blackCanvasGroup.blocksRaycasts = true;
- go.SetActive(false);
- }
- static IEnumerator FadeBlackOverlay(float targetAlpha, float duration)
- {
- EnsureBlackOverlay();
- _blackCanvas.gameObject.SetActive(true);
- _blackCanvasGroup.blocksRaycasts = targetAlpha > 0f;
- float startAlpha = _blackCanvasGroup.alpha;
- if (duration <= 0f)
- {
- _blackCanvasGroup.alpha = targetAlpha;
- }
- else
- {
- float startTime = Time.unscaledTime;
- float endTime = startTime + duration;
- while (Time.unscaledTime < endTime)
- {
- float t = Mathf.InverseLerp(startTime, endTime, Time.unscaledTime);
- t = Mathf.SmoothStep(0f, 1f, t);
- _blackCanvasGroup.alpha = Mathf.Lerp(startAlpha, targetAlpha, t);
- yield return null;
- }
- _blackCanvasGroup.alpha = targetAlpha;
- }
- if (targetAlpha <= 0f)
- _blackCanvas.gameObject.SetActive(false);
- }
- public static void RunSwitchToLandscapeThen(Action onReady)
- {
- CoroutineRunnerHost.Instance.StartCoroutine(RunSwitchCoroutine(onReady));
- }
- static IEnumerator RunSwitchCoroutine(Action onReady)
- {
- yield return SwitchToLandscapeAndWait();
- onReady?.Invoke();
- }
- sealed class CoroutineRunnerHost : MonoBehaviour
- {
- static CoroutineRunnerHost _instance;
- public static CoroutineRunnerHost Instance
- {
- get
- {
- if (_instance != null)
- return _instance;
- var go = new GameObject("[ScreenOrientationHelper]");
- DontDestroyOnLoad(go);
- _instance = go.AddComponent<CoroutineRunnerHost>();
- return _instance;
- }
- }
- }
- }
- }
|