Search code examples
unity-game-engineorientationscene

How can I force an orientation per scene in Unity3D


Unity3D has a Screen class with an orientation property that allows you to force orientation in code, which lets you have different scenes with different orientations (useful in mini-games).

Setting this works fine for Android but crashes on iOS. What is the fix?


Solution

  • The problem is the file UnityViewControllerBaseiOS.mm that gets generated during the build for iOS has an assert in it which inadvertently prevents this property from being used. It is possible to create a post-build class that runs after the iOS build files have been generated that can alter the generated code before you compile it in XCode.

    Just create a C# script named iOSScreenOrientationFix.cs and paste in the following code - adapted from this Unity3D forum post. Note that this file must be placed in a folder named Editor, or in one of its subfolders.

    using UnityEngine;
    using UnityEditor;
    using UnityEditor.Callbacks;
    using System.IO;
    
    namespace Holovis
    {
        public class iOSScreenOrientationFix : MonoBehaviour
        {
    #if UNITY_CLOUD_BUILD
        // This method is added in the Advanced Features Settings on UCB
        // PostBuildProcessor.OnPostprocessBuildiOS
        public static void OnPostprocessBuildiOS (string exportPath)
        {
            Debug.Log("OnPostprocessBuildiOS");
            ProcessPostBuild(BuildTarget.iPhone,exportPath);
        }
    #endif
    
            [PostProcessBuild]
            public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
            {
    #if !UNITY_CLOUD_BUILD
                ProcessPostBuild(buildTarget, path);
    #endif
            }
    
            private static void ProcessPostBuild(BuildTarget buildTarget, string path)
            {
                if (buildTarget == BuildTarget.iOS)
                {
    #if !UNITY_CLOUD_BUILD
                    Debug.Log("Patching iOS to allow setting orientation");
    #endif
                    string filePath = Path.Combine(path, "Classes");
                    filePath = Path.Combine(filePath, "UI");
                    filePath = Path.Combine(filePath, "UnityViewControllerBaseiOS.mm");
    
                    Debug.Log("File Path for View Controller Class: " + filePath);
    
                    string classFile = File.ReadAllText(filePath);
    
                    string newClassFile = classFile.Replace("NSAssert(UnityShouldAutorotate()", "//NSAssert(UnityShouldAutorotate()");
    
                    File.WriteAllText(filePath, newClassFile);
                }
            }
        }
    }
    

    You can set it in a scene by attaching the following MonoBehaviour to a game object

    using UnityEngine;
    
    namespace Holovis
    {
        public class SetDeviceOrientation : MonoBehaviour
        {
            public ScreenOrientation orientation = ScreenOrientation.AutoRotation;
    
            void Awake()
            {
                Screen.orientation = orientation;
            }
        }
    }
    

    NOTE: Setting Screen.orientation has no effect when running on desktop, in the Unity editor, or when testing using Unity Remote.