【Unity】ゲーム画面のアスペクト比(縦横比)を固定する方法

ゲーム画面の縦横比を固定する方法 ゲーム開発

今回はUnityに関する話題で、タイトルの通り

ゲーム画面のアスペクト比(縦横比)を固定する方法

をサンプルスクリプト付きでご紹介するという内容になっております。

Unityでゲームを作っているとゲームによっては様々な解像度の画面に対応しないといけない場合があります。このときによく悩みのタネになるのがゲーム画面のアスペクト比です。

ゲームによってはアスペクト比が想定した比率と違うとプレイしたときの感触が変わってしまったり、アスペクト比によって有利不利が左右されてしまう場合もよくあるので、この問題を何とかしたいと思っている方は結構いらっしゃるのではないでしょうか。

そこでここではゲーム画面のアスペクト比を固定して、どんな解像度の画面でも一定のアスペクト比になるようにするための方法を解説していきますね。

前提:ゲーム画面のアスペクト比を固定したほうがいいケースについて

はじめに、そもそもゲーム画面のアスペクト比を固定したほうがいい場合について簡単にご説明しておきます。

プレイヤーによってゲーム画面のアスペクト比は様々

まずゲーム画面のアスペクト比は基本的に

  • 全画面表示ならスクリーンの解像度
  • ウィンドウ表示ならウィンドウのサイズ

によって変化します。PCゲームであれば普通はフルHDや4Kなどの解像度で出力されるのでアスペクト比は16:9になることが多いですが、出力先のディスプレイや設定次第ではどうなるかわかりません。例えば超ワイドなディスプレイを使っているプレイヤーがいるかもしれませんし、中には設定を変更して変なアスペクト比で遊んでいるプレイヤーも全くいないとは言い切れません。

想定外のアスペクト比で自作ゲームを遊ばれると困る場合がある

そんなわけで色々なアスペクト比に対応できるゲームを作るのがベスト…なのですが、ゲームを開発しているとなかなかそうもいかないのが現実です。そもそもアスペクト比が想定と違うと

  • 思っていた以上に視野が広く(もしくは狭く)なりゲームの感触が変わったり、ゲームの有利不利が変わったりしてしまう
    (例:横スクロールSTGでは横長の画面のほうが先を見渡しやすくなり有利になる…など)
  • 画面のレイアウトが崩れる

といった問題が発生することがよくあるので、想定外のアスペクト比で遊んでもらっては困る場合がけっこうあるんですよ。でも色々なアスペクト比のパターンがあるとして、限られた開発時間の中で一つ一つのパターンを虱潰しにチェックしていくわけにもいきません。

こんなゲームの場合はアスペクト比を固定すると安心

そこで次のようなゲームの場合はアスペクト比を固定しておくと安心です。

  • 見える範囲が変わると不都合が生じるゲーム
  • どうしても画面のレイアウトを変更できないゲーム

特にシューティング系のゲームなど「見える範囲が変わるとゲームバランスが崩れる」ような場合はアスペクト比を固定しておくことをお勧めします。

ゲーム画面のアスペクト比を固定するためのC#スクリプト

では前置きはこのくらいにして、メインとなるゲーム画面のアスペクト比を固定するためのC#スクリプトを掲載しておきます。

注意:
下記のC#スクリプトは個人利用の範囲においてご自由に利用して頂いてOKですが、勝手に別の場所に転載したり作者を偽ったりするのは禁止とします。

using UnityEngine;

namespace KurokumaSoft
{

	public sealed class AspectRatioSetter : MonoBehaviour
	{

		[SerializeField]
		Camera targetCamera;
		[SerializeField]
		Vector2 targetSize = new(16f, 9f);
		[SerializeField]
		bool autoSetOnStart = true;

		public Camera TargetCamera { get { return targetCamera; } set { targetCamera = value; } }
		public Vector2 TargetSize { get {  return targetSize; } set { targetSize = value; } }

		void Awake()
		{
			if (autoSetOnStart)
			{
				SetAspectRatio();
			}
		}

		public void SetAspectRatio()
		{
			if(targetCamera == null)
			{
				return;
			}

			float currentRatio = (float)Screen.width / Screen.height;
			float targetRatio = targetSize.x / targetSize.y;
			float scaleHeight = currentRatio / targetRatio;
			Rect rect = targetCamera.rect;

			if (scaleHeight < 1f)
			{
				rect.width = 1f;
				rect.height = scaleHeight;
				rect.x = 0;
				rect.y = (1f - scaleHeight) / 2f;
			}
			else
			{
				float scaleWidth = 1f / scaleHeight;
				rect.width = scaleWidth;
				rect.height = 1f;
				rect.x = (1f - scaleWidth) / 2f;
				rect.y = 0;
			}

			targetCamera.rect = rect;
		}

	}

}

このC#スクリプトの使い方

上記のC#スクリプトを適当なゲームオブジェクト(カメラのゲームオブジェクト等)にアタッチして、「targetCamera」にメインカメラを登録してください。「autoSetOnStart」にチェックが入っていればゲーム開始時にアスペクト比が自動的に指定した値に変更されます。

もしゲーム中に画面の解像度が変更された場合はその直後に「SetAspectRatio」関数を呼び出すようにしてください。

C#スクリプトの解説

ゲーム画面の縦横比はカメラの「rect」変数で調整できるので、現在のアスペクト比と目標のアスペクト比から高さスケールを算出してそれを元に調整を行います。

高さスケール(scaleHeight)の計算

現在のアスペクト比と目標アスペクト比の比率を計算して結果をscaleHeightに格納します。この値が1より小さい場合は現在の画面は目標アスペクト比に対して「横長」であることを意味し、1より大きい場合は現在の画面は目標アスペクト比に対して「縦長」であることを意味します。

画面を横長に変更する場合→縦方向に画面を縮めて調整(上下に余白が出現)

scaleHeightが1未満の場合は画面を縦方向にスケールします。

  • ビューポートの幅は1f(全幅)
  • 高さはscaleHeightに設定
  • 上下の余白を計算してrect.yに設定(画面が中心に来るように計算)

画面を縦長に変更する場合→横方向に画面を縮めて画面を調整(左右に余白が出現)

scaleHeight が1以上の場合は画面を横方向にスケールします。

  • ビューポートの幅を scaleWidth(1f / scaleHeight)に設定
  • 高さは 1f(全高)
  • 左右の余白を計算してrect.x に設定(画面が中心に来るように計算)

余白部分の不具合を解消する方法

さて、上記のC#スクリプトを使えばゲーム画面のアスペクト比を固定できるのですがここで一つ問題が発生します。それは余白部分の表示がおかしくなることがあるということです。

先ほどのスクリプトによってできる余白部分は一見すると何の問題もないように見えるのですが、実はこの部分は何もしないと描画の初期化が行われないので例えばUIがそこに重なって移動すると下記のように残像のようなものが残ってしまいます。

レターボックス部分にカーソルのUIが重なって残像のような見た目になった場合の例
レターボックス部分にカーソルのUIが重なって残像のような見た目になった場合の例

この問題を回避するためには余白用の別のカメラを用意する必要があります。新しいカメラを作成して設定を次のように変更しましょう。

  • 優先度を「-100」など非常に低い値にする
  • カリングマスクを「Nothing」に変更して何も映らないようにする
  • AudioListenerコンポーネントを削除する

※注意:こちらのカメラはアスペクト比を固定する必要はないので先ほどのC#スクリプトをアタッチする必要はありません。

これで余白部分に変な残像が表示されなくなります。

おわりに

以上、Unityでゲーム画面のアスペクト比を固定する方法をご紹介しました。アスペクト比の固定方法は意外とややこしいので初心者の方にとっては大変だと思いますが、ぜひ上記の内容を参考にしていただればと思います。

この記事がUnityでのゲーム開発のお役に立てば幸いです。