Support multiple aspect ratio in Unity
Asked Answered
D

6

9

I've been trying to create a Unity 2D game that supports each and every aspect ratios of devices for both android and tablets. Is there a way to do so that's been provided or recommended by Unity?

Dramatics answered 6/10, 2014 at 6:18 Comment(0)
G
1

There are a few things that should be considered. The first is what elements should be allowed to scale? There are two categories, namely UI and Game Elements.

The Game Elements portion can mean a lot of things. If the game space is limited, the key is typically, including a generous portion of "negative space", or parts of the image that don't affect the game play significantly. For instance, the below image could be cropped from the left and right without affecting the image significantly. Put the center part of the image as the key element, or one side.

enter image description here

One could also stretch the elements, although that might lead to undesirable effects. Having a surplus of image and testing with different aspect rations is the best way typically for such background elements. These background elements can be placed in the background, with the canvas being set to "Scale With Screen Size", and setting the "Screen Match Mode" to the effect that works best for your image. See "Canvas Scaler" for more information.

As for the other UI elements, the key is to use anchor points. You can tell a UI element to take either a number of pixels, or fill a portion of the screen, when you place it. Look at the "Rect Transform" component included with each such UI object. You can adjust these on the screen as well.

Lastly, you could do it programmatically. There exists Screen.height and Screen.width. You could adjust the objects as desired in run time to make it work. I suggest you don't do this for everything, but it might help in some cases.

Gaffrigged answered 11/1, 2016 at 17:52 Comment(0)
R
0

In my case, I do work by create all of it as a scale

So, it could support no matter screen are

//Find Screen resolution at the splash or loading screen
    float scalex = DataFactory.SCREEN_WIDTH / (float)DataFactory.OUR_FIXED_GAME_SCREEN;
    float scaley = DataFactory.SCREEN_HEIGHT / (float)DataFactory.OUR_FIXED_GAME_SCREEN;
    if (scalex >= scaley)
        DataFactory.SCALE = scalex;
    else 
        DataFactory.SCALE = scaley;

//Set all size in game at the start
private int gameWidth = (int) (1400 * DataFactory.SCALE);
private int gameHeight = (int) (800 * DataFactory.SCALE);


private int startGameX = (int) (300 * DataFactory.SCALE);
private int startGameY = (int) (280 * DataFactory.SCALE);

private int objectX = (int) (410  * DataFactory.SCALE) + DataFactory.BEGIN_X;
private int objectY = (int) (979 * DataFactory.SCALE) + DataFactory.BEGIN_Y;

private int objectGapX = (int) (400 * DataFactory.SCALE);
private int objectGapY = (int) (180 * DataFactory.SCALE);
private int objectWidth = (int) (560 * DataFactory.SCALE);
private int objectHeight = (int) (400 * DataFactory.SCALE);


private int xRing = (int) (1005 * DataFactory.SCALE) +  DataFactory.BEGIN_X;
private int yRing = (int) (1020 * DataFactory.SCALE) + DataFactory.BEGIN_Y;
private int radiusOutside = (int) (740 * DataFactory.SCALE);
private int radiusInside = (int) (480 * DataFactory.SCALE);
private int radiusObject = (int) (600 * DataFactory.SCALE);
private int yObjectRing = (int) (920 * DataFactory.SCALE) + DataFactory.BEGIN_Y;

* ALL FIXED VALUED IS THE VALUED THAT I CREATE BY BASE ON SINGLE SCREEN *

This is some sample of 3D Game that I made, however, I still use the same concept at GUI part

enter image description here

This is some sample of 2D Game that I used this concept

enter image description here

enter image description here

Roncesvalles answered 6/10, 2014 at 11:3 Comment(2)
this is the 3D game. and my issue is not related to the scaling part its related to the aspect ratio that can not be solved by the just scaling the game objectDramatics
I also use this concept in 2D Game that I made... also with this scaling concept, no matter what screen sizes are, it will keep all the ratio to be the proper oneRoncesvalles
D
0

I know it’s an old post, wanted to show an alternative for this. You could try to define towards which axis you would like to scale your game (ex. all width should be always visible, height should scale respectively to the width): store all scene objects in a parent and scale the parent. Ex. (my width was fixed and the height got cut off for the width )

bottomRightPosition  = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, - Camera.main.transform.position.z));
topLeftPosition  = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, -Camera.main.transform.position.z));

float Width = topLeftPosition.x -bottomRightPosition.x
float scale = width / optimizedWorldDistance

gameHolder.transform.localScale = new Vector3(scale,scale,1); // for 2D

Note: my gameHolder is initially of scale (1,1,1);

Dissuasive answered 20/2, 2015 at 7:53 Comment(0)
W
0

You should put everything in a main game object and scale it with difference ratio using a simple script (camera 'something' could help you to detect the screen ratio). That's my idea. Sorry for my bad English.

Wincer answered 16/2, 2016 at 14:39 Comment(0)
F
0

Try the new unity3d ui with anchoring.

Fatten answered 14/3, 2016 at 9:6 Comment(0)
G
0

For all the UI elements you must use the unit UI system, which is the best way to support multi platforms and aspect ratios.

The following content is based on this article: This article say basically the same things that I'm saying: 1) Regarding design ONLY in high resolution the article said:

"Another approach is to use higher resolution graphics (in fact the one with the highest resolution of the device you want to target) and scale it down on all devices. However, this is not a good idea because you effectively need much more memory and will lose performance on low-end devices."

So design in high resolution and then scale down it's not the good approach.

So as the article said the best thing it's to have different images for different resolution (SD, HD UD) and load the right image when the game it's loading: the article said: "The best approach is to use a different image with the higher resolution and use this image version on the iPhone 4 and the low-res version on an iPhone 3GS, which is effectively what Apple is doing by using images with a @2x suffix for the file name.

In a similar way, you can create all your graphics in ultra-high-resolution needed for the iPad 3 for instance and append another suffix, and load the right image based on the screen resolution the device has. This is called content scaling, as the game was written only for a single "logical" scene size, and all the images & fonts are scaled to the device resolution."

So by using this approach we solved the problem of target devices with differnt RESOLUTIONS. No like the articole said there is another problem which is target the devices with different ASPECT RATIO: From the artichle: "However, this approach is not sufficient when you want to target devices with different aspect ratios"

To do that I usually choose an aspect ratio that can fit the design of my game and use the following script to maintain the same aspect ratio on different devices:

    /* 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<Camera>()){
                if (GetComponent<Camera>().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
        //Debug.Log("Aspect Ratio " + GetComponent<Camera>().aspect);
        if (GetComponent<Camera>().aspect >= 0.7f)
        {
            UnitsForWidth = 2.2f;
        } 
        else
        {
            UnitsForWidth = 2f;
        }
        /* 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<Camera>().orthographicSize = 1f / GetComponent<Camera>().aspect * UnitsForWidth / 2f;

        //Get the new height and Widht based on the new orthographicSize
        _height = 2f * GetComponent<Camera>().orthographicSize;
        _width = _height * GetComponent<Camera>().aspect;

        float cameraX, cameraY;
        cameraX = GetComponent<Camera>().transform.position.x;
        cameraY = GetComponent<Camera>().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<Camera>().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
}


  [1]: http://v-play.net/doc/vplay-different-screen-sizes/

This should sole the different aspect ratios problem. Now if you want anchor some game object the be always in a fixed position event if the game is resized on devices with different aspect ratios, you can use the following 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;

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

    // Use this for initialization
    void Start () {
        UpdateAnchor();
    }

    void UpdateAnchor() {
        switch(anchorType) {
        case AnchorType.BottomLeft:
            SetAnchor(CameraFit.Instance.BottomLeft);
            break;
        case AnchorType.BottomCenter:
            SetAnchor(CameraFit.Instance.BottomCenter);
            break;
        case AnchorType.BottomRight:
            SetAnchor(CameraFit.Instance.BottomRight);
            break;
        case AnchorType.MiddleLeft:
            SetAnchor(CameraFit.Instance.MiddleLeft);
            break;
        case AnchorType.MiddleCenter:
            SetAnchor(CameraFit.Instance.MiddleCenter);
            break;
        case AnchorType.MiddleRight:
            SetAnchor(CameraFit.Instance.MiddleRight);
            break;
        case AnchorType.TopLeft:
            SetAnchor(CameraFit.Instance.TopLeft);
            break;
        case AnchorType.TopCenter:
            SetAnchor(CameraFit.Instance.TopCenter);
            break;
        case AnchorType.TopRight:
            SetAnchor(CameraFit.Instance.TopRight);
            break;
        }
    }

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


    // Update is called once per frame
#if UNITY_EDITOR
    void Update () {
        UpdateAnchor();
    }
#endif
}

Hope this can help, for more info please read the article that I've linked above.

Group answered 27/4, 2016 at 8:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.