在 Unity 中支持多种纵横比

Posted

技术标签:

【中文标题】在 Unity 中支持多种纵横比【英文标题】:Support multiple aspect ratio in Unity 【发布时间】:2014-10-06 06:18:29 【问题描述】:

我一直在尝试创建一个 Unity 2D 游戏,它支持 android 和平板电脑的各种设备纵横比。有没有 Unity 提供或推荐的方法?

【问题讨论】:

【参考方案1】:

有几件事情需要考虑。首先是应该允许哪些元素缩放?有两个类别,即 UI 和 Game Elements。

游戏元素部分可能意味着很多事情。如果游戏空间有限,关键通常是,包括大量的“负空间”,或者不会显着影响游戏玩法的部分图像。例如,可以从左右裁剪下面的图像,而不会显着影响图像。将图像的中心部分作为关键元素,或一侧。

也可以拉伸元素,尽管这可能会导致不良影响。拥有多余的图像并使用不同的宽高比进行测试通常是此类背景元素的最佳方式。这些背景元素可以放置在背景中,画布设置为“随屏幕大小缩放”,并将“屏幕匹配模式”设置为最适合您的图像的效果。有关详细信息,请参阅“Canvas Scaler”。

至于其他 UI 元素,关键是使用锚点。您可以在放置 UI 元素时告诉它使用多个像素或填充屏幕的一部分。查看每个此类 UI 对象包含的“矩形变换”组件。您也可以在屏幕上调整这些。

最后,您可以通过编程方式完成。存在Screen.heightScreen.width。您可以在运行时根据需要调整对象以使其工作。我建议您不要对所有事情都这样做,但在某些情况下它可能会有所帮助。

【讨论】:

【参考方案2】:

就我而言,我通过将所有这些都创建为比例来工作

所以,不管是什么屏幕都可以支持

//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 是我基于单屏创建的值 *

这是我制作的一些 3D 游戏示例,但我在 GUI 部分仍然使用相同的概念

这是我使用这个概念的一些 2D 游戏示例

【讨论】:

这是 3D 游戏。我的问题与缩放部分无关,它与仅缩放游戏对象无法解决的纵横比有关 我在自己做的2D游戏中也用到了这个概念……也用这个缩放概念,不管屏幕大小,它都会保持所有比例都是合适的【参考方案3】:

我知道这是一个旧帖子,想展示一个替代方案。您可以尝试定义您希望将游戏缩放到哪个轴(例如,所有宽度应始终可见,高度应分别缩放到宽度):将所有场景对象存储在父级中并缩放父级。 前任。 (我的宽度是固定的,高度被剪掉了)

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

注意:我的 gameHolder 最初是 (1,1,1);

【讨论】:

【参考方案4】:

您应该将所有内容放在一个主游戏对象中,并使用一个简单的脚本以不同的比例对其进行缩放(相机“某些东西”可以帮助您检测屏幕比例)。 这就是我的想法。 对不起我的英语不好。

【讨论】:

【参考方案5】:

尝试使用锚定的新 unity3d 用户界面。

【讨论】:

【参考方案6】:

对于所有的 UI 元素,您必须使用单元 UI 系统,这是支持多平台和纵横比的最佳方式。

以下内容基于this文章: 这篇文章说的基本上和我说的一样: 1) 关于仅高分辨率的设计,文章说:

“另一种方法是使用更高分辨率的图形(实际上 一个具有您要定位的设备的最高分辨率)和 在所有设备上缩小它。但是,这不是一个好主意,因为 你实际上需要更多的内存,并且会失去性能 低端设备。”

因此,以高分辨率设计然后按比例缩小这不是好方法。

因此,正如文章所说,最好的办法是为不同的分辨率(SD、HD UD)提供不同的图像,并在游戏加载时加载正确的图像:文章说: “最好的方法是使用具有更高分辨率的不同图像,并在 iPhone 4 上使用此图像版本,在 iPhone 3GS 上使用低分辨率版本,这实际上是 Apple 通过使用带有@2x 后缀的图像来实现的文件名。

以类似的方式,您可以创建 iPad 3 所需的所有超高分辨率图形并附加另一个后缀,然后根据设备的屏幕分辨率加载正确的图像。这称为内容缩放,因为游戏仅针对单个“逻辑”场景大小编写,并且所有图像和字体都缩放到设备分辨率。”

因此,通过使用这种方法,我们解决了具有不同分辨率的目标设备的问题。 不像文章所说,还有另一个问题是针对具有不同纵横比的设备: 从文章: “但是,当您想要定位具有不同纵横比的设备时,这种方法是不够的”

为此,我通常会选择适合我的游戏设计的纵横比,并使用以下脚本在不同设备上保持相同的纵横比:

    /* 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 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

希望这能有所帮助,有关更多信息,请阅读我上面链接的文章。

【讨论】:

以上是关于在 Unity 中支持多种纵横比的主要内容,如果未能解决你的问题,请参考以下文章

ImageView 裁剪顶部和底部并固定纵横比

确保相机预览大小/纵横比与生成的视频相匹配

FFmpeg - 使用纵横比更改视频的分辨率

如何在没有纵横比的情况下在 SVG 中保持图像的纵横比

自动布局程序化纵横比设置

纵横比约束没有改变