Unityでカメラを増やしたとき、自動的に画面を分割してくれるスクリプト

More than 1 year has passed since last update.

b.gif

複数のカメラをシーンビューで同時に見たいとき、インスペクタからビューポートをチマチマ調整するのは面倒です。そこで、これを自動化してくれるスクリプトを書きました。空のゲームオブジェクトにスクリプトをアタッチすれば使えると思います。

まだまだC#勉強中のため、効率の良い書き方をコメントに書いてくれると嬉しいです。

コメントの内容を記事のソースコードに反映のは時間があればやろうと思います。

変数名や関数名がダサいのは許してください。


既知の不具合

現在、最初の画面比が1:1から離れていると、ゼロ割り算を起こしたり、最初から2分割されていたりする問題を確認しています。


ソースコード

using System.Collections;

using System.Collections.Generic;
using UnityEngine;

public class CameraGamenBunkatu : MonoBehaviour
{
// 2:11 2018/05/21
// 初期アスペクト比が1:1から離れていると、おかしくなる問題がある。
// ダミーカメラとは、背景を黒にするためのカメラ。 ダミーカメラが無いと、上に描画するものが何もないとき、ビューポートがおかしくなる。

int yoko_num = -1; // 横に並ぶカメラの画面数
int tate_num = -1;
Camera[] cams; // ダミーカメラを除いたカメラの配列。

int preCameraCount = 0; // 前のフレームでのカメラの数。これをupdate関数内で使うことで、カメラが増えたら自動的に画面の調整を行う。

// カメラの数に応じて画面を調整する。
void Gamen_Tyousei()
{
const string dummy_cam_str = "dummy_cam";

GameObject d_cam = GameObject.Find(dummy_cam_str);
if (d_cam == null)
{
d_cam = new GameObject();
d_cam.name = dummy_cam_str;
Camera d_c = d_cam.AddComponent<Camera>();
d_c.clearFlags = CameraClearFlags.SolidColor;
d_c.backgroundColor = Color.black;
d_c.cullingMask = 0; // 0にすると、インスペクタプルダウンメニューのNothingになるんだと思う。
d_c.depth = -2; // デプスの浅いマスクから表示される。デフォルトのカメラが確か-1。 https://naichilab.blogspot.jp/2013/07/unity.html
print("カメラ数が分割数に対して余る場合、背景を黒にするためのダミーカメラを生成しました。");
}

Camera[] Camcam = Camera.allCameras; // 一旦配列にコピーして、インデックスアクセス可能にする。

List<Camera> cam_list = new List<Camera>();
for (int i = 0; i < Camcam.Length; i++)
{
if (Camcam[i].gameObject.name != dummy_cam_str) // ダミーカメラだけ除いた配列を作りたい。
{
cam_list.Add(Camcam[i]);
}
}

cams = cam_list.ToArray();
// print("カメラ総数 : " + camTotalNum);

// 初期アスペクト比が1:1から離れていると、おかしくなる問題がある。多分、原因はここでゼロ割り算が起きたりしているせい。
yoko_num = Mathf.CeilToInt(Mathf.Sqrt(cams.Length * Screen.width / Screen.height)); // 繰り上げ。平方根をとっているのは、縦も横も同じ数になるようにするため。
tate_num = cams.Length / yoko_num; // 横が決まったら縦も自動的に決まる。

if (cams.Length % yoko_num != 0)
{
// 余りがある場合は、一列増やす。
tate_num += 1;
}

// カメラが画面に描画されるときのビューポートでの単体の大きさ。
float oneHeight = 1.0f / tate_num;
float oneWidth = 1.0f / yoko_num;

for (int i = 0; i < tate_num; i++)
{
for (int j = 0; j < yoko_num; j++)
{
try
{
Camera c = cams[i * yoko_num + j];

int hoge = (tate_num - 1 - i); // 余りが出た場合、黒い画面が下に来るようにするためにこの計算をしている。
c.rect = new Rect(oneWidth * j, oneHeight * hoge, oneWidth, oneHeight);
}
catch (System.IndexOutOfRangeException) // 一列増やしている場合は要素外アクセスが起こる場合がある。そのときは、カメラ配列をたどるのを終了。
{
goto KOKO_DAYO;
}
}
}

KOKO_DAYO:
;
}

void Start()
{
}

void OnGUI()
{
float oneHeight = 1.0f / tate_num;
float oneWidth = 1.0f / yoko_num;

for (int i = 0; i < tate_num; i++)
{
for (int j = 0; j < yoko_num; j++)
{
try
{
int num = i * yoko_num + j;
Camera c = cams[num];

int hoge = i; // ビューポートとは座標系が異なるため、カメラの配置のときと、ここの部分だけちょっと違う。
Rect rect = new Rect(oneWidth * j * Screen.width, oneHeight * hoge * Screen.height, oneWidth * Screen.width, oneHeight * Screen.height);
GUI.Label(rect, c.name + "[" + num.ToString() + "]_" + rect.ToString());
}
catch (System.IndexOutOfRangeException)
{
goto KOKO_DAYO;
}
}
}

KOKO_DAYO:
;
}

// Update is called once per frame
void Update()
{
if (preCameraCount != Camera.allCamerasCount) // もしかして毎フレームallCamerasCountやるのは速度的に良くない?
{
Gamen_Tyousei();
preCameraCount = Camera.allCamerasCount;
}

// デバック用。カメラを増やす。他の方法で動的にカメラを増やした場合は、Gamen_Tyousei()関数を実行してやる必要がある。
if (Input.GetKeyDown(KeyCode.C))
{
int camTotalNum = Camera.allCamerasCount - 1; // -1してダミーカメラを除いている。

GameObject cam = new GameObject();
cam.transform.position = Vector3.forward * -10;
cam.name = "add_camera_" + camTotalNum.ToString();
cam.AddComponent<Camera>();
}
}
}