概要
今回は、GameViewのスクリーンショットがショートカットキーでいつでも撮影(キャプチャ)できるエディタ拡張を作成しました。
Unityのスクリーンショットを撮影する
Unityエディタ上でスクリーンショットを撮影する方法は様々です。
実際のゲーム画面のプレビューであるGameViewを撮影したいとき、もっともオーソドックスなのはScreenCapture.CaptureScreenshotを呼び出す方法です。
とりあえず、この関数を呼び出しておけば、ゲーム再生中にスクリーンショットの撮影が可能となります。
問題はこれをいつ呼び出すかということになります。
Unityで入力を受け取りたければ、基本的にUpdate関数内でInputクラスの関数を使って入力待ちをすることになると思います。
しかし、この方法には以下の問題があります。
- ゲーム再生中にしか呼び出すことができない
 - 全シーンのオブジェクトに当該スクリプトがアタッチされたゲームオブジェクトを配置する必要がある
 
とにかくUpdateが回って入力が受け取れる環境を準備するしかありません。
要は、UnityのGameViewが撮影できるという機能が欲しいというだけなのに、シーン上にいちいち入力のためのオブジェクトを準備するしかないのです。
DontDestroyOnLoadのオブジェクトとして生成し、シングルトンっぽく扱ってもいいですが、これもやっぱり気持ち悪いと思います。
ただ置いておくだけで、GameViewのスクショがいつでも撮れる機能が欲しいですよね。
機能
そんなこんなで、以下機能を備えたエディタ拡張を作成しました。
- シーンに入力待ち用のゲームオブジェクトを必要としない
 - いつでも撮れる
 - 任意で設定が変更可能(保存先、ファイル名など)
 - 保存されたPNGがエクスプローラーや関連付けられたアプリケーションで開かれ、撮影したことが直感的にわかる
 
使い方
- CaptureGameView.csをAssets/Editorに入れてください
 - Alt + Shift + C でスクリーンショットが撮れます
 - 保存先を指定しファイルを保存してください
 
補足
キー入力の受け付けは、Unityのショートカット拡張で行っているため、Updateの呼び出しに依存せずゲーム再生中でなくても撮影ができます。
また、ゲームを再生をしなくてもコルーチンを進行させることができるようにしました。
これにより、PNGファイル生成を待ってから、エクスプローラーとWindowsで関連付けられたアプリケーションで開いてくれます。「今どんなのが撮れたのか分からない」みたいな状況を防げるようにしました。
しかし、生成に時間がかかったり、いちいち撮影のたびにゲームの再生が停止したりするので、連射などをしたい場合はこれらの設定をfalseにしてください。
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Diagnostics;
using System.Collections;
public class GameViewCapture : EditorWindow {
	//ディレクトリのパス
	//エスケープシーケンスに注意(\ → \\)
	public static string directoryPath = "./ScreenShots";
	//ファイルの名前
	//拡張子は不要
	public static string filename = "ScreenShot";
	//日付をつけるかどうか
	public static bool isYear = true;
	public static bool isMonth = true;
	public static bool isDate = true;
	public static bool isHour = true;
	public static bool isMinute = true;
	public static bool isSecond = true;
	//撮影したらゲームを停止するかどうか
	public static bool isPausePlaying = true;
	//名前をつけて保存のパネルを開くかどうか
	public static bool isOpenSaveFilePanel = true;
	//エクスプローラーで開くかどうか
	public static bool isOpenExplorer = true;
	//関連付けられたアプリケーションで開くかどうか
	public static bool isOpenApplication = false;
	//出力中のファイルのパス
	public static string generatedFilePath = null;
	// オブジェクトの共通Openコマンド
	// % (ctrl on Windows, cmd on macOS), # (shift), & (alt)
	[MenuItem("Tools/GameViewCapture &#c")]
	//ゲームビューをキャプチャする
	static void Capture() {
		if (isPausePlaying) {
			//再生中だったら
			if (EditorApplication.isPlaying) {
				//ゲームを停止
				EditorApplication.isPaused = true;
			}
		}
		//ファイルを出力中だったら
		if (generatedFilePath != null) {
			//ダイアログを表示
			if (EditorUtility.DisplayDialog("ファイルを出力中です", "出力中のファイル" + generatedFilePath + "は破棄されます。" + "\n" + "新しいファイルを保存しますか?", "保存", "中止")) {
				//出力中を解除
				generatedFilePath = null;
			}
			else {
				//処理を中止
				return;
			}
		}
		//日付文字列を作成
		string time = "";
		if (isYear) {
			time += DateTime.Now.ToString("_yyyy");
		}
		if (isMonth) {
			time += DateTime.Now.ToString("_MM");
		}
		if (isDate) {
			time += DateTime.Now.ToString("_dd");
		}
		if (isHour) {
			time += DateTime.Now.ToString("_HH");
		}
		if (isMinute) {
			time += DateTime.Now.ToString("_mm");
		}
		if (isSecond) {
			time += DateTime.Now.ToString("_ss");
		}
		//ファイルのパス
		string filePath = null;
		if (isOpenSaveFilePanel) {
			//ファイルの保存先を指定
			filePath = EditorUtility.SaveFilePanel("名前を付けて保存", "", filename + time, "png");
			//パスの指定がなかったら
			if (string.IsNullOrEmpty(filePath)) {
				//処理を中止
				return;
			}
		}
		else {
			//ファイルの保存先を作成
			filePath = string.Format(Path.Combine(directoryPath, filename + time + ".png"));
			//フォルダが存在していなかったら
			if (!Directory.Exists(directoryPath)) {
				//ダイアログを表示
				if (EditorUtility.DisplayDialog("フォルダが存在しません", "保存先のフォルダ" + directoryPath + "は存在しません。" + "\n" + "新しくフォルダを作成しますか?", "作成", "中止")) {
					//フォルダを作成
					Directory.CreateDirectory(directoryPath);
					UnityEngine.Debug.Log("generated : " + directoryPath);
				}
				else {
					//処理を中止
					return;
				}
			}
		}
		//ゲームビューをキャプチャ
		ScreenCapture.CaptureScreenshot(string.Format(filePath));
		//キャプチャしたことを通知
		UnityEngine.Debug.Log("Captured : " + filePath);
		//EditorApplication.updateにコルーチンの進行を追加
		IEnumerator coroutine = GenerateFile(filePath);
		EditorApplication.update += () => coroutine.MoveNext();
	}
	public static IEnumerator GenerateFile(string path) {
		//出力中に設定
		generatedFilePath = path;
		//出力されるまで待つ
		while (!File.Exists(path)) {
			yield return null;
		}
		//出力中を解除
		generatedFilePath = null;
		//出力されたことを通知
		UnityEngine.Debug.Log("generatedd : " + path);
		//ファイルを開く
		OpenFile(path);
	}
	public static void OpenFile(string path) {
		if (isOpenExplorer) {
			//エクスプローラーで開く
			Process.Start("explorer.exe", "/select," + path.Replace("/", "\\"));
		}
		if (isOpenApplication) {
			//関連付けられたアプリケーションで開く
			Process.Start(path);
		}
	}
}
 これですっきり。
 ブログ掲載などに役に立つかと思いますので、ご活用ください。
 ご拝読、ありがとうございました。