I want to calculate and cache some variables after a rotation event has occurred. I don't see that in the Unity API. How?
Code (csharp): if ((Input.deviceOrientation == DeviceOrientation.LandscapeLeft) (Screen.orientation != ScreenOrientation.LandscapeLeft)) { Screen.orientation = ScreenOrientation.LandscapeLeft; } if ((Input.deviceOrientation == DeviceOrientation.LandscapeRight) (Screen.orientation != ScreenOrientation.LandscapeRight)) { Screen.orientation = ScreenOrientation.LandscapeRight; } This stuff?
Could you check if the screen orientation is not equal to the previous frames orientation. Then fire the event if true.
I'm looking for an event, not a constant Update check. (Unity typically just gives us MonoBehaviour methods with "On" in front of them.) I don't need it to be fully contained in the Unity API if there's a better way of handling it.
I guess it's possible via xcode event (didRotateBlah). This could get sent via UnitySendMessage from objective C to unity to a gameObject, in the same manner as the "On" functions. That is the only workaround I know, seems long-winded.
As it turns out, the enumerator list constants for ScreenOrientation and DeviceOrientation match, so I'm going with this. Code (csharp): IEnumerator CheckForChange() { while (true) switch (Input.deviceOrientation) { case DeviceOrientation.Unknown: case DeviceOrientation.FaceUp: case DeviceOrientation.FaceDown: yield return null; break; default: yield return Screen.orientation == (ScreenOrientation)Input.deviceOrientation ? null : StartCoroutine(Fade()); break; } }
This thread is old, but is there some new solution for checking change orientation? Or I have to do it in Update etc?
I've been researching this lately, and came up with the following class. Warning: Compiles, but untested! Just add the DeviceChange script to any active GameObject. I stole a bit of code from places (thanks Jessy!). You subscribe to the change events, and can have multiple methods subscribed to receive the event notice. For example, you could have each of your UI classes get notified when the resolution or orientation changes like this: Code (CSharp): DeviceChange.OnOrientationChange += MyOrientationChangeCode; DeviceChange.OnResolutionChange += MyResolutionChangeCode; DeviceChange.OnResolutionChange += MyOtherResolutionChangeCode; void MyOrientationChangeCode(DeviceOrientation orientation) { } void MyResolutionChangeCode(Vector2 resolution) { } Device Change Class: Code (CSharp): using System; using System.Collections; using UnityEngine; public class DeviceChange : MonoBehaviour { public static event Action<Vector2> OnResolutionChange; public static event Action<DeviceOrientation> OnOrientationChange; public static float CheckDelay = 0.5f; // How long to wait until we check again. static Vector2 resolution; // Current Resolution static DeviceOrientation orientation; // Current Device Orientation static bool isAlive = true; // Keep this script running? void Start() { StartCoroutine(CheckForChange()); } IEnumerator CheckForChange(){ resolution = new Vector2(Screen.width, Screen.height); orientation = Input.deviceOrientation; while (isAlive) { // Check for a Resolution Change if (resolution.x != Screen.width || resolution.y != Screen.height ) { resolution = new Vector2(Screen.width, Screen.height); if (OnResolutionChange != null) OnResolutionChange(resolution); } // Check for an Orientation Change switch (Input.deviceOrientation) { case DeviceOrientation.Unknown: // Ignore case DeviceOrientation.FaceUp: // Ignore case DeviceOrientation.FaceDown: // Ignore break; default: if (orientation != Input.deviceOrientation) { orientation = Input.deviceOrientation; if (OnOrientationChange != null) OnOrientationChange(orientation); } break; } yield return new WaitForSeconds(CheckDelay); } } void OnDestroy(){ isAlive = false; } } [Edit] Simplified code by using Actions, not Delegates. [Edit 2] Fixed bug (removed unneeded parenthesis) in example code
See http://docs.unity3d.com/ScriptRefer...ehaviour.OnRectTransformDimensionsChange.html If you have a script attached to a RectTransform, when the dimensions of that rect transform changes (e.g. as a result of a rotation), then that callback is called.
If you created a top-level gameobject (a panel perhaps) with a RectTransform anchor set to Stretch-Stretch to cover the entire screen, then I suppose you can watch for resolution changes by using OnRectTransformDimensionsChange() on that object. That would be neat to see.
This might seem a stupid change on top of Doug's version, but I think using the "editor-ready" UnityEvents is nice. Here it is, but I take no credit since it's a really really small improvement (IMHO): Code (CSharp): using System; using System.Collections; using UnityEngine; using UnityEngine.Events; public class DeviceChange : MonoBehaviour { public UnityEvent OnResolutionChange = new UnityEvent(); public UnityEvent OnOrientationChange = new UnityEvent(); public static float CheckDelay = 0.5f; // How long to wait until we check again. public static Vector2 resolution; // Current Resolution public static DeviceOrientation orientation; // Current Device Orientation static bool isAlive = true; // Keep this script running? void Start() { StartCoroutine(CheckForChange()); } IEnumerator CheckForChange() { resolution = new Vector2(Screen.width, Screen.height); orientation = Input.deviceOrientation; while (isAlive) { // Check for a Resolution Change if (resolution.x != Screen.width || resolution.y != Screen.height) { resolution = new Vector2(Screen.width, Screen.height); OnResolutionChange.Invoke(); } // Check for an Orientation Change switch (Input.deviceOrientation) { case DeviceOrientation.Unknown: // Ignore case DeviceOrientation.FaceUp: // Ignore case DeviceOrientation.FaceDown: // Ignore break; default: if (orientation != Input.deviceOrientation) { orientation = Input.deviceOrientation; OnOrientationChange.Invoke(); } break; } yield return new WaitForSeconds(CheckDelay); } } void OnDestroy() { isAlive = false; } }
@DougMcFarlane @tfHandle Thanks It was desired to simply change the Ortho size with orientation. The script was useful as was and tfHandle helped make it easier. Simply adding the script to the object you want changed and making the var for it instead of the cam used in my example included...
I've made what I think to be an improvement so it doesn't have to do a constant check and makes the call on the frame it occurs (instead of waiting for the CheckDelay), it utilizes Unity's OnRectTransformDimensionsChange call. I made use of the lazy singleton pattern. I also improved the resolution change check slightly so it won't callback when the screen is rotated 90 degrees. Code (CSharp): using UnityEngine; using UnityEngine.Events; [RequireComponent(typeof(RectTransform))] public class ScreenWatcher : MonoBehaviour { static ScreenWatcher instance = null; static UnityEvent OnResolutionChange = new UnityEvent(); static UnityEvent OnOrientationChange = new UnityEvent(); static Vector2 resolution; // Current Resolution static ScreenOrientation orientation; // Current Screen Orientation static void init() { if (instance != null) return; resolution = new Vector2(Screen.width, Screen.height); orientation = Screen.orientation; GameObject canvas = new GameObject("ScreenWatcher"); canvas.AddComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay; instance = canvas.AddComponent<ScreenWatcher>(); DontDestroyOnLoad(canvas); } private void Awake() { if (instance != this) { Destroy(this); } } private void OnRectTransformDimensionsChange() { // Check for an Orientation Change ScreenOrientation curOri = Screen.orientation; switch (curOri) { case ScreenOrientation.Unknown: // Ignore { break; } default: { if (orientation != curOri) { orientation = curOri; OnOrientationChange.Invoke(); } break; } } // Check for a Resolution Change if ((resolution.x != Screen.width && resolution.x != Screen.height) || (resolution.y != Screen.height && resolution.y != Screen.width)) { resolution = new Vector2(Screen.width, Screen.height); OnResolutionChange.Invoke(); } } public static void AddResolutionChangeListener(UnityAction callback) { init(); OnResolutionChange.AddListener(callback); } public static void RemoveResolutionChangeListener(UnityAction callback) { OnResolutionChange.RemoveListener(callback); } public static void AddOrientationChangeListener(UnityAction callback) { init(); OnOrientationChange.AddListener(callback); } public static void RemoveOrientationChangeListener(UnityAction callback) { OnOrientationChange.RemoveListener(callback); } private void OnDestroy() { OnResolutionChange.RemoveAllListeners(); OnOrientationChange.RemoveAllListeners(); if (instance == this) { instance = null; } } }
@ProtoTerminator Maybe I did something wrong, but it didn't work for me, while code posted by @tfHandle did. Built from Unity to Xcode simulator, didn't test on device. Code (CSharp): void Start () { Debug.Log("Started"); ScreenWatcher.AddOrientationChangeListener (OnOrientationChange); } public void OnOrientationChange () { Debug.Log ("Orientation changed"); }
@StanBishop Woops, That's because Unity calls Awake() during the AddComponent before it actually gets assigned to instance, so it destroys it immediately. Change Awake() to Start() and it should be good. I also fixed the OnDestroy() here: Code (CSharp): using UnityEngine; using UnityEngine.Events; [RequireComponent(typeof(RectTransform))] public class ScreenWatcher : MonoBehaviour { static ScreenWatcher instance = null; public static ScreenWatcher Instance { get { return instance; } } static UnityEvent OnResolutionChange = null; static UnityEvent OnOrientationChange = null; static Vector2 resolution; // Current Resolution static ScreenOrientation orientation; // Current Screen Orientation static void init() { if (instance != null) return; resolution = new Vector2(Screen.width, Screen.height); orientation = Screen.orientation; OnResolutionChange = new UnityEvent(); OnOrientationChange = new UnityEvent(); GameObject canvas = new GameObject("ScreenWatcher"); canvas.AddComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay; instance = canvas.AddComponent<ScreenWatcher>(); DontDestroyOnLoad(canvas); } private void Start() { if (instance != this) { Destroy(this); } } private void OnRectTransformDimensionsChange() { // Check for an orientation change. ScreenOrientation curOri = Screen.orientation; switch (curOri) { case ScreenOrientation.Unknown: // Ignore { break; } default: { if (orientation != curOri) { orientation = curOri; OnOrientationChange.Invoke(); } break; } } // Check for a resolution change. if ((resolution.x != Screen.width && resolution.x != Screen.height) || (resolution.y != Screen.height && resolution.y != Screen.width)) { resolution = new Vector2(Screen.width, Screen.height); OnResolutionChange.Invoke(); } } public static void AddResolutionChangeListener(UnityAction callback) { init(); OnResolutionChange.AddListener(callback); } public static void RemoveResolutionChangeListener(UnityAction callback) { OnResolutionChange.RemoveListener(callback); } public static void AddOrientationChangeListener(UnityAction callback) { init(); OnOrientationChange.AddListener(callback); } public static void RemoveOrientationChangeListener(UnityAction callback) { OnOrientationChange.RemoveListener(callback); } private void OnDestroy() { if (instance == this) { // Clean up memory. OnResolutionChange.RemoveAllListeners(); OnResolutionChange = null; OnOrientationChange.RemoveAllListeners(); OnOrientationChange = null; instance = null; } } } [EDIT] I just tested on my android, and it does not detect a change between Portrait/PortraitUpsideDown or LandscapeRight/LandscapeLeft. That's a limitation of the OnRectTransformDimensionsChange because the rect itself is still the same in that case. If you need to detect between those 180 degree rotations, I guess your best bet will be to use tfHandle's code above.
My script using Screen.orientation only with less code, which works fine on mobile for detecting device orientation change. Code (csharp): using System; using System.Collections; using UnityEngine; using UnityEngine.Events; public class OrientationManager : MonoBehaviour { // This event will only be called when an orientation changed (i.e. won't be call at lanch) public static event UnityAction<ScreenOrientation> orientationChangedEvent; [SerializeField] private bool _debugMode = false; private ScreenOrientation _orientation; void Start() { _orientation = Screen.orientation; InvokeRepeating("CheckForChange", 1, 1); } private static void OnOrientationChanged(ScreenOrientation orientation) { if (orientationChangedEvent != null) orientationChangedEvent(orientation); } private void CheckForChange() { if (_debugMode) Debug.Log("Screen.orientation=" + Screen.orientation); if (_orientation != Screen.orientation) { _orientation = Screen.orientation; OnOrientationChanged(_orientation); } } #if UNITY_EDITOR [ContextMenu("Print Orientation")] private void PrintOrientation() { Debug.Log(_orientation); } [ContextMenu("Simulate Landscape Left")] private void SetLandscapeLeft() { OnOrientationChanged(ScreenOrientation.LandscapeLeft); } [UnityEditor.MenuItem("Debug/Orientation/Simulate Landscape Left")] private static void DoSetLandscapeLeft() { OnOrientationChanged(ScreenOrientation.LandscapeLeft); } [ContextMenu("Simulate Landscape Right")] private void SetLandscapeRight() { OnOrientationChanged(ScreenOrientation.LandscapeRight); } [UnityEditor.MenuItem("Debug/Orientation/Simulate Landscape Right")] private static void DoSetLandscapeRight() { OnOrientationChanged(ScreenOrientation.LandscapeRight); } #endif }
A question, why use INVOKE? You could use Update. The other thing, I'm trying to use your code, how do I do some action when I change direction? It happens that if I put an action (move an image) with the INVOKE, the change of position is repeated in loop until it disappears from the screen, and what I need is for it to move only when changing and not at every moment.
Please change that into Code (CSharp): if ((resolution.x != Screen.width || resolution.y != Screen.height) && (resolution.x != Screen.height || resolution.y != Screen.width)) Otherwise if you go from e.g. 600x500 to 500x500 it won't notice. [EDIT] Not that that would ever happen
Good catch. It could also be improved to check if the orientation was changed or not to detect a 600x500 => 500x600 without rotating. Code (CSharp): if ((resolution.x != Screen.width || resolution.y != Screen.height) && (!orientationChanged || resolution.x != Screen.height || resolution.y != Screen.width))
Add to a canvas stretched full screen Code (CSharp): using System; using UnityEngine; using UnityEngine.EventSystems; [RequireComponent(typeof(RectTransform))] public class UiMonitor : Singleton<UiMonitor> { public RectTransform rectTransform; public event EventHandler<DimensionsChangedEventArgs> DimensionsChanged; private void Start() { rectTransform = GetComponent<RectTransform>(); } protected void OnRectTransformDimensionsChange() { //Debug.Log("RectTransformDimensionsChange firing on " + this.name + " fine."); if (DimensionsChanged != null) { int w = (int)rectTransform.rect.width; int h = (int)rectTransform.rect.height; DimensionsChanged(this, new DimensionsChangedEventArgs(w, h)); } } } public class DimensionsChangedEventArgs : EventArgs { public int width = 0; public int height = 0; public DimensionsChangedEventArgs(int width, int height) { this.width = width; this.height = height; } }
Thanks all for the suggestions. I wanted to ask what would happen if the portrait orientation lock for the device is enabled. Will the change orientation event still trigger?
Here's how I go about it, simple is best. It doesn't need any coroutines or update loop. Code (CSharp): using System.Collections; using UnityEngine; using UnityEngine.EventSystems; public class Orientation : MonoBehaviour { RectTransform rectTransform; GameObject landscape; GameObject portrait; void Awake() { rectTransform = GetComponent<RectTransform>(); landscape = transform.Find("landscape").gameObject; portrait = transform.Find("portrait").gameObject; SetOrientation(); } void SetOrientation() { if(rectTransform == null) return; bool verticalOrientation = rectTransform.rect.width < rectTransform.rect.height ? true : false; portrait.SetActive(verticalOrientation); landscape.SetActive(!verticalOrientation); } void OnRectTransformDimensionsChange() { SetOrientation(); } } OnRectTransformDimensionsChange will get called before AND after Awake, so you'll need to check for null. Also, it's a event, so theres no per frame hit
This is been working for me, works even in Unity Editor which is helpful for testing. public class viewController : MonoBehaviour { float screenWidth; string orientacionSystem; void Start() { screenWidth = Screen.width; orientacionSystem = "Portrait"; if (Screen.width > Screen.height) { orientacionSystem = "Landscape"; } if (orientacionSystem == "Portrait") { /*Do stuff for Portrait */ } else { /*Do stuff for Landscape */ } } private void Update() { if (screenWidth != Screen.width) { screenWidth = Screen.width; print("change of orientation detected "); orientacionSystem = "Portrait"; if (Screen.width > Screen.height) { orientacionSystem = "Landscape"; } if (orientacionSystem == "Portrait") { /*Do stuff for Portrait */ } else { /*Do stuff for Landscape */ } } } }
There is one caveat when using the OnRectTransformDimensionsChange method. If the screen is exactly a square, then the OnRectTransformDimensionsChange method won't be called when screen is being rotated.