【Unity】IMGUIの変換行列をいじってどんな解像度でも見やすいGUIを描画する

UnityのIMGUIの変換行列をいじってどんな解像度でも見やすいGUIを描画する方法です。

Unity2020.1

やりたいこと

UnityのGUIクラスを使うとIMGUIでGUIが描画できます。
例えば以下のようにボタンを描画するスクリプトを記述してみます。

using UnityEngine;

public class Example : MonoBehaviour
{
    private void OnGUI()
    {
        var rect = new Rect(12, 12, 100, 30);
        GUI.Button(rect, "Example");
    }
}

描画結果は以下のようになります。

f:id:halya_11:20210220114014p:plain
ボタンを描画

ここで、解像度を大きい値に変更してみます。

f:id:halya_11:20210220114104p:plain
見えない

ボタンが見えないくらいの大きさに描画されてしまいました。

正攻法と問題点

この問題に対応する方法の正攻法としてはボタンのサイズやフォントサイズを解像度に応じてスケーリングすることです。

using UnityEngine;

public class Example : MonoBehaviour
{
    private void OnGUI()
    {
        var scale = (float)Screen.width / 320; // 基準値は適当に決める(ホントはもっとちゃんとやるべき)
        var style = new GUIStyle(GUI.skin.button);
        style.fontSize = (int)(scale * 12);
        var rect = new Rect(12, 12, 100 * scale, 30 * scale);
        GUI.Button(rect, "Example", style);
    }
}

これで大きなボタンが表示されます。

f:id:halya_11:20210220115253p:plain
ボタン

ボタンはこれで解決するのですが、たとえばWindowのようなものだとタイトルを大きくしてもその背景が大きくできなかったりします。

f:id:halya_11:20210220115600p:plain
ウィンドウ

これではかなり不格好です。

変換行列をいじって解決する

この問題を解決するためには根本的に別のアプローチが必要です。
GUIには変換行列を設定できるため、今回はそもそも解像度に応じてこの変換行列のスケーリングを変えてしまいたいと思います。

using UnityEngine;

public class Example : MonoBehaviour
{
    private void OnGUI()
    {
        var oldMatrix = GUI.matrix;
        
        var scale = (float)Screen.width / 320; // 基準値は適当に決める(ホントはもっとちゃんとやるべき)
        var screenWidth = (int)(Screen.width / scale);
        // 変換行列を上書き
        GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(scale, scale, 1));
        GUI.ModalWindow(1, new Rect(12, 12, screenWidth - 24, 300), id =>
        {
        }, "Example");
        
        GUI.matrix = oldMatrix;
    }
}

これを再生すると高い解像度でも以下のようにウィンドウが比較的まともに表示されることが確認できました。

f:id:halya_11:20210220120039p:plain
拡大された

(枠線まで拡大されちゃうけど、これ以上はIMGUIではどうしようもない