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

Unity
スポンサーリンク

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

更新履歴

  • 2020/09/22
    スクリプトをダウンロードできるように修正、一部コードの表示の不具合を修正
  • 2020/09/22
    カメラのnull参照を修正、Nanになるケースを修正

 

見た目の挙動

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

この対応を入れておかないと、幅が長いスマホなどで本来カメラ外の見えてはいけないモノが見えてしまったり、端末毎に操作感が変わってしまうので調整に大変苦労します。

もちろん一部画面外が黒く塗りつぶされていて画面を活かしきれてない感はあるのですが、そもそもスマホによっては見えてはいけないもの(例えばイベントが起きる前でキャラが消えてるとか)が見えてるのは論外です。

アスペクト比が違う画面などでも調整不要なこの機能を入れて楽をしてしまいましょう!

 

参考サイト

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

Unity2Dで画面のアスペクト比を固定にしたい【Unity】 - Qiita
2Dゲームを作っている時に色んなデバイスで画面サイズを背景に合わせたい時があったのでメモ。 背景にサイズを合わせたい アスペクト比は固定にしたい 背景からはみ出す部分は黒くしたい デバイスのスクリーンサイズから縦に合わせるか横に合...
【Unity】画面アスペクト比を画面の高さに自動的に合わせて調整する
Unityで画面アスペクト比を固定する方法は割と紹介されていますが、基本はアスペクト比を固定しつつも縦側の解像…

ただこれだけだとCanvasScalarなどを使ってもいつも同じ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 * 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側の対応をしていきます。

コメント

  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をコピーしました