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?
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.
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.
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
This is some sample of 2D Game that I used this concept
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);
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.
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.
© 2022 - 2024 — McMap. All rights reserved.