2023年9月22日 星期五

【筆記】- Unity 根據不同行動裝置解析度,自動依照 Camera 調整 GameObject Sprite 定位佈局(固定位置有效)

參考網友所發佈的文章「Make Your Game Look The Same All Mobile Screen Sizes - Unity」,實際在範例專案中修改並測試,可以耶...實作測試如下:

(1) 確定基本參考解析度(本例:iPhone 7 plue (1080x1920),且相關「固定位置」的 GameObject Sprite 物件都就定位後...



(2) 編輯 Camera Viewport Handler  (Script) 程式元件

在 Assets 中加入 C# 程式,並命名為 CameraViewportHandler.cs

程式內容如下:


using UnityEngine;


[ExecuteInEditMode]

[RequireComponent(typeof(Camera))]

public class CameraViewportHandler : MonoBehaviour

{

    public enum Constraint { Landscape, Portrait }


    #region FIELDS

    public Color wireColor = Color.white;

    public float UnitsSize = 1; // size of your scene in unity units

    public Constraint constraint = Constraint.Portrait;

    public static CameraViewportHandler Instance;

    public new Camera camera;


    public bool executeInUpdate;


    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()

    {

        camera = GetComponent<Camera>();

        Instance = this;

        ComputeResolution();

    }


    private void ComputeResolution()

    {

        float leftX, rightX, topY, bottomY;


        if (constraint == Constraint.Landscape)

        {

            camera.orthographicSize = 1f / camera.aspect * UnitsSize / 2f;

        }

        else

        {

            camera.orthographicSize = UnitsSize / 2f;

        }


        _height = 2f * camera.orthographicSize;

        _width = _height * camera.aspect;


        float cameraX, cameraY;

        cameraX = camera.transform.position.x;

        cameraY = 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);

    }


    private void Update()

    {

#if UNITY_EDITOR


        if (executeInUpdate)

            ComputeResolution();


#endif

    }


    void OnDrawGizmos()

    {

        Gizmos.color = wireColor;


        Matrix4x4 temp = Gizmos.matrix;

        Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);

        if (camera.orthographic)

        {

            float spread = camera.farClipPlane - camera.nearClipPlane;

            float center = (camera.farClipPlane + camera.nearClipPlane) * 0.5f;

            Gizmos.DrawWireCube(new Vector3(0, 0, center), new Vector3(camera.orthographicSize * 2 * camera.aspect, camera.orthographicSize * 2, spread));

        }

        else

        {

            Gizmos.DrawFrustum(Vector3.zero, camera.fieldOfView, camera.farClipPlane, camera.nearClipPlane, camera.aspect);

        }

        Gizmos.matrix = temp;

    }

    #endregion


} // class


(3) 編輯 Anchor Game Object  (Script) 程式元件

在 Assets 中加入 C# 程式,並命名為 AnchorGameObject.cs

程式內容如下:


using UnityEngine;

using System.Collections;


[ExecuteInEditMode]

public class AnchorGameObject : MonoBehaviour

{

    public enum AnchorType

    {

        BottomLeft,

        BottomCenter,

        BottomRight,

        MiddleLeft,

        MiddleCenter,

        MiddleRight,

        TopLeft,

        TopCenter,

        TopRight,

    };


    public bool executeInUpdate;


    public AnchorType anchorType;

    public Vector3 anchorOffset;


    IEnumerator updateAnchorRoutine; //Coroutine handle so we don't start it if it's already running


    // Use this for initialization

    void Start()

    {

        updateAnchorRoutine = UpdateAnchorAsync();

        StartCoroutine(updateAnchorRoutine);

    }


    /// <summary>

    /// Coroutine to update the anchor only once CameraFit.Instance is not null.

    /// </summary>

    IEnumerator UpdateAnchorAsync()

    {


        uint cameraWaitCycles = 0;


        while (CameraViewportHandler.Instance == null)

        {

            ++cameraWaitCycles;

            yield return new WaitForEndOfFrame();

        }


        if (cameraWaitCycles > 0)

        {

            print(string.Format("CameraAnchor found CameraFit instance after waiting {0} frame(s). " +

                "You might want to check that CameraFit has an earlie execution order.", cameraWaitCycles));

        }


        UpdateAnchor();

        updateAnchorRoutine = null;


    }


    void UpdateAnchor()

    {

        switch (anchorType)

        {

            case AnchorType.BottomLeft:

                SetAnchor(CameraViewportHandler.Instance.BottomLeft);

                break;

            case AnchorType.BottomCenter:

                SetAnchor(CameraViewportHandler.Instance.BottomCenter);

                break;

            case AnchorType.BottomRight:

                SetAnchor(CameraViewportHandler.Instance.BottomRight);

                break;

            case AnchorType.MiddleLeft:

                SetAnchor(CameraViewportHandler.Instance.MiddleLeft);

                break;

            case AnchorType.MiddleCenter:

                SetAnchor(CameraViewportHandler.Instance.MiddleCenter);

                break;

            case AnchorType.MiddleRight:

                SetAnchor(CameraViewportHandler.Instance.MiddleRight);

                break;

            case AnchorType.TopLeft:

                SetAnchor(CameraViewportHandler.Instance.TopLeft);

                break;

            case AnchorType.TopCenter:

                SetAnchor(CameraViewportHandler.Instance.TopCenter);

                break;

            case AnchorType.TopRight:

                SetAnchor(CameraViewportHandler.Instance.TopRight);

                break;

        }

    }


    void SetAnchor(Vector3 anchor)

    {

        Vector3 newPos = anchor + anchorOffset;

        if (!transform.position.Equals(newPos))

        {

            transform.position = newPos;

        }

    }


#if UNITY_EDITOR

    // Update is called once per frame

    void Update()

    {

        if (updateAnchorRoutine == null && executeInUpdate)

        {

            updateAnchorRoutine = UpdateAnchorAsync();

            StartCoroutine(updateAnchorRoutine);

        }

    }

#endif

}


(4) 在 Main camera 物件內加入 Camera Viewport Handler  (Script) 程式元件,並勾選「Execute In Update」以在 Unity 設計工具中「所見即得」,即時看到調整參數後的變化.然後,調整 Units Size 參數,讓 Sprite 元件全縮入Camera 的框框範圍.



(5) 在 WallGameGroup、WallLeft、WallRight、Ceiling、DeathLine 物件中加入 Anchor Game Object (Script) 程式元件,並勾選「Execute In Update」以在 Unity 設計工具中「所見即得」,即時看到調整參數後的變化.然後,調整個物件中 Anchor Game Object (Script) 元件內的 Anchor Type 與 Anchor Offset 參數,以符合並固定在 Camera 範圍中.

PS.注意 Wall 以 WallGameGroup 作為群組後的佈局方式,這是重點...







(6) 測試看看,改選擇 Redmin 10 (1080x2400)



沒有留言:

張貼留言