I’m working on a project where users should be able to use the app in any orientation. However, when the orientation changes, the camera appears to zoom in because the FOV is staying the same but the screen height is changing.
I’ve tried using the following formula to keep the camera zoom the same, but this causes the FOV to change a lot on devices with different resolutions.
var camera.fieldOfView = 2.0f * Mathf.Atan(Screen.height * 0.5f / distance) * Mathf.Rad2Deg;
Is there a way to change the frustum/FOV to make an object appear to be the same size across all mobile devices and resolutions/orientations?
What exactly do you mean by “the same size”. The code you have above appears to be attempting to adjust the FOV to keep a constant number of pixels per game unit at a specified distance from the camera. That code doesn’t really do anything special for portrait orientation as it’s just setting the camera FOV, and Unity’s camera FOV controls the vertical FOV. It also means very high resolution devices will display things very small compared to lower resolution devices.
What I suspect you actually want is a way to set a minimum aspect FOV. Basically make sure the horizontal FOV never goes below a minimum value when going into portrait mode.
You can do something like this. This keeps the a consistent width area.
var scale = camera.aspect / (base aspect ratio like 16/9);
var mat = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(scale, scale, 1));
camera.ResetProjectionMatrix();
camera.projectionMatrix = mat * camera.projectionMatrix;
Sorry, I realize that I’m not being very clear, I’m just having trouble describing the issue!
What I’m looking for is a way to make a game object be a a constant pixel height on the screen.
The default behavior with autorotate on looks like this:
After posting the question, I wrote some code to handle rotation myself. It results in the behavior that I want, but it is a little over-complicated. This is how I want things to work:
So I’m wondering if there’s a way to achieve that by adjusting some camera settings when changing the orientation, or if I should just stick to what I have.
EDIT: For some context, I’m working on a constellation viewer. It works similarly to a photosphere viewer, where the camera is in a fixed position in the centre, and it rotates depending on the device gyroscope or touch controls.
Unity’s camera.fieldOfView sets the vertical field of view. Unity now provides built in functions for converting from a horizontal FOV to vertical FOV that you can use if you want to properly support portrait aspect aspect ratios. Unity’s documentation mentions it briefly as well.
What I like to do is think about what the minimum vertical and horizontal FOV should be for your game. If at your current aspect ratio the horizontal FOV is less than the minimum you have set, calculate the appropriate vertical FOV to maintain the horizontal FOV.
If anyone finds this useful, created an solution that keeps the horizontal FOV if device orientation changes to portrait.
Just slap on your camera and define the aspect ratio where it starts keeping stuff horizontally visible.
IE: You like how your fov horizontally looks at 16:9, just use the default HFovOnAspect of 1.777. Job done.
Regular camera variant
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class CameraFovSwitcher : MonoBehaviour {
Camera cam;
Camera Cam { get { if (!cam) cam = GetComponent<Camera>(); return cam; } }
[Range(0.1f, 2)]
[Tooltip("Aspect treshold for switching to horizontal fov calculation")]
public float HFovOnAspect = 1.777f;
float vFov;
float vFovNew;
private void Awake() {
vFov = Cam.fieldOfView;
}
void Update() {
if (Cam.aspect < HFovOnAspect) {
vFovNew = Camera.HorizontalToVerticalFieldOfView(Camera.VerticalToHorizontalFieldOfView(vFov, HFovOnAspect), Cam.aspect);
if (Cam.fieldOfView != vFovNew) {
Cam.fieldOfView = vFovNew;
}
} else if (Cam.fieldOfView != vFov) {
Cam.fieldOfView = vFov;
}
}
}
Cinemachine Virtual Camera variant
using UnityEngine;
using Cinemachine;
[RequireComponent(typeof(CinemachineVirtualCamera))]
public class CMCameraFovSwitcher : MonoBehaviour {
CinemachineVirtualCamera cam;
CinemachineVirtualCamera Cam { get { if (!cam) cam = GetComponent<CinemachineVirtualCamera>(); return cam; } }
[Range(0.1f,2)]
[Tooltip("Aspect treshold for switching to horizontal fov calculation")]
public float HFovOnAspect = 1.777f;
float vFov;
float vFovNew;
private void Awake() {
vFov = Cam.m_Lens.FieldOfView;
}
void Update() {
if (Cam.m_Lens.Aspect < HFovOnAspect) {
vFovNew = Camera.HorizontalToVerticalFieldOfView(Camera.VerticalToHorizontalFieldOfView(vFov, HFovOnAspect), Cam.m_Lens.Aspect);
if (Cam.m_Lens.FieldOfView != vFovNew) {
Cam.m_Lens.FieldOfView = vFovNew;
}
} else if (Cam.m_Lens.FieldOfView != vFov) {
Cam.m_Lens.FieldOfView = vFov;
}
}
}