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

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

#準備
Canvasのインスペクターは次のように設定しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1384531/e06eea0a-eb0f-ec77-1055-2c47b18a7dbf.png)


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

# 縦も横もすべて表示する

![E9fxeCUVUA0bC8T.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1384531/ededaa24-23d7-c03a-7521-9fe46015467d.png)
上の画像は私の大好きな「＃コンパス」というスマホゲームから引用しています。
iPhoneの画面をiPadの縦のサイズに合わせるようにしてスケーリングすると、iPadの画面はカメラの表示域が左右に広いのが分かります。
これは以下のコードで実装できます。

``` c:
    [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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1384531/f1018300-fe29-9a88-daa7-84df4263307a.png)
カメラのサイズを調整しています。投影方法を「平行投影」に設定した際のカメラの表示ボリュームを定義したもので、画面の縦半分にUnit（画素数 / Pixels Per Unit）がいくつ入るのかを求めその値を入れることで適切な表示領域が設定されます。

- rect
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1384531/c81ef64b-c116-56d3-ce41-bb8618e89ee0.png)
カメラの描画範囲の矩形を調整しています。0 (左/下)から 1 (右/上) 範囲の値。
ここではカメラを画面いっぱいに表示したいのでrectの第１・第２引数は０、第３・第４引数は１に設定しています。

#アスペクト比を固定し、余白部分は表示しない
![E9f1VwtUYAA40L_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1384531/5bff3864-f5fd-92f4-993c-28c1edf0f0f5.png)

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

``` c:
    [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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1384531/73e0c6b5-5be9-eab4-7c19-07ad850a95d5.gif)
適当に拾った画像を背景に使っているので見づらいかもしれませんが、私が現在作りかけている開発のGIF画像です。縦をすべて表示し、横の余白部分は表示しないように実装しました。

``` c: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端末だと２画面表示があるとのことですし何かあっては怖いのでとりあえず毎フレーム処理を回しています。

#参考にした素晴らしいサイト様☺
https://pengoya.net/unity/aspect/