Understand Aspect Ratios and Screen Resolutions In Unity
🧩

Understand Aspect Ratios and Screen Resolutions In Unity

image

Like the 12 labours of Hercules, handling different aspect ratios in unity is a hard feature to achieve.

It’s difficult because there are many possibilities and different things to consider.

The goal today is to let you better understand what is important to remember and help you to become a better Unity Game Developer.

BecauseĀ it’s quite easy to make mistakes or choose the wrong way to handle different aspect ratios in Unity.

Besides this, I’m going to show you some helpful implementations that will help you to quickly and easily solve this difficult labour!

I will talk about a 2D world, but basically you can consider the same concepts as basics for a 3D world too!

Ok then what are we waiting for? Let’s go!

šŸ–¼Aspect Ratios in Unity and in General

image

First of all, what is the aspect ratio?

šŸ‘‰šŸ»
The aspect ratio of an image is the ratio of its width to its height. It is commonly expressed as two numbers separated by a colon, as in 16:9. For an x:y aspect ratio, the image is x units wide and y units high. Widely used aspect ratios include 1.85:1 and 2.39:1 in film photography, 4:3 and 16:9 in television, and 3:2 in still camera photography.

Se when we are going to design our game, we should find an aspect ratio that could fit to design the game.

We can choose our desired aspect ratio or we can alternatively pick one of the most used ones: like 16:9 for landscape games.

Aspect ratio is the result of width/height division. 16:9 means 16/9 = 1,77777777777777778.

The important thing when you design the game is to always design all the assets and screens of the game following the chosen aspect ratio.

Let me give you an example:

1024Ɨ576, 1280Ɨ720 (HD), 1366Ɨ768, 1920Ɨ1080 (Full HD), 2048Ɨ1152 are 16:9 it means that the result of width/height will be the same. Try yourself and check if I’m right.

So this is the first thing to consider, choose an aspect ratio and a resolution and maintain it to design your game. It will be your ā€œtargetā€ or ā€œreferenceā€ aspect ratio and resolution.

You can see which aspect ratio you are working on in the game tab in Unity.

image

🤟The 3 main areas for handling aspect ratios in Unity

Now to design good games, you should separate all the visual elements of your game in 2 categories

There are 2 main areas to consider:

  1. Game Elements.
  2. UI Elements.

And 1 that is not as important as the first 2, but I want to tell you about it:

  1. Backgrounds.

Aspect Ratios Unity:Ā Game Elements

image

Game elements are all the game objects we have in our scenes. To easily understand what I’m talking about consider all the objects with sprites except the ones in the Canvas object.

šŸŽ„Camera

The camera does all the hard work. If you try to change the different aspect ratio you will notice that unity keeps the height of the game, but not the width. It affects quite a lot of your game in different aspect ratios.

image

So it would be nice to have a script that will automatically scale the game for every different aspect ratio.

Lucky for you I have a special script to let you do this and see in real time what happens directly in the editor.

/* The MIT License (MIT)

Copyright (c) 2014, Marcel Căşvan

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */

using System;
using System.Collections;
using UnityEngine;

[ExecuteInEditMode]
[RequireComponent (typeof (Camera))]
public class CameraFit : MonoBehaviour
{
	#region FIELDS
	public float UnitsForWidth = 1; // width of your scene in unity units
	public static CameraFit Instance;
	
	private float _width;
	private float _height;
	//*** bottom screen
	private Vector3 _bl;
	private Vector3 _bc;
	private Vector3 _br;
	//*** middle screen
	private Vector3 _ml;
	private Vector3 _mc;
	private Vector3 _mr;
	//*** top screen
	private Vector3 _tl;
	private Vector3 _tc;
	private Vector3 _tr;
	#endregion
	
	#region PROPERTIES
	public float Width {
		get {
			return _width;
		}
	}
	public float Height {
		get {
			return _height;
		}
	}
	
	// helper points:
	public Vector3 BottomLeft {
		get {
			return _bl;
		}
	}
	public Vector3 BottomCenter {
		get {
			return _bc;
		}
	}
	public Vector3 BottomRight {
		get {
			return _br;
		}
	}
	public Vector3 MiddleLeft {
		get {
			return _ml;
		}
	}
	public Vector3 MiddleCenter {
		get {
			return _mc;
		}
	}
	public Vector3 MiddleRight {
		get {
			return _mr;
		}
	}
	public Vector3 TopLeft {
		get {
			return _tl;
		}
	}
	public Vector3 TopCenter {
		get {
			return _tc;
		}
	}
	public Vector3 TopRight {
		get {
			return _tr;
		}
	}
	#endregion
	
	#region METHODS
	private void Awake()
	{
		try{
			if((bool)GetComponent()){
				if (GetComponent().orthographic) {
					ComputeResolution();
				}
			}
		}catch (Exception e){
			Debug.LogException(e, this);
		}
	}
	
	private void ComputeResolution()
	{
        float deviceWidth;
		float deviceHeight;
		float leftX, rightX, topY, bottomY;
#if UNITY_EDITOR
        deviceWidth = GetGameView().x;
		deviceHeight = GetGameView().y;
#else
		deviceWidth = Screen.width;
		deviceHeight = Screen.height;
#endif
   
		/* Set the ortograpish size (shich is half of the vertical size) when we change the ortosize of the camera the item will be scaled 
		 * autoamtically to fit the size frame of the camera
		 */
		GetComponent().orthographicSize = 1f / GetComponent().aspect * UnitsForWidth / 2f;

		//Get the new height and Widht based on the new orthographicSize
		_height = 2f * GetComponent().orthographicSize;
		_width = _height * GetComponent().aspect;
		
		float cameraX, cameraY;
		cameraX = GetComponent().transform.position.x;
		cameraY = GetComponent().transform.position.y;
		
		leftX = cameraX - _width / 2;
		rightX = cameraX + _width / 2;
		topY = cameraY + _height / 2;
		bottomY = cameraY - _height / 2;
		
		//*** bottom
		_bl = new Vector3(leftX, bottomY, 0);
		_bc = new Vector3(cameraX, bottomY, 0);
		_br = new Vector3(rightX, bottomY, 0);
		//*** middle
		_ml = new Vector3(leftX, cameraY, 0);
		_mc = new Vector3(cameraX, cameraY, 0);
		_mr = new Vector3(rightX, cameraY, 0);
		//*** top
		_tl = new Vector3(leftX, topY, 0);
		_tc = new Vector3(cameraX, topY , 0);
		_tr = new Vector3(rightX, topY, 0);
		Instance = this;
	}
	
	private void Update()
	{
		#if UNITY_EDITOR
		ComputeResolution();
		#endif
	}
	
	private void OnDrawGizmos()
	{
		if (GetComponent().orthographic) {
			DrawGizmos();
		}
	}
	
	private void DrawGizmos()
	{
		//*** bottom
		Gizmos.DrawIcon(_bl, "point.png", false);
		Gizmos.DrawIcon(_bc, "point.png", false);
		Gizmos.DrawIcon(_br, "point.png", false);
		//*** middle
		Gizmos.DrawIcon(_ml, "point.png", false);
		Gizmos.DrawIcon(_mc, "point.png", false);
		Gizmos.DrawIcon(_mr, "point.png", false);
		//*** top
		Gizmos.DrawIcon(_tl, "point.png", false);
		Gizmos.DrawIcon(_tc, "point.png", false);
		Gizmos.DrawIcon(_tr, "point.png", false);
		
		Gizmos.color = Color.green;
		Gizmos.DrawLine(_bl, _br);
		Gizmos.DrawLine(_br, _tr);
		Gizmos.DrawLine(_tr, _tl);
		Gizmos.DrawLine(_tl, _bl);
	}
	
	private Vector2 GetGameView()
	{
		System.Type T = System.Type.GetType("UnityEditor.GameView,UnityEditor");
		System.Reflection.MethodInfo getSizeOfMainGameView =
			T.GetMethod("GetSizeOfMainGameView",System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
		System.Object resolution = getSizeOfMainGameView.Invoke(null, null);
		return (Vector2)resolution;
	}
	#endregion
}

In particular there is just a main parameter we can work with to adjust our game from the standard aspect ratio we chose before:

  • Pixels to Units

This is about how many pixels to units we would like to visualise.

Adjusting this parameter will affect the size of the rectangle of the Camera.

  • So the first thing is to check if we are in the target aspect ratio chosen before (1).
  • Then select the camera (2) and add this script to the camera (3).
  • Then with the Game tab opened, just change the parameter (4). The camera will start adjusting its size, we should set the parameter until we are satisfied with the size of the camera.
image

Once found the desired size we can switch in different resolutions and check what happens.

image

Everything is fitting in the right way for all the resolutions.

But you have a problem, as you can see, in some aspect ration, there is a blue margin at the top bottom of the game. We need to fix this problem, by anchoring some elements into the scene.

Anchors

Now we should solve another issue, the things are scaled by the width and look nice, but some time, some object are moved from the original position and this can create quite a problems.

To solve this we can use another script to anchor the object to the camera!

This is the script:

/***
 * This script will anchor a GameObject to a relative screen position.
 * This script is intended to be used with CameraFit.cs by Marcel Căşvan, available here: http://gamedev.stackexchange.com/a/89973/50623
 * 
 * Note: For performance reasons it's currently assumed that the game resolution will not change after the game starts.
 * You could not make this assumption by periodically calling UpdateAnchor() in the Update() function or a coroutine, but is left as an exercise to the reader.
 */
/* The MIT License (MIT)

Copyright (c) 2015, Eliot Lash

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
using UnityEngine;
using System.Collections;
using UnityEditor;

[ExecuteInEditMode]
public class CameraAnchor : MonoBehaviour
{
    public enum AnchorType
    {
        Undefined,
        BottomLeft,
        BottomCenter,
        BottomRight,
        MiddleLeft,
        MiddleCenter,
        MiddleRight,
        TopLeft,
        TopCenter,
        TopRight,
    };

    private Vector3 standardPosition;
    public AnchorType anchorType;
    public Vector3 anchorOffset;

    // Use this for initialization
    void Start()
    {
#if UNITY_EDITOR
        if (!EditorApplication.isPlaying && anchorType == AnchorType.Undefined)
        {
            standardPosition = transform.position;
        }
        else
        {
            Vector3 newPos = GetAnchorVector() + anchorOffset;
            standardPosition = transform.position - newPos;
        }

#endif
#if !UNITY_EDITOR
        Vector3 newPos = GetAnchorVector() + anchorOffset;
        standardPosition = transform.position - newPos;
#endif


        UpdateAnchor();
    }

    Vector3 GetAnchorVector()
    {
        Vector3 anchor = Vector3.zero;
        switch (anchorType)
        {
            case AnchorType.BottomLeft:
                anchor = CameraFit.Instance.BottomLeft;
                break;
            case AnchorType.BottomCenter:
                anchor = CameraFit.Instance.BottomCenter;
                break;
            case AnchorType.BottomRight:
                anchor = CameraFit.Instance.BottomRight;
                break;
            case AnchorType.MiddleLeft:
                anchor = CameraFit.Instance.MiddleLeft;
                break;
            case AnchorType.MiddleCenter:
                anchor = CameraFit.Instance.MiddleCenter;
                break;
            case AnchorType.MiddleRight:
                anchor = CameraFit.Instance.MiddleRight;
                break;
            case AnchorType.TopLeft:
                anchor = CameraFit.Instance.TopLeft;
                break;
            case AnchorType.TopCenter:
                anchor = CameraFit.Instance.TopCenter;
                break;
            case AnchorType.TopRight:
                anchor = CameraFit.Instance.TopRight;
                break;
            case AnchorType.Undefined:
                anchor = Vector3.zero;
                break;
        }
        return anchor;
    }

    void UpdateAnchor()
    {
        Vector3 anchorVector = GetAnchorVector();
        SetAnchor(anchorVector);
    }

    void SetAnchor(Vector3 anchor)
    {
        Vector3 newPos = anchor + anchorOffset;
        if (!transform.position.Equals(standardPosition + newPos))
        {
            transform.position = standardPosition + newPos;
        }
    }


    // Update is called once per frame
#if UNITY_EDITOR
    void Update()
    {
        if (!EditorApplication.isPlaying)
            UpdateAnchor();


    }
#endif
}

We can attach this script to all the objects we want to anchor somewhere to the camera.

  • Always in the target aspect ratio, just select all the game objects we want to anchor in a certain point of the camera (1).
  • Then attach the script Camera Anchor (2)
  • Set the anchor point of the camera (3), you will notice everything will move away from the point it was before.
  • Adjust with the new position through the parameter (4), the objects will move according to it.
  • Repeat this action to anchor all the objects to the different anchor points of the camera if needed.
  • image

Well now we can repeat again the test of changing aspect ratio:

image

Now the ground is always in the bottom, so we reached our goal, it just remains to take care of the background!

Aspect Ratios Unity: Backgrounds

image

I just want to spend a few words on backgrounds because usually, having a surplus of images is the best way typically for such background elements.

TheseĀ background elements, being bigger than the game area, could save us in different resolutions to see some nice images instead of black bars.

So the first thing to do is in our target aspect ratio check if the background we placed is bigger than the camera size, please keep in mind, the background should be always visible with different camera sizes.

Then we can use the anchor script to anchor it to the center of the camera.

  • Again select the background object in the scene (1)
  • Add the anchor script (2)
  • Set the anchor point as ā€œMiddle Centerā€ (3)
  • Adjust the position if necessary (4)
  • image

Now we are ready to check the final result.

image

I think we are pretty satisfied now. Our game is ready to handle different aspect ratios!

Aspect Ratios Unity:Ā UI

image

One last important area to discuss is the UI.

UI is the user interface and in Unity it includes all the elements we can add grouped under UI, like a button or a text for instance.

UI Anchors

Unity usesĀ CanvasĀ objects to design and add the UI elements in a game.

We are really lucky because it provides a similar system we used before, but visual to anchor all UI elements to the screen.

  • So we should just add a UI component (1)
  • Select the UI component (2)
  • Select the Anchor quadrant (3)
  • Decide where anchor our element (4)
  • Obviously we can edit the size, position of the element etc…
  • image

Once placed all the elements we can see what happens now:

image

The anchors are kept, but the UI looks a bit weird, because there is no resize of the elements.

Unity thought about it and gave us a special component to solve the problem:Ā Canvas Scaler.

Canvas Scaler

It should be already attached to the Canvas, if not you can easily add it by searching it through the components.

  • Select the Canvas (1)
  • Check the Canvas Scaler component in the right
  • Set the UI Scale Mode in ā€œScale With Screenā€ (2)
  • Do you remember the reference resolution you chose in the beginning with the aspect ratio? Set it here. (3)
  • Set the ā€œMatchā€ value to 0.5 (4)
image

Obviously you canĀ check the documentation or play with these parameters to find different strategies depending on what you want to do. I just showed you the common settings.

Let’s see in the end our result.

image

We definitely did it!

Conclusion

This was a really hard job! Maths, scripts, parameters a lot of stuff, but we worked it out together.

I know it’s a bit hard, but this is an important aspect to consider becauseĀ without handling different aspect ratios, it will be hard for you to release your game!

Let’s recap what we have to to do handle different aspect ratios in Unity:

  • Choose a resolution and its aspect ratio as a target to design our game.
  • Add the CameraFit script to the Camera to let game elements resize by width.
  • Add the Camera Anchor script to all the game objects we want to anchor to a point in the camera.
  • Check if the backgrounds are big enough to always fit the camera size
  • Set the anchor points of UI elements when we add to the Canvas
  • Set the Canvas Scaler component and adjust its parameters

This is it! I hope you found this article helpful, see you for the next challenge!