LoginSignup
1
1

More than 1 year has passed since last update.

【Unity】カメラの表示領域をスクリプトで調整する

Last updated at Posted at 2021-08-24

Unityで2Dスマホゲームを開発したいと考えているのですが、端末によって画面の大きさや比率が異なると思います。どの端末でも問題なくプレイできるように、スクリプトを用いてカメラの表示領域を動的に調整する方法を勉強しました。
Unity初心者なので解釈が間違っている部分などありましたらご教示いただけると幸いです。

準備

Canvasのインスペクターは次のように設定しました。
image.png

  • Canvas
    • レンダーモード:スクリーンスペース - カメラ
      • UIをスクリーンに描画
      • 常にレンダーカメラの正面に描画される
  • Canvas Scaler
    • UIスケールモード:画面サイズに拡大
      • 参照解像度に合わせてUIのスケールがサイズ調整される
    • スクリーンマッチモード:Expand
      • 縦横の解像度に合わせてスケーリングを行う
    • ユニット毎の参照ピクセル
      • Unityの距離の単位を「1ユニット=1ピクセル」に設定

縦も横もすべて表示する

E9fxeCUVUA0bC8T.png
上の画像は私の大好きな「#コンパス」というスマホゲームから引用しています。
iPhoneの画面をiPadの縦のサイズに合わせるようにしてスケーリングすると、iPadの画面はカメラの表示域が左右に広いのが分かります。
これは以下のコードで実装できます。

    [SerializeField] private Camera camera_;        // 対象のカメラ
    [SerializeField] private Vector2 aspectVec_;    // 参照解像度
    [SerializeField] private float pixelPerUnit_;   // 画像のPixelPerUnit
    private float currentAspect_ = 0.0f;            // 現在のアスペクト比

    private void SetCameraViewArea()
    {
       // カメラサイズの調整
       camera_.orthographicSize = aspectVec_.y / pixelPerUnit_ / 2;

       // ビューポートの調整
       float baseAspect = aspectVec_.y / aspectVec_.x;     // 基準のアスペクト比

       if (baseAspect <= currentAspect_)
       {
            // 画面が縦に長い場合、画面の長さにアスペクト比を追従させる
            float bgScale = currentAspect_ / baseAspect;

            // カメラのサイズを縦の長さに合わせて設定しなおす
            camera_.orthographicSize *= bgScale;
       }

       // viewportRectを設定(範囲内全てを表示)
       camera_.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
     }
  • orthographicSize
    image.png
    カメラのサイズを調整しています。投影方法を「平行投影」に設定した際のカメラの表示ボリュームを定義したもので、画面の縦半分にUnit(画素数 / Pixels Per Unit)がいくつ入るのかを求めその値を入れることで適切な表示領域が設定されます。

  • rect
    image.png
    カメラの描画範囲の矩形を調整しています。0 (左/下)から 1 (右/上) 範囲の値。
    ここではカメラを画面いっぱいに表示したいのでrectの第1・第2引数は0、第3・第4引数は1に設定しています。

アスペクト比を固定し、余白部分は表示しない

E9f1VwtUYAA40L_.png

私がいつも利用している目覚ましアプリから引用しています。
先程の画面とは違い、iPadの画面はiPhoneの画面比率を壊さないよう余白部分は表示していない(黒塗りしている)ことが分かります。
これは以下のコードで実装できます。

    [SerializeField] private Camera camera_;        // 対象のカメラ
    [SerializeField] private Vector2 aspectVec_;    // 参照解像度
    [SerializeField] private float pixelPerUnit_;   // 画像のPixelPerUnit
    private float currentAspect_ = 0.0f;            // 現在のアスペクト比

    private void SetCameraViewArea()
    {
       // カメラサイズの調整
       camera_.orthographicSize = aspectVec_.y / pixelPerUnit_ / 2;

       // ビューポートの調整
       float baseAspect = aspectVec_.y / aspectVec_.x;     // 基準のアスペクト比

       if (baseAspect <= currentAspect_)
       {
           // 画面が縦に長い場合
           float bgScale = aspectVec_.x / Screen.width;

           // viewportRectの縦幅
           float tmpHeight = aspectVec_.y / (Screen.height * bgScale);

           // viewportRectを設定
           camera_.rect = 
            new Rect(0.0f, (1.0f - tmpHeight) / 2, 1.0f, tmpHeight);
       }
       else
       {
           // 画面が横に長い場合
           float bgScale = aspectVec_.y / Screen.height;

           // viewportRectの横幅
           float tmpWidth = aspectVec_.x / (Screen.width * bgScale);

           // viewportRectを設定
           camera_.rect = 
            new Rect((1.0f - tmpWidth) / 2, 0.0f, tmpWidth, 1.0f);
       }
    }

rectの部分で長い方の画面は表示しないように調整しているのが分かります。

採用したコード

ezgif-2-f2b131e1029a.gif
適当に拾った画像を背景に使っているので見づらいかもしれませんが、私が現在作りかけている開発のGIF画像です。縦をすべて表示し、横の余白部分は表示しないように実装しました。

AspectKeeper.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// カメラの表示領域を調整する
[ExecuteAlways]
public class AspectKeeper : MonoBehaviour
{
    [SerializeField] private Camera camera_;      // 対象のカメラ
    [SerializeField] private Vector2 aspectVec_;  // 参照解像度
    [SerializeField] private float pixelPerUnit_; // 画像のPixelPerUnit
    private float currentAspect_ = 0.0f;          // 現在のアスペクト比

    void Update()
    {
        // カメラの表示領域の調整
        SetCameraViewArea();
    }

    // カメラの表示領域の調整
    private void SetCameraViewArea()
    {
        // 現在のアスペクト比を取得し、変化がなければ処理を抜ける
        float currentAspect = (float)Screen.height / (float)Screen.width;

        if (Mathf.Approximately(currentAspect_, currentAspect))
        {
            return;
        }

        currentAspect_ = currentAspect;

        // カメラサイズの調整
        camera_.orthographicSize = aspectVec_.y / pixelPerUnit_ / 2;

        // ViewportRectの調整
        SetViewportRect();
    }

    // ViewportRectの調整
    private void SetViewportRect()
    {
        float baseAspect = aspectVec_.y / aspectVec_.x;     // 基準のアスペクト比

        if (baseAspect > currentAspect_)
        {
            // 画面が横に広い場合、基準のアスペクト比に合わせて調整する
            float bgScale = aspectVec_.y / Screen.height;

            // viewportRectの幅
            float tmpWidth = aspectVec_.x / (Screen.width * bgScale);

            // viewportRectを設定
            camera_.rect = new Rect(
             (1.0f - tmpWidth) / 2, 0.0f, tmpWidth, 1.0f);
        }
        else
        {
            // 画面が縦に長い場合、画面の長さにアスペクト比を追従させる
            float bgScale = currentAspect_ / baseAspect;

            // カメラのサイズを縦の長さに合わせて設定しなおす
            camera_.orthographicSize *= bgScale;

            // viewportRectを設定(範囲内全てを表示)
            camera_.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
        }
    }
}

縦画面固定のスマホアプリを制作予定なので正直Update()関数で呼び出す必要があるのかは謎なのですが、Android端末だと2画面表示があるとのことですし何かあっては怖いのでとりあえず毎フレーム処理を回しています。

参考にした素晴らしいサイト様☺

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1