【Unity】全スマートフォン、全解像度で完全同じ画面を再現する

Unity
スポンサーリンク

Unityで開発するアプリの解像度が変わる時、カメラやキャンバス内の配置が若干変わってしまい思った通りに配置されないことがあったので今回その対策をいれてみました。

見た目の挙動

画面のサイズを変更するとこのように変化します。
キチッとゲームの比率やUIのサイズや位置が変わらず維持できていますね。

この対応を入れておかないと、ゲームは見えてはいけないカメラ外のモノが見えてしまったり、端末毎に操作感が変わってしまう可能性があるので注意です。

 

参考サイト

こちらのサイトを参考しました。
https://pengoya.net/unity/aspect/

ただこれだけだとUIの位置やサイズが維持できないためプラスアルファその辺りも対応しました。

 

実装内容

おおまかな流れとしてはスクリプト2種類用意して
各オブジェクトに設定していく流れとなります。

カメラ(Camera)

カメラに対して、下記のスクリプトをアタッチします。
詳細な説明は省きますが、おおまかに説明すると

変更されたアスペクトのどちらが縦と横どちらが大きいか判別して
カメラの描画領域を調整しています。

これをゲームで使っているカメラに設定追加してください。

 

スクリプト内容

refCameraに使用するカメラコンポーネント、widthに基本解像度の幅、heightに基本解像度の高さ、pixelPerUnitについてもCanvasなどと統一した値を利用してください。

CameraStableAspect.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode()]
[RequireComponent(typeof(Camera))]
public class CameraStableAspect : MonoBehaviour
{
    [SerializeField]
    Camera refCamera;

    [SerializeField]
    int width = 1920;

    [SerializeField]
    int height = 1080;

    [SerializeField]
    float pixelPerUnit = 100f;


    int m_width = -1;
    int m_height = -1;

    void Awake()
    {
        if( refCamera == null ){
            refCamera = GetComponent();
        }
        UpdateCamera();
    }

    private void Update()
    {
        UpdateCameraWithCheck();
    }

    void UpdateCameraWithCheck()
    {
        if(m_width == Screen.width && m_height == Screen.height){
            return;
        }
        UpdateCamera();
    }

    void UpdateCamera()
    {
        float screen_w = (float)Screen.width;
        float screen_h = (float)Screen.height;
        float target_w = (float)width;
        float target_h = (float)height;

        //アスペクト比
        float aspect =  screen_w / screen_h;        
        float targetAcpect = target_w / target_h;
        float orthographicSize = (target_h / 2f / pixelPerUnit);

        //縦に長い
        if (aspect < targetAcpect)
        {
            float bgScale_w = target_w / screen_w;
            float camHeight = target_h / (screen_h * bgScale_w);
            refCamera.rect = new Rect( 0f, (1f-camHeight)*0.5f, 1f, camHeight);
        }
        // 横に長い
        else
        {
            // カメラのorthographicSizeを横の長さに合わせて設定しなおす
            float bgScale = aspect / targetAcpect;
            orthographicSize *= bgScale;

            float bgScale_h = target_h / screen_h;
            float camWidth = target_w / (screen_w * bgScale_h);
            refCamera.rect = new Rect( (1f-camWidth)*0.5f, 0f, camWidth, 1f);
        }

        refCamera.orthographicSize = orthographicSize;

        m_width = Screen.width;
        m_height = Screen.height;
    }
}

 

キャンバス(Canvas)直下のRectTransformオブジェクト

一般的な構造としてはCanvasの下にUIを配置していくと思うのですが、そこに全てのUIを子としてぶら下げる親を事前に追加しておきます。

少し手間なのですが、CanvasはScreenサイズ(解像度)に強制で連動してしまうためそのスクリプトで制御できるRectTransformの親を一つ追加する必要があります。

追加方法としては「Hierarchy→右クリック→UI→Panel」で追加し、不要なImageなどを削除、位置をセンタリング」でご利用ください。

その追加したオブジェクトに下記スクリプトを追加します。

 

スクリプト内容

処理的には「CanvasScaler」の「Scale With Screen Size」という設定と同じです。
referenceResolutionにUIを構築する解像度、matchWidthOrHeightはCanvasScalerの処理を引用し、同じ処理にしています。

RectScalerWithViewport.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode()]
public class RectScalerWithViewport : MonoBehaviour
{
    [SerializeField]
    RectTransform refRect = null;

    [SerializeField]
    Vector2 referenceResolution = new Vector2(1920, 1080);

    [Range(0, 1)]
    [SerializeField] float matchWidthOrHeight = 0;

    float m_width = -1;
    float m_height = -1;

    private const float kLogBase = 2;

    private void Awake()
    {
        if(refRect == null){
            refRect = GetComponent();
        }
        UpdateRect();
    }

    private void Update()
    {
        UpdateRectWithCheck();
    }

    private void OnValidate()
    {
        UpdateRect();
    }

    void UpdateRectWithCheck()
    {
        Camera cam = Camera.main;
        float width = cam.rect.width * Screen.width;
        float height = cam.rect.height * Screen.height;
        if(m_width == width && m_height == height){
            return;
        }
        UpdateRect();
    }

    void UpdateRect()
    {
        Camera cam = Camera.main;
        float width = cam.rect.width * Screen.width;
        float height = cam.rect.height * Screen.height;
        m_width = width;
        m_height = height;

        // canvas scalerから引用
        float logWidth = Mathf.Log(width / referenceResolution.x, kLogBase);
        float logHeight = Mathf.Log(height / referenceResolution.y, kLogBase);
        float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, matchWidthOrHeight);
        float scale = Mathf.Pow(kLogBase, logWeightedAverage);

        refRect.localScale = new Vector3(scale, scale, scale);

        // スケールで縮まるので領域だけ広げる
        float revScale = 1f / scale;
        refRect.sizeDelta = new Vector2(m_width * revScale, m_height * revScale);
   }
}

 

開発中ゲームのPR

こちらの機能を開発するときに利用したゲームです。

最後に

いかがでしたでしょうか。
この機能で一応は縦横比を維持したまま、ゲームの画面がスマートフォン端末ごとに変わってしまうことが避けられました。

ただ画面引き伸ばされていない場所は見えては行けないモノが見えるので
キャンバスの縁辺りは何か塗りつぶす、パネルなどを置いて隠す必要があるので必要であれば対応を入れてみてください。

なにはともわれ端末画面ごとにレイアウトの配置が変わる負担が減ったので今後この機能はずっと使うことになりそうです。

解像度対応面倒くさい!!

という方はぜひご活用いただければ幸いです。

ではここまでこの記事をお読み頂きありがとうございました。

 

コメント

タイトルとURLをコピーしました