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

Unity
スポンサーリンク

実装内容

キャンバス(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;
    float m_matchWidthOrHeight = 0f;

    private const float kLogBase = 2;

    private void Awake()
    {
        if(refRect == null){
            refRect = GetComponent<RectTransform>();
        }
        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 && m_matchWidthOrHeight == matchWidthOrHeight ){
            return;
        }
        UpdateRect();
    }

    void UpdateRect()
    {
        if( referenceResolution.x == 0f || referenceResolution.y == 0f){
            return;
        }
        Camera cam = Camera.main;
        if( cam == null ){
            return;
        }
        float width = cam.rect.width * Screen.width;
        float height = cam.rect.height * Screen.height;
        if( width == 0f || height == 0f ){
            return;
        }

        // 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);

        if( float.IsNaN(scale) || scale <= 0f ){
            return;
        }

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

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

        m_width = width;
        m_height = height;
        m_matchWidthOrHeight = matchWidthOrHeight;
   }
}

 

参考サイト

今回こちらのサイトを参考しました。

Unity2Dで画面のアスペクト比を固定にしたい【Unity】 - Qiita
2Dゲームを作っている時に色んなデバイスで画面サイズを背景に合わせたい時があったのでメモ。 背景にサイズを合わせたい アスペクト比は固定にしたい 背景からはみ出す部分は黒くしたい デバイスのスクリーンサイズから縦に合わせるか横に合...

ただこれだけだとCanvasScalarなどを使ってもいつも同じUI位置やサイズが維持できないためプラスアルファその辺りも対応しています。

 

開発中ゲームのPR

こちらの機能を利用したゲームです。

予約サイトにも登録していますのでご興味のある方はぜひ!

iOS :yoyaku-top10.jp/u/a/MzAwMTA
Android :yoyaku-top10.jp/u/a/MzAwMDE

最後に

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

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

必要であれば別途対応を入れてみてください。
※簡単にいうと画面上下左右の端にピボットを置きます。そして塗りつぶし用イメージの位置を連動させて対応。

 

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

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

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

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

 

コメント

  1. うなぎ より:

    神スクリプトをありがとうございます!
    とっっっても助かりました!

  2. Tomo より:

    とても助かります!
    ありがとうございました!
    RectScalerWithViewport.csをアタッチする場所がよく分からなかったのですが、キャンバス内に空のゲームオブジェクトを作成してそこにアタッチすればいいんですか?

    • Tadashi Tadashi より:

      助けになったようで良かったです!
      下記回答となります。

      はい、RectScalerWithViewport.csはゲーム上でお使いのCanvasを親として
      その一つ下の階層にゲームオブジェクト(記事で説明しているPanel)を追加し、そこにRectScalerWithViewportスクリプトをアタッチします。

      その追加したゲームオブジェクトはRectTransformと呼ばれるCanvasの子どもになるためのオブジェクトとして作成されますので、そのRectTransformプロパティはCanvasのサイズに連動するように中心を原点とした完全フィット設定にしてください(上下左右のサイズは完全連動、原点を中心にする設定です)。

      そしてPanelで追加すると不要なImageコンポーネントが付いてきますのでこれを削除してください。

      最後にこれまでCanvas直下にあったUIなどのゲームオブジェクトをその追加したPanel(改名推奨、例:ScalerPanelなど)に全てドラッグ&ドロップで移動させます。

      少々初歩的な部分などを端折っている点もありますので
      気になることなどありましたらお気軽にご連絡ください~!

      追伸:ゲーム上でCanvasが複数ある場合、毎回対応入れないといけないのでPrefabしておくと便利です。

  3. にゃんぱす より:

    コメント失礼します。
    とてもいいスクリプトを提供していただきありがとうございます。自分のゲームでも採用してみたいと思います。

    質問なのですが、キャンバスやパネル側の設定はデフォルトの設定のままでしょうか?
    それともCanvas Scaler や Render Mode などの設定も任意のものに変更する必要がありますか?

  4. にゃんぱす より:

    とても良いスクリプトを提供していただきありがとうございます。自分のゲームでもこの機能を実装してみようと思います。

    質問なのですが、CanvasやCamera側の設定で変更しなければならない箇所はありますでしょうか。Canvas Scaler や Render Modeなどの設定は任意のものに変更しなければなりませんか?

    • Tadashi Tadashi より:

      コメントありがとうございます!

      記事に書けておらず申し訳ないのですが、CanvasScalarは利用しない前提の実装となっています。
      なので利用する場合はコンポーネントを削除してご利用してみてください。

      またよくミスをしそうなのは利用しているカメラに「MainCamera」タグを設定していない場合に必要なカメラがスクリプト上で参照できないため
      正常に動かないケースもありそうです。

      Render Modeについては特に意識していなかったのですが、私の環境のCanvasでは「Screen Space – Overlay」となっています。
      こちらも設定して検証してみていただけると幸いです。

  5. 灯火 より:

    このスクリプトに凄く助けられました!unity初心者の者です
    ありがとうございます。

    android端末で試すと、画面サイズは適用するのですが、
    左右の画面外に映る物を消すことが出来ず、困っています。

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

    と書いて頂いている部分になると思うのですが、Panelを置いてみても、消すことが出来ませんでした。

    ご回答頂けると幸いです。

    • Tadashi Tadashi より:

      本記事が参考になったようで幸いです!

      下記回答となります。
      見えては行けない部分はUIのImageコンポーネントでその部分を上塗りするような形で配置する必要があります。

      方法としては端末によっては上下左右どちらに余白が来るかわからないため
      伸び縮みした場合に備えて、上下左右を覆うために4つのImageパネルを用意します。

      そしてあくまで私の例となりますが

      ・パネルはCanvas直下に配置し、Canvas内で一番最後に描画されるようにHierarchy上で一番最後に配置します(Canvas上の描画はHierarchy上で上から順に描画されます)
      ・左右2つのパネルはRectTransformで上下のストレッチ(左は左寄せ、右は右寄せ)にし、Widthを1000にしておきます
      ・上下2つのパネルも同様にRectTransformで左右のストレッチ(上は上寄せ、下は下寄せ)にしてHeightを1000にしておきます
      ・4つのImageコンポーネントの「SourceImage」はNoneにし、色も自分が塗りつぶしたい色に設定しておきます

      上記の方法で私のアプリは実装しています。
      参考になれば幸いです。

  6. 灯火 より:

    お忙しい所、本当にありがとうございますm(_ _)m

    試してみます!

    • Tadashi Tadashi より:

      いえいえ!また気になる点がありましたらコメントください

      ちなみにさきほどのコメントで書き忘れていたのですが
      「上下右左の寄せ」設定についてはRectTransformのpivotで寄せる部分に合わせて変更してあげる必要ありますのでご注意ください~

  7. 匿名 より:

    何度もすみません。
    現在、ブロック崩しをandroidアプリとして作っています。
    こちらのスクリプトで、画面サイズがしっかりと端末に反映されているのですが、

    上下は正常です。

    左右の両端に、切れ目が有り、切れ目の外側に
    鏡の様にブロック崩しのブロック等、画面内のオブジェクトが写っています。

    先日回答して頂いた、Imageパネルを用意し、pivotで位置を調節等、試してみたのですが、うまくいかず

    何度も申し訳ないです。改善策有りますでしょうか。

  8. 灯火 より:

    何度もすみません。

    現在ブロック崩しのandroidアプリを作っています。
    こちらのスクリプトを使わせていただき、画面が端末毎に反映されるようになりました!ですが、

    上下のサイズは正常なのですが、
    左右の両端に切れ目があり、切れ目の外側に、鏡の様にゲーム内のブロックオブジェクト等が映っています。

    先日回答して頂いた、canvas直下へPanel作り、pivotで調節等試してみたのですが、
    解決できず、コメントさせていただきました。

    何度も申し訳ないです。
    解決策ありますでしょうか。

    • Tadashi Tadashi より:

      いえいえ、もしかしたらお互いなにか勘違いしてるのかもしれませんね・・・

      こちらのゲームは縦画面のものとなりますでしょうか?
      また気になるのは「Canvas直下」という点なのですが、塗りつぶしのImageを一番最後に描画するためにはCanvasの最後に配置する必要があります。

      この画像は私のPrefabの構成です

      ・RootPanelの下にUIなどを配置
      ・FramePanelの下に上下左右のパネルを配置

      という構成になっています。
      Prefabの階層表示で上にあるものほど優先されて表示されるので
      ゲームUIなどを表示したあとにImageパネルで上書きするためにFramePanelという階層はゲームUI(RootPanel)よりも下側に配置しています。

      こちらの画像を参考に今一度PrefabやCanvasを確認していただけると幸いです。

  9. 灯火 より:

    ありがとうございます!
    回答して頂いた画像どうりにすると出来ました。
    お時間かけてしまい、すみませんでした。

スポンサーリンク
タイトルとURLをコピーしました