Currently having the same issue in 2020.3.8f1 (setting allowed rotations doesn’t force a reevaluation of the current rotation when it becomes disallowed), and eventually I tried a solution as proposed above- force to an allowed orientation, then wait before returning to auto.(AllowedRotations being my own enum-flagset; code is run whenever Screen.width/Screen.height changes frame-over-frame in Update)
private void SetRotations(AllowedRotations allowedRotations)
{
Screen.autorotateToPortrait = ((int)allowedRotations & (int)AllowedRotations.Portrait) != 0;
Screen.autorotateToPortraitUpsideDown = ((int)allowedRotations & (int)AllowedRotations.PortraitUpsideDown) != 0;
Screen.autorotateToLandscapeLeft = ((int)allowedRotations & (int)AllowedRotations.LandscapeLeft) != 0;
Screen.autorotateToLandscapeRight = ((int)allowedRotations & (int)AllowedRotations.LandscapeRight) != 0;
if (_attemptForce)
{
// we can't force a re-execution of the internal autorotation,
// or at least I haven't found that API,
// so force orientation to the first sensible allowed value
// and wait for the user to rotate correspondingly.
if (Screen.autorotateToPortrait && Input.deviceOrientation == DeviceOrientation.Portrait) Screen.orientation = ScreenOrientation.Portrait;
else if (Screen.autorotateToPortraitUpsideDown && Input.deviceOrientation == DeviceOrientation.PortraitUpsideDown) Screen.orientation = ScreenOrientation.PortraitUpsideDown;
else if (Screen.autorotateToLandscapeLeft && Input.deviceOrientation == DeviceOrientation.LandscapeLeft) Screen.orientation = ScreenOrientation.LandscapeLeft;
else if (Screen.autorotateToLandscapeRight && Input.deviceOrientation == DeviceOrientation.LandscapeRight) Screen.orientation = ScreenOrientation.LandscapeRight;
else if (Screen.autorotateToPortrait) Screen.orientation = ScreenOrientation.Portrait;
else if (Screen.autorotateToPortraitUpsideDown) Screen.orientation = ScreenOrientation.PortraitUpsideDown;
else if (Screen.autorotateToLandscapeLeft) Screen.orientation = ScreenOrientation.LandscapeLeft;
else if (Screen.autorotateToLandscapeRight) Screen.orientation = ScreenOrientation.LandscapeRight;
// immediate return will return to the last internally-detected auto value,
// even if that value is no longer permitted
//Screen.orientation = ScreenOrientation.AutoRotation;
StartCoroutine(ResetToAuto());
}
}
Unfortunately, it appears that the auto-orientation my device is returning to is the most recently “cached” autorotation from before I set the explicit rotation. E.g. if I am in portrait, force to landscape, coroutine-wait a few frames, then set back to auto, Unity immediately returns to portrait even though this is disallowed.
I then tried a more sophisticated solution- waiting until the device is in the forced orientation before releasing… and as of writing, that seems to either not change the orientation at all, or occasionally just throw everything into a mess where Screen width, height, orientation and deviceOrientation get into a change cascade which never stabilizes to a consistent representation of device state.
bool IsRightSideUp()
{
bool yes = Screen.orientation == ScreenOrientation.AutoRotation || Screen.orientation == ScreenOrientation.Unknown;
if (Screen.orientation == ScreenOrientation.Portrait && Input.deviceOrientation == DeviceOrientation.Portrait) yes = true;
else if (Screen.orientation == ScreenOrientation.PortraitUpsideDown && Input.deviceOrientation == DeviceOrientation.PortraitUpsideDown) yes = true;
else if (Screen.orientation == ScreenOrientation.LandscapeLeft && Input.deviceOrientation == DeviceOrientation.LandscapeLeft) yes = true;
else if (Screen.orientation == ScreenOrientation.LandscapeRight && Input.deviceOrientation == DeviceOrientation.LandscapeRight) yes = true;
return yes;
}
IEnumerator ResetToAuto()
{
while (!IsRightSideUp())
{
yield return new WaitForEndOfFrame();
}
Screen.orientation = ScreenOrientation.AutoRotation;
}
So… there are a few possibilities here, obviously.
Unity could be bugged and not quite keeping screen orientation, autorotation, device rotation, and screen size in sync as far as they’re exposed to the developer on a given frame, meaning e.g. ScreenOrientation.Portrait does not always occur, when allowed, during DeviceOrientation.Portrait, and does not always mean that width<=height.
The (prototype) device I’m targeting could equally be bugged and not consistently exposing its orientation state to Unity, so Unity legitimately thinks it’s in portrait when the device is sideways.
However, one way or another, if I don’t try to force orientation, everything resolves correctly when the device is manually rotated to an allowed orientation, at which point, as per the OP, the allowed rotations are enforced.
Thus, it would remain very useful to simply be able to manually invoke whatever Unity does upon device rotation to recompute autorotation, assuming the API is too entrenched to allow disabling the current rotation to fall back on an allowed rotation.