1

I've developed an Android game using Unity. And in the end I've faced the next problem:

Each level of my game has strict borders (like a box) along the sides of the screen. I've been developing the game with aspect 9:16 (pic. 1). When I've built it and run on my phone (it's 9:19.5) I found out that leftmost and rightmost parts of the levels are out of the screen (pic. 2).

How can I fix this, so the game fits every aspect ratio?

I've tried different solutions, but nothing satisfied me. I'm so desperate that I'm already agree to just scale it down from 9:16 so that it fits the WIDTH of the screen plus adding black bars at the top and at the bottom (pic. 3). Height is not so important in my game, but width is crucial. Or is there some better solution?

enter image description here enter image description here enter image description here

UPDATE 2: My project's design (kinda grayboxing): enter image description here Here's how it looks FINE with different aspects in play mode: enter image description here And here's how it looks POORLY with different aspects in play mode: enter image description here

16
  • 1
    It depends how you setup your Canvas and which constraints you added to your main views. Can you share that detail too? Commented Feb 22, 2021 at 17:12
  • 1
    I mean the Anchor Presets. That's where the magic happens. Using those you declare how your View should be placed inside your Canvas. 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. Commented Feb 23, 2021 at 9:50
  • 1
    Oh, so you are designing your level layout in 3d with cubes and trying to fit that it in your screen with a proper aspect ratio? Do you really need to design this with cubes? It seems like a 2d game to me. Commented Feb 23, 2021 at 14:40
  • 1
    If it's going to be a 2D game, no need to use 3D objects to design the level. It will be much easier to achieve the same design using Sprites and UI elements which will scale properly in every screen. Commented Feb 23, 2021 at 15:37
  • 1
    Yes, the white borders can be a simple Image inside the Canvas and the level should not be part of the Canvas itself. And the other part (levels, characters and etc) can be used as a 2D Sprite. Commented Feb 23, 2021 at 16:00

2 Answers 2

1

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;
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Wow, this is something! I'm definitely going to try it, although this is, as you said, "big and scary" for me and gonna take a while to understand. Thank you very much!
0

So, I found a way to achieve the goal. I added an empty game object to the scene and child'ed to it all the level structure, plus adding two black bars (as simple cubes) above and below the level. With that approach I can scale that parent game object so that all the level structure scales proportionally. All I have to do then is creating a script for scaling the parent game object according to the width of the screen.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.