PR

【Unity】全てのスマートフォン、アスペクト比、解像度でも同じ画面を再現する方法

Unityで全解像度同じ画面を再現する Unity

実装内容

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

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

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

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

その追加したオブジェクトに「RectScalerWithViewport.cs」スクリプトを追加します。

 

スクリプト内容

処理的には「CanvasScaler」の「Scale With Screen Size」という設定と同じです。

referenceResolutionにUIを構築する解像度、matchWidthOrHeightはCanvasScalerの処理を引用し、同じ処理にしています。

RectScalerWithViewport.cs

using UnityEngine;

namespace TedLab
{
    public class RectScalerWithViewport : MonoBehaviour
    {
        private const float LogBase = 2;

        [SerializeField] private Camera refCamera = null;
        [SerializeField] private RectTransform refRect = null;
        [SerializeField] private Vector2 referenceResolution = new Vector2(1920, 1080);
        [Range(0, 1)] [SerializeField] private float matchWidthOrHeight = 0;

        private float _width = -1;
        private float _height = -1;
        private float _matchWidthOrHeight = 0f;

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

        private void Update()
        {
            UpdateRectWithCheck();
        }

        public void UpdateRectWithCheck()
        {
            if (refRect == null)
                return;
            
            var targetCamera = GetTargetCamera();
            if (targetCamera == null)
                return;

            var rect = targetCamera.rect;
            var width = rect.width * Screen.width;
            var height = rect.height * Screen.height;

            if (Mathf.Approximately(_width, width)
                && Mathf.Approximately(_height, height)
                && Mathf.Approximately(_matchWidthOrHeight, matchWidthOrHeight))
            {
                return;
            }

            UpdateRect();
        }

        private Camera GetTargetCamera()
        {
            // 設定があればそちらを優先
            return refCamera != null ? refCamera : Camera.main;
        }

        private void UpdateRect()
        {
            if (refRect == null)
                return;

            if (Mathf.Approximately(referenceResolution.x, 0f) || Mathf.Approximately(referenceResolution.y, 0f))
                return;

            var targetCamera = GetTargetCamera();
            if (targetCamera == null)
                return;

            var rect = targetCamera.rect;
            var width = rect.width * Screen.width;
            var height = rect.height * Screen.height;
            if (width == 0f || height == 0f)
            {
                return;
            }

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

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

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

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

            _width = width;
            _height = height;
            _matchWidthOrHeight = matchWidthOrHeight;
        }
    }
}

 

画面外が不正な表示になる対応 – 追記 2021/06/08

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

上記のCanvasBaseというのがCanvas機能をPrefab化しているものです。

そして赤線のFramePanelというのが「塗りつぶすためのパネル」で
それを上下左右(Left, Right, Up, Down)を基点にして4つ用意しているような感じです。

私の場合は「RootPanel」という箇所にUIなどを追加する形で
今回紹介した全解像度で同じ動作するようにしています。

Canvasは画像のようにPrefab化にしておくと
すぐにUIに適用できるのでおすすめです!

 

実装方法の解説動画を作りました! 2021/07/30

記事版はこちらです。

 

自動で画面が更新されてしまう問題の対応 – 追記 2023/08/07

エディタ上で保存ボタンを押すと勝手に設定しているCanvasが変更されることがったので

  • 更新ボタンの追加
  • メニューに全更新の操作を追加

に変更いたしました。

ほとんど変わってないのに毎回更新されて大変だったんですよね…

これからの画面更新方法

以前は自動で更新されてしまっていた画面サイズの更新処理は
これからはご自身でエディタ上の更新ボタンを押したり、メニュー操作でシーン上のオブジェクトを全更新してもらう必要があります。

 

・画面上部のメニュー「TedLab⇢ModifyResolution⇢シーンのオブジェクトを全て更新」

メニューからシーンのオブジェクトを全更新

・「Rect Scalar With Viewport 」の反映ボタン

rectview更新

・「Camera Stable Aspect 」の反映ボタン

camerastable更新

 

ただゲーム実行時にも自動でこの処理は必ず通るため
絶対に押す必要があるわけではなく、あくまで確認したいときに利用する想定です。

必要なときだけ更新するって感じになりました

ダウンロードとスクリプト更新について

あと本ページはダウンロードからunitypackageをインストールする形に変えましたので本記事の上部付近をご確認ください。

また以前こちらのスクリプトをご自身で追加された方はunitypackageをインストールして移行する必要があります。

その際はまずはunitypackageをインストールして、その追加されたスクリプトを追加して設定を同じものにしていただければ利用していただくことができます。

また以前ご自身で追加されたものは移行が完了した後に削除してくださいね。

unitypackageにしたので今後はスムーズに新しいバージョンに移行が出来るようになる想定です

 

活動内容のPR

下記が本サイトでの活動内容となります。

他の開発動画を見る

 

最後に

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

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

解像度の対応って本当に大変すぎ!

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

また本サイトではゲーム開発や個人的に知った有益なことを中心に発信しております。
もしご興味がある方がいましたら画面左下のベルマークから本サイトを購読していただけますと幸いです。

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

 

【ゲーム開発講座 / Unity】解像度調整プラグインの解説動画を作りました!
これまで本サイトでよく利用されているプラグイン「解像度を全デバイスで同じ画面を再現するスクリプト」の対応方法を動画にしてみました。 動画内で様々な失敗にも遭遇しているのでこれまでうまく動かせなかった人がいればぜひ参考にしていただけますと幸いです。
【Unity】複数解像度スクリーンショットを一括で撮影する拡張機能
アプリを開発していると申請用のスクリーンショットを用意するのが大変ですよね。 なので今回は複数解像度を連続撮影できる拡張機能をご紹介します。 手間だなぁと感じている方はぜひご利用いただければ幸いです。
Unity環境でAndroidアプリを最適化しよう!
Androidアプリの高速化・最適化について記事にしてみましたので動かしてみたけど処理がすごい重い、画面がカクカクって方はぜひ読んでみてくださいね。
【unityroom】ゲームジャム初参加!「鳥フライト」開発レポート
今回unityroomで開催されていた「Unity 1週間ゲームジャム」に参加しました。 そこで1週間でゲームを作った流れや感じたことを残しておこうかとレポート書いてみました。 他の人がどんな感じでゲーム作っているかなど気になる方の参考になれば幸いです。

コメント

  1. うなぎ より:

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

  2. Tomo より:

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

    • Tadapy 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などの設定は任意のものに変更しなければなりませんか?

    • Tadapy Tadashi より:

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

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

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

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

  5. 灯火 より:

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

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

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

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

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

    • Tadapy 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

    試してみます!

    • Tadapy Tadashi より:

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

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

  7. 灯火 より:

    何度もすみません。

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

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

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

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

    • Tadapy Tadashi より:

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

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

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

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

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

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

  8. 灯火 より:

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

  9. ゴルドメ より:

    2週間前にUnityを触り始めて、試行錯誤の末、ようやくウンコアプリが完成しました
    さっそくストアにリリースしてみようとしましたが
    スマホやタブレットではPCのようにキャンバスが表示されずに困っていました・・
    この神スクリプトを見つけた時は舞い上がりました!
    素晴らしいスクリプト!!
    もう感謝しかありませんッ!!!

    • Tadapy Tadapy より:

      おお、アプリのリリースに貢献できたようで幸いです!
      稀にうまくいかない方もいるようなのでうまく動作されたようでよかったです^_^

  10. Atsushi より:

    久しぶりにUnityで携帯アプリ作成に挑戦することになり
    この画面サイズ調整を使わせてもらうことにしました。

    実際に実装してみて、動作はするのですが、
    ビルド後に下のようなエラーが発生します。

    MissingReferenceException: The object of type ‘RectTransform’ has been destroyed but you are still trying to access it.
    Your script should either check if it is null or you should not destroy the object.

    エラー情報には
    RectScalerWithViewport.UpdateRect () (at Assets/PlugIn/RectScalerWithViewport.cs:87)
    RectScalerWithViewport.OnValidateImpl () (at Assets/PlugIn/RectScalerWithViewport.cs:47)
    とあるので、ここでエラーが発生しているようなのですが、解決法がわかりません。

    バージョンは2021.3.18f1ですが、皆さんも発生しているエラーなのでしょうか?

    • Tadapy Tadapy より:

      こちらおそらくDestroyされているオブジェクトに対して何かしらの処理をしていると思われます。
      RectScalerWithViewportのプロパティ「refRect」に設定しているオブジェクトが削除されているのではないでしょうか?

      本来「refRect」に設定するのはスクリプトを設定されているゲームオブジェクトのRectTransformや
      子ゲームオブジェクトのものが設定されることを想定されているため「refRect」だけ削除されることはない想定となりますので
      設定しているRectTransformが問題ないか一度確認していただけると幸いです。

  11. むっちゃん より:

    Unityを独学で勉強しており、エディタ上では問題無いのに、実機に移すと画面が崩れたり、変なところに飛んだり、問題だらけの状態に陥っていましたが、こちらの記事のおかげで、その問題も解決できました。
    そして何とかAppStoreで脱出ゲームをリリースすることができました。
    本当に感謝しております。
    ありがとうございました。

    • Tadapy Tadapy より:

      ご連絡ありがとうございます!本記事が参考になったようで幸いです。
      また何か不具合や要望などありましたらぜひご連絡いただければと思います。

スポンサーリンク
Ads Blocker Image Powered by Code Help Pro

広告ブロッカーが検出されました

広告をブロックする拡張機能を使用していることがわかりました。これらの広告ブロックを無効にして、私たちをサポートしてください。

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