268 lines
11 KiB
C#
268 lines
11 KiB
C#
// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved.
|
|
// This code can only be used under the standard Unity Asset Store End User License Agreement
|
|
// A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms
|
|
|
|
// ReSharper disable RedundantUsingDirective
|
|
using System;
|
|
// ReSharper restore RedundantUsingDirective
|
|
using System.Collections;
|
|
using Doozy.Runtime.Common;
|
|
using Doozy.Runtime.Common.Attributes;
|
|
using Doozy.Runtime.Common.Utils;
|
|
using Doozy.Runtime.Mody;
|
|
using Doozy.Runtime.Signals;
|
|
using UnityEngine;
|
|
// ReSharper disable MemberCanBePrivate.Global
|
|
// ReSharper disable UnusedMember.Local
|
|
|
|
namespace Doozy.Runtime.UIManager.Orientation
|
|
{
|
|
/// <summary>
|
|
/// Detects the current screen orientation of the target device.
|
|
/// </summary>
|
|
[RequireComponent(typeof(RectTransform), typeof(Canvas))]
|
|
[DisallowMultipleComponent]
|
|
public class OrientationDetector : SingletonBehaviour<OrientationDetector>
|
|
{
|
|
#if UNITY_EDITOR
|
|
[UnityEditor.MenuItem("GameObject/Doozy/Orientation/Orientation Detector", false, 8)]
|
|
private static void CreateComponent(UnityEditor.MenuCommand menuCommand)
|
|
{
|
|
GameObjectUtils.AddToScene<OrientationDetector>("Orientation Detector", true, true);
|
|
}
|
|
#endif
|
|
|
|
// ReSharper disable MemberCanBePrivate.Global
|
|
private static string streamCategory => "Orientation";
|
|
private static string streamName => nameof(OrientationDetector);
|
|
// ReSharper restore MemberCanBePrivate.Global
|
|
|
|
[ClearOnReload]
|
|
private static SignalStream s_stream;
|
|
/// <summary> Signal stream for the OrientationDetector </summary>
|
|
public static SignalStream stream => s_stream ??= SignalsService.GetStream(streamCategory, streamName);
|
|
|
|
private RectTransform m_RectTransform;
|
|
/// <summary> Reference to the RectTransform component </summary>
|
|
public RectTransform rectTransform => m_RectTransform ? m_RectTransform : m_RectTransform = GetComponent<RectTransform>();
|
|
|
|
private Canvas m_Canvas;
|
|
/// <summary> Reference to the Canvas component </summary>
|
|
public Canvas canvas => m_Canvas ? m_Canvas : m_Canvas = GetComponent<Canvas>();
|
|
|
|
/// <summary> Callback triggered when the device orientation changed </summary>
|
|
public DetectedOrientationEvent OnOrientationChanged = new DetectedOrientationEvent();
|
|
|
|
/// <summary> Callback triggered when the device orientation changed </summary>
|
|
public ModyEvent OnAnyOrientation = new ModyEvent();
|
|
|
|
/// <summary> Callback triggered when the device orientation changed to Portrait </summary>
|
|
public ModyEvent OnPortraitOrientation = new ModyEvent();
|
|
|
|
/// <summary> Callback triggered when the device orientation changed to Landscape </summary>
|
|
public ModyEvent OnLandscapeOrientation = new ModyEvent();
|
|
|
|
[SerializeField] private DetectedOrientation CurrentOrientation = DetectedOrientation.Unknown;
|
|
/// <summary> Current detected device orientation </summary>
|
|
public DetectedOrientation currentOrientation
|
|
{
|
|
get => CurrentOrientation;
|
|
private set => CurrentOrientation = value;
|
|
}
|
|
|
|
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
|
/// <summary> Previous logical screen orientation (previous Screen.orientation value) </summary>
|
|
public ScreenOrientation previousScreenOrientation { get; private set; }
|
|
|
|
/// <summary> Internal variable used to count evey orientation check. This is needed to cancel two notifications passes happening OnRectTransformDimensionsChange </summary>
|
|
private int orientationCheckCount { get; set; }
|
|
|
|
/// <summary> Coroutine that checks the device orientation every checkInterval seconds </summary>
|
|
private Coroutine orientationCheckCoroutine { get; set; }
|
|
/// <summary> Interval in seconds between each orientation check </summary>
|
|
private float checkInterval { get; set; } = 0.1f;
|
|
|
|
private bool firstOrientationCheck { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Set the proper settings for the Canvas and RectTransform components
|
|
/// </summary>
|
|
public void Initialize()
|
|
{
|
|
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
if (canvas.isRootCanvas) return;
|
|
rectTransform.anchorMin = Vector2.zero;
|
|
rectTransform.anchorMax = Vector2.one;
|
|
}
|
|
|
|
private void Reset()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnValidate()
|
|
{
|
|
Initialize();
|
|
}
|
|
#endif
|
|
|
|
protected override void Awake()
|
|
{
|
|
firstOrientationCheck = true;
|
|
base.Awake();
|
|
currentOrientation = DetectedOrientation.Unknown;
|
|
Initialize();
|
|
}
|
|
|
|
private IEnumerator Start()
|
|
{
|
|
// CheckDeviceOrientation();
|
|
yield return null;
|
|
// CheckDeviceOrientation();
|
|
yield return new WaitForEndOfFrame();
|
|
CheckDeviceOrientation();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
StartCheckOrientation();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
StopCheckOrientation();
|
|
}
|
|
|
|
private void StartCheckOrientation()
|
|
{
|
|
if (orientationCheckCoroutine != null) return;
|
|
orientationCheckCoroutine = StartCoroutine(CheckOrientation());
|
|
}
|
|
|
|
private void StopCheckOrientation()
|
|
{
|
|
if (orientationCheckCoroutine == null) return;
|
|
StopCoroutine(orientationCheckCoroutine);
|
|
orientationCheckCoroutine = null;
|
|
}
|
|
|
|
private IEnumerator CheckOrientation()
|
|
{
|
|
var wait = new WaitForSecondsRealtime(checkInterval);
|
|
yield return new WaitForEndOfFrame();
|
|
while (true)
|
|
{
|
|
yield return wait;
|
|
CheckDeviceOrientation();
|
|
}
|
|
// ReSharper disable once IteratorNeverReturns
|
|
}
|
|
|
|
private void OnRectTransformDimensionsChange()
|
|
{
|
|
orientationCheckCount++;
|
|
if (orientationCheckCount < 2) return;
|
|
orientationCheckCount = 0;
|
|
CheckDeviceOrientation();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check the current device orientation and send a signal with the orientation value if it has changed.
|
|
/// <para/> This method is called automatically by the OrientationDetector.
|
|
/// </summary>
|
|
public void CheckDeviceOrientation() =>
|
|
CheckDeviceOrientation(false);
|
|
|
|
/// <summary>
|
|
/// Check the current device orientation and send a signal with the orientation value if it has changed.
|
|
/// </summary>
|
|
/// <param name="forceUpdate"> Force the update of the orientation value, even if it hasn't changed (to trigger callbacks) </param>
|
|
public void CheckDeviceOrientation(bool forceUpdate)
|
|
{
|
|
#if UNITY_EDITOR
|
|
{
|
|
//Portrait
|
|
if (Screen.width < Screen.height)
|
|
{
|
|
if (currentOrientation == DetectedOrientation.Portrait && !forceUpdate) return;
|
|
UpdateOrientation(DetectedOrientation.Portrait);
|
|
return;
|
|
}
|
|
|
|
//Landscape
|
|
if (currentOrientation == DetectedOrientation.Landscape && !forceUpdate) return;
|
|
UpdateOrientation(DetectedOrientation.Landscape);
|
|
|
|
|
|
}
|
|
#else //!UNITY_EDITOR
|
|
{
|
|
if (!firstOrientationCheck && previousScreenOrientation == Screen.orientation && !forceUpdate) return;
|
|
|
|
firstOrientationCheck = false;
|
|
|
|
switch (Screen.orientation)
|
|
{
|
|
case ScreenOrientation.Portrait:
|
|
if (currentOrientation == DetectedOrientation.Portrait && !forceUpdate)
|
|
return; //if the current orientation is portrait, then we don't need to update anything
|
|
UpdateOrientation(DetectedOrientation.Portrait);
|
|
break;
|
|
case ScreenOrientation.PortraitUpsideDown:
|
|
if (currentOrientation == DetectedOrientation.Portrait && !forceUpdate)
|
|
return; //if the current orientation is portrait, then we don't need to update anything
|
|
UpdateOrientation(DetectedOrientation.Portrait);
|
|
break;
|
|
case ScreenOrientation.LandscapeLeft:
|
|
if (currentOrientation == DetectedOrientation.Landscape && !forceUpdate)
|
|
return; //if the current orientation is landscape, then we don't need to update anything
|
|
UpdateOrientation(DetectedOrientation.Landscape);
|
|
break;
|
|
case ScreenOrientation.LandscapeRight:
|
|
if (currentOrientation == DetectedOrientation.Landscape && !forceUpdate)
|
|
return; //if the current orientation is landscape, then we don't need to update anything
|
|
UpdateOrientation(DetectedOrientation.Landscape);
|
|
break;
|
|
case ScreenOrientation.AutoRotation:
|
|
//fallback to the default orientation -> Landscape
|
|
if (currentOrientation == DetectedOrientation.Landscape && !forceUpdate)
|
|
return; //if the current orientation is landscape, then we don't need to update anything
|
|
UpdateOrientation(DetectedOrientation.Landscape);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
}
|
|
#endif //UNITY_EDITOR
|
|
}
|
|
|
|
|
|
private void UpdateOrientation(DetectedOrientation orientation)
|
|
{
|
|
// Debug.Log($"[{Time.frameCount}] Orientation changed to {orientation}");
|
|
stream.SendSignal(orientation);
|
|
OnOrientationChanged.Invoke(orientation);
|
|
OnAnyOrientation?.Execute();
|
|
switch (orientation)
|
|
{
|
|
case DetectedOrientation.Portrait:
|
|
OnPortraitOrientation?.Execute();
|
|
break;
|
|
case DetectedOrientation.Landscape:
|
|
OnLandscapeOrientation?.Execute();
|
|
break;
|
|
case DetectedOrientation.Unknown:
|
|
//do nothing
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null);
|
|
}
|
|
currentOrientation = orientation;
|
|
#if !UNITY_EDITOR
|
|
previousScreenOrientation = Screen.orientation;
|
|
#endif
|
|
}
|
|
}
|
|
}
|