Unity's camera assumes that games will be played in a landscape orientation. Therefore the amount of the game world that is rendered vertically will always be the same on any screen, regardless of aspect ratio. If you are restricting your game to be played only on mobile platforms, and only in portrait mode, then you will want to force Unity to do the opposite. You want the camera to always render the same width, and show a variable amount of height depending on aspect ratio. (Helpful hint: Develop with the smallest aspect ratio you expect, like 4:5, not 9:16. Configure all of the aspect ratios or resolutions you want to test in the dropdown menu at the top of the Game View. Switch between these for testing, and allow your UI and game view to expand out for taller aspect ratios. Otherwise your UI will probably run together on smaller aspect ratios. See my post on CanvasScaler here.)
Anyway, to change the default camera behavior, you'll need to add a script to your camera. Something like this should do:
Edit: I modified the component to handle both perspective and orthographic cameras. Based on your game it appears you are, or perhaps should be, using an orthographic camera, which my previous answer did not handle. Also, there are no more properties on the component. Simply attach to a camera, then use the camera's properties as normal. Also, it now handles viewports, so it works with cameras that don't occupy the entire screen.
There is one known issue. With an orthographic camera, if you attempt to adjust the viewport to be greater than the entire screen size, something internal to Unity resets the projection matrix and overrides this component's behavior. To fix, simply toggle the component, or reload the scene or editor, anything that triggers the component to do its thing again.
using UnityEngine;
[ExecuteAlways]
[RequireComponent(typeof(Camera))]
public class HorizontallyAlignedCamera : MonoBehaviour
{
private Camera _camera;
private float _aspectRatio;
private float _fieldOfView;
private bool _isOrthographic;
private float _orthographicSize;
private void Awake()
{
_camera = GetComponent<Camera>();
}
private void OnEnable()
{
CachePropertiesAndRecalculate();
}
private void LateUpdate()
{
if(CameraPropertyHasChanged())
CachePropertiesAndRecalculate();
}
private void OnDisable()
{
_camera.ResetProjectionMatrix();
}
private bool CameraPropertyHasChanged()
{
bool hasChanged = (_aspectRatio != _camera.aspect
|| _fieldOfView != _camera.fieldOfView
|| _isOrthographic != _camera.orthographic
|| _orthographicSize != _camera.orthographicSize);
return hasChanged;
}
private void CacheCameraProperties()
{
_aspectRatio = _camera.aspect;
_fieldOfView = _camera.fieldOfView;
_isOrthographic = _camera.orthographic;
_orthographicSize = _camera.orthographicSize;
}
private void CachePropertiesAndRecalculate()
{
CacheCameraProperties();
if(_camera.orthographic)
RecalculateOrthographicMatrix();
else
RecalculatePerspectiveMatrix();
}
private void RecalculatePerspectiveMatrix()
{
float near = _camera.nearClipPlane;
float nearx2 = near * 2.0f;
float far = _camera.farClipPlane;
float halfFovRad = _camera.fieldOfView * 0.5f * Mathf.Deg2Rad;
// This is what aligns the camera horizontally.
float width = nearx2 * Mathf.Tan(halfFovRad);
float height = width / _camera.aspect;
// This is the default behavior.
//float height = nearx2 * Mathf.Tan(halfFovRad);
//float width = height * _camera.aspect;
float a = nearx2 / width;
float b = nearx2 / height;
float c = -(far + near) / (far - near);
float d = -(nearx2 * far) / (far - near);
Matrix4x4 newProjectionMatrix = new Matrix4x4(
new Vector4(a, 0.0f, 0.0f, 0.0f),
new Vector4(0.0f, b, 0.0f, 0.0f),
new Vector4(0.0f, 0.0f, c, -1.0f),
new Vector4(0.0f, 0.0f, d, 0.0f));
_camera.projectionMatrix = newProjectionMatrix;
}
private void RecalculateOrthographicMatrix()
{
// This is what aligns the camera horizontally.
float width = 2.0f * _camera.orthographicSize;
float height = width / _camera.aspect;
// This is the default behavior.
//float height = 2.0f * _camera.orthographicSize;
//float width = height * _camera.aspect;
float near = _camera.nearClipPlane;
float far = _camera.farClipPlane;
float a = 2.0f / width;
float b = 2.0f / height;
float c = -2.0f / (far - near);
float d = -(far + near) / (far - near);
Matrix4x4 newProjectionMatrix = new Matrix4x4(
new Vector4(a, 0.0f, 0.0f, 0.0f),
new Vector4(0.0f, b, 0.0f, 0.0f),
new Vector4(0.0f, 0.0f, c, 0.0f),
new Vector4(0.0f, 0.0f, d, 1.0f));
_camera.projectionMatrix = newProjectionMatrix;
}
}
Anchor Presets. That's where the magic happens. Using those you declare how yourViewshould be placed inside yourCanvas. For example: Top Right, Bottom Left, Stretch horizontal or vertical and etc. If it's ok with you it would be better to share a sneak peek of your design in order to help improving it.