2023年9月25日 星期一

【筆記】- Unity 之設定純 Canvas 的畫面格局,以因應不同手機螢幕縮放!

 (1) 1080 x 1920 的解析度為範例,將 Main Camera 中的 Camera -> Size 設定為 960 也就是最大邊 1920/2



(2) Canvas Render Mode 改為 Screen Space - Camera,並將 Main Camera 拖拉進 Render Camera 欄位,及設定 Canvas Scaler 中的 UI Scale Mode 為「Scale With Screen Size」,Reference Resolution X1080 Y 1920Screen Math Mode 為「Expand」!









2023年9月23日 星期六

【筆記】- Unity 之 Gyroscope & Acceleration (陀螺儀和重力加速器) 範例

在 Unity UI 中加入一個 Text 元件,命名為 GScensor,並在其中加入以下 GScensor.cs 程式元件.

程式中會置入 Gyroscope & Acceleration (陀螺儀和重力加速器) 數據偵測.

GScensor.cs


using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;


public class Gscensor : MonoBehaviour

{

    //public Ball ball;

    private bool isHasGyro = false;

    private Text infoMsg;

    // Use this for initialization

    void Start () {

        // 自動抓取 Text 元件(看 C# 程式掛在哪個 Component 下,就抓哪個)

        infoMsg = GetComponent<Text>();

        if(SystemInfo.supportsGyroscope){

            Input.gyro.enabled = true;

            isHasGyro = true;

            infoMsg.text = "Use Gyroscope";

        }

        else{

            infoMsg.text = "Use GSensor";

        }

    }

    

    // Update is called once per frame

    void Update () {

        float x1;

        float y1;

        float z1;

        float x2;

        float y2;

        float z2;


        x1=y1=z1=x2=y2=z2=0f;

        //Vector3 force;

        if(isHasGyro){ // 如果有「陀螺儀 (Gyroscope)

            x1 = Input.gyro.attitude.x*10.0F;

            y1 = Input.gyro.attitude.y*10.0F;

            z1 = Input.gyro.attitude.z*10.0F;


            x2 = Input.acceleration.x*10.0F;

            y2 = Input.acceleration.y*10.0F;

            z2 = Input.acceleration.z*10.0F;


            //infoMsg.text = "Use Gyroscope : " + x1 + " , " + y1 + " , " + z1;


            //force = new Vector3(-x * 10.0F, 0.0F, -y * 10.0F);

        }

        else{ // 不然就只使用「重力感測器 G-Sensor

            x2 = Input.acceleration.x*10.0F;

            y2 = Input.acceleration.y*10.0F;

            z2 = Input.acceleration.z*10.0F;

            

            //infoMsg.text = "Use Acceleration : " + x2 + " , " + y2 + " , " + z2;


            //force = new Vector3(x*10.0F , 0.0F, y*10.0F);

        }


        infoMsg.text = "Use Gyroscope : " + Mathf.Round(x1) + " , " + Mathf.Round(y1) + " , " + Mathf.Round(z1);

        infoMsg.text +="\n"; // 文字折行

        infoMsg.text += "Use Acceleration : " + Mathf.Round(x2) + " , " + Mathf.Round(y2) + " , " + Mathf.Round(z2);


        //ball.forward(force);

    }

}

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) WallGameGroupWallLeftWallRightCeilingDeathLine 物件中加入 Anchor Game Object (Script) 程式元件,並勾選「Execute In Update」以在 Unity 設計工具中「所見即得」,即時看到調整參數後的變化.然後,調整個物件中 Anchor Game Object (Script) 元件內的 Anchor Type Anchor Offset 參數,以符合並固定在 Camera 範圍中.

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







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