Unityで開発するアプリの解像度が変わる時、カメラやキャンバス内の配置が若干変わってしまい思った通りに配置されないことがあったので今回その対策をいれてみました。
今回簡単に解像度対応をできるように記事を書いたのでお困りの方の参考になれば幸いです。
更新履歴
- 2020/03/24
スクリプトをダウンロードできるように修正、一部コードの表示の不具合を修正 - 2020/09/22
カメラのnull参照を修正、Nanになるケースを修正 - 2021/06/21
塗りつぶしについて記述追加
- 2021/07/30
解説動画を追加 - 2022/10/08
Warningが出力される問題を修正
見た目の挙動
画面のサイズを変更するとこのように変化します。
キチッとゲームの画面比率やUIの位置・サイズが変わらず維持できていますね。
この対応を入れておかないと、幅が長いスマホなどで本来カメラ外の見えてはいけないモノが見えてしまったり、端末毎に操作感が変わってしまうので調整に大変苦労します。
もちろん一部画面外が黒く塗りつぶされていて画面を活かしきれてない感はあるのですが、そもそもスマホによっては見えてはいけないもの(例えばイベントが起きる前でキャラが消えてるとか)が見えてるのは論外です。
アスペクト比が違う画面などでも調整不要なこの機能を入れて楽をしてしまいましょう!
ダウンロード
ダウンロードはこちら
Unity解像度調整スクリプト
実装内容
おおまかな流れとしてはスクリプト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< Camera >(); } 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 * z); 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側の対応をしていきます。
コメント
神スクリプトをありがとうございます!
とっっっても助かりました!
お喜びいただけたようで幸いです!
とても助かります!
ありがとうございました!
RectScalerWithViewport.csをアタッチする場所がよく分からなかったのですが、キャンバス内に空のゲームオブジェクトを作成してそこにアタッチすればいいんですか?
助けになったようで良かったです!
下記回答となります。
はい、RectScalerWithViewport.csはゲーム上でお使いのCanvasを親として
その一つ下の階層にゲームオブジェクト(記事で説明しているPanel)を追加し、そこにRectScalerWithViewportスクリプトをアタッチします。
その追加したゲームオブジェクトはRectTransformと呼ばれるCanvasの子どもになるためのオブジェクトとして作成されますので、そのRectTransformプロパティはCanvasのサイズに連動するように中心を原点とした完全フィット設定にしてください(上下左右のサイズは完全連動、原点を中心にする設定です)。
そしてPanelで追加すると不要なImageコンポーネントが付いてきますのでこれを削除してください。
最後にこれまでCanvas直下にあったUIなどのゲームオブジェクトをその追加したPanel(改名推奨、例:ScalerPanelなど)に全てドラッグ&ドロップで移動させます。
少々初歩的な部分などを端折っている点もありますので
気になることなどありましたらお気軽にご連絡ください~!
追伸:ゲーム上でCanvasが複数ある場合、毎回対応入れないといけないのでPrefabしておくと便利です。
コメント失礼します。
とてもいいスクリプトを提供していただきありがとうございます。自分のゲームでも採用してみたいと思います。
質問なのですが、キャンバスやパネル側の設定はデフォルトの設定のままでしょうか?
それともCanvas Scaler や Render Mode などの設定も任意のものに変更する必要がありますか?
とても良いスクリプトを提供していただきありがとうございます。自分のゲームでもこの機能を実装してみようと思います。
質問なのですが、CanvasやCamera側の設定で変更しなければならない箇所はありますでしょうか。Canvas Scaler や Render Modeなどの設定は任意のものに変更しなければなりませんか?
コメントありがとうございます!
記事に書けておらず申し訳ないのですが、CanvasScalarは利用しない前提の実装となっています。
なので利用する場合はコンポーネントを削除してご利用してみてください。
またよくミスをしそうなのは利用しているカメラに「MainCamera」タグを設定していない場合に必要なカメラがスクリプト上で参照できないため
正常に動かないケースもありそうです。
Render Modeについては特に意識していなかったのですが、私の環境のCanvasでは「Screen Space – Overlay」となっています。
こちらも設定して検証してみていただけると幸いです。
早急な返答ありがとうございます!
無事実装することができました!
ご返事ありがとうございます!
無事に動いたようで良かったです〜(^_^)
このスクリプトに凄く助けられました!unity初心者の者です
ありがとうございます。
android端末で試すと、画面サイズは適用するのですが、
左右の画面外に映る物を消すことが出来ず、困っています。
『ただ画面引き伸ばされていない場所は見えては行けないモノが見えるのでキャンバスの縁辺りは何か塗りつぶす、パネルなどを置いて隠す必要があります。』
と書いて頂いている部分になると思うのですが、Panelを置いてみても、消すことが出来ませんでした。
ご回答頂けると幸いです。
本記事が参考になったようで幸いです!
下記回答となります。
見えては行けない部分はUIのImageコンポーネントでその部分を上塗りするような形で配置する必要があります。
方法としては端末によっては上下左右どちらに余白が来るかわからないため
伸び縮みした場合に備えて、上下左右を覆うために4つのImageパネルを用意します。
そしてあくまで私の例となりますが
・パネルはCanvas直下に配置し、Canvas内で一番最後に描画されるようにHierarchy上で一番最後に配置します(Canvas上の描画はHierarchy上で上から順に描画されます)
・左右2つのパネルはRectTransformで上下のストレッチ(左は左寄せ、右は右寄せ)にし、Widthを1000にしておきます
・上下2つのパネルも同様にRectTransformで左右のストレッチ(上は上寄せ、下は下寄せ)にしてHeightを1000にしておきます
・4つのImageコンポーネントの「SourceImage」はNoneにし、色も自分が塗りつぶしたい色に設定しておきます
上記の方法で私のアプリは実装しています。
参考になれば幸いです。
お忙しい所、本当にありがとうございますm(_ _)m
試してみます!
いえいえ!また気になる点がありましたらコメントください
ちなみにさきほどのコメントで書き忘れていたのですが
「上下右左の寄せ」設定についてはRectTransformのpivotで寄せる部分に合わせて変更してあげる必要ありますのでご注意ください~
何度もすみません。
現在ブロック崩しのandroidアプリを作っています。
こちらのスクリプトを使わせていただき、画面が端末毎に反映されるようになりました!ですが、
上下のサイズは正常なのですが、
左右の両端に切れ目があり、切れ目の外側に、鏡の様にゲーム内のブロックオブジェクト等が映っています。
先日回答して頂いた、canvas直下へPanel作り、pivotで調節等試してみたのですが、
解決できず、コメントさせていただきました。
何度も申し訳ないです。
解決策ありますでしょうか。
いえいえ、もしかしたらお互いなにか勘違いしてるのかもしれませんね・・・
こちらのゲームは縦画面のものとなりますでしょうか?
また気になるのは「Canvas直下」という点なのですが、塗りつぶしのImageを一番最後に描画するためにはCanvasの最後に配置する必要があります。
この画像は私のPrefabの構成です
・RootPanelの下にUIなどを配置
・FramePanelの下に上下左右のパネルを配置
という構成になっています。
Prefabの階層表示で上にあるものほど優先されて表示されるので
ゲームUIなどを表示したあとにImageパネルで上書きするためにFramePanelという階層はゲームUI(RootPanel)よりも下側に配置しています。
こちらの画像を参考に今一度PrefabやCanvasを確認していただけると幸いです。
ありがとうございます!
回答して頂いた画像どうりにすると出来ました。
お時間かけてしまい、すみませんでした。
おお、良かったです!
また何かありましたら連絡ください~
2週間前にUnityを触り始めて、試行錯誤の末、ようやくウンコアプリが完成しました
さっそくストアにリリースしてみようとしましたが
スマホやタブレットではPCのようにキャンバスが表示されずに困っていました・・
この神スクリプトを見つけた時は舞い上がりました!
素晴らしいスクリプト!!
もう感謝しかありませんッ!!!
おお、アプリのリリースに貢献できたようで幸いです!
稀にうまくいかない方もいるようなのでうまく動作されたようでよかったです^_^
久しぶりに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ですが、皆さんも発生しているエラーなのでしょうか?
こちらおそらくDestroyされているオブジェクトに対して何かしらの処理をしていると思われます。
RectScalerWithViewportのプロパティ「refRect」に設定しているオブジェクトが削除されているのではないでしょうか?
本来「refRect」に設定するのはスクリプトを設定されているゲームオブジェクトのRectTransformや
子ゲームオブジェクトのものが設定されることを想定されているため「refRect」だけ削除されることはない想定となりますので
設定しているRectTransformが問題ないか一度確認していただけると幸いです。