Help us understand the problem. What is going on with this article?

【Unity】Unityでスクリーンショットを撮る時に必要なTipsまとめ

概要

この記事のターゲットは Vtuberの活動をしていて、Youtubeのサムネ作る為に毎回Blenderでレンダリングしてたり、撮影するために別のソフト立ち上げてたりする方 です。
またVRMファイルのサムネ用アイコンの作成でも便利です。
※知ってる人からするとほぼ役立ちません。
unityで撮影すればポストエフェクトをそのまま使えたり、アセットを背景にできるのでわざわざ別のソフトで画作りするよりよっぽど楽な事があるので、活用するのがおすすめです。

ScreenCapture .CaptureScreenshot

ScreenShot.cs
if(Input.GetKeyDown(KeyCode.S))
{
   ScreenCapture.CaptureScreenshot("image.png");
   Debug.Log("screenshot");
}

※最小プランのスクリプト
1.unityでC#のスクリプトを作成する。名前は「ScreenShot」とか。
2.作成したC#のファイルを開いて void Update(){ } (update関数)の中に上記のスクリプトを書き足す。
3.上記のスクリプトを適当なゲームオブジェクトにアタッチする。
4.実行してSキーを押すと…プロジェクトの保存されてるディレクトリに画像が保存されています。

参考:Unityリファレンス:ScreenCapture .CaptureScreenshot
この記事で言いたい事は「このクラス便利だよ」が8割なのですが…
使うにあたっていくつかTipsがあります。

ScreenCapture.CaptureScreenshot() の引数には拡張子を含める事

文字列の中に .png が抜けると画像として保存されない。
(保存されたファイルに後から拡張子足すと読み込めます)
リファレンスの引数が "SomeLevel" だから騙される。書くなら "SomeLevel.png" やろ

同名のファイル名がある場合、上書きされる

例えばフィールド変数に「string imageTitle = "Vtuber";」とか作って…
ScreenCapture.CaptureScreenshot(imageTitle)」とかすると、撮影する度に「imageTitle」を書き換える必要がある。

スクリーンショットを撮る関数にRandom.Rangeで適当な値作ってひっつけてもいいけど、どうせやるなら「タイムスタンプ欲しいよね」って話になる。

保存先は文字列を結合して引数として渡す

ScreenShot.cs
   ScreenCapture.CaptureScreenshot("Assets/images/image.png");

1.unityのAssetsフォルダの中に「images」フォルダを作成する。
2.ScreenCapture.CaptureScreenshotの引数を「"Assets/images/image.png"」に書き換える。
3.imagesフォルダの中に「image.png」が作成されるようになる。
パスを間違えてnullだと当然エラー。

Gameタブの解像度に依存している

このメソッドは「unityのgameタブに表示されてる画像を保存する」という処理なので、保存される画像のサイズは参照しているgameタブの解像度に依存する。
なので、あらかじめgameタブの解像度を調整する必要がある。

…というように、下準備をするのは結構面倒。

面倒なので書きました

ScreenShot.cs
using System;
using System.IO;
using System.Collections;
using UnityEngine;

public class ScreenShot : MonoBehaviour
{
    //タイムスタンプの定義
    public enum TIME_STAMP
    {
        MMDDHHMMSS,
        YYYYMMDDHHMMSS,
    }

    [SerializeField, Tooltip("タイムスタンプの書式設定")]
    private TIME_STAMP _timeStampStyle;

    //ファイル名の指定
    [SerializeField, Tooltip("ファイル名の末尾に付く文字")]
    private string _imageTitle = "img";

    //保存先の指定 (末尾に / を付けてください)
    [SerializeField, Tooltip("ファイルの保存先 末尾の/ を含めてください")]
    private string _screenShotFolder = "/ScreenShots/";

    //撮影ボタンの表示切替
    [SerializeField, Tooltip("trueならGUIの撮影ボタンを表示します")]
    private bool _shotButtonActive = false;

    void Update()
    {
        //「Q」でボタンの表示切替
        if (Input.GetKeyDown(KeyCode.Q))
        {
            _shotButtonActive = !_shotButtonActive;
        }

        //「P」で撮影
        if (Input.GetKeyDown(KeyCode.P))
        {
            // Application.dataPath = ../Assets
            string path = Application.dataPath + _screenShotFolder;
            StartCoroutine(imageShooting(path, _imageTitle));
        }
    }

    //撮影ボタン設定
    void OnGUI()
    {
        if (_shotButtonActive == false) { return; }

        if (GUI.Button(new Rect(10, 10, 40, 20), "Shot"))
        {
            // Application.dataPath = ../Assets
            string path = Application.dataPath + _screenShotFolder;
            StartCoroutine(imageShooting(path, _imageTitle));
        }
    }

    //撮影処理
    //第一引数 ファイルパス / 第二引数 タイトル
    private IEnumerator imageShooting(string path, string title)
    {
        imagePathCheck(path);
        string name = getTimeStamp(_timeStampStyle) + title + ".png";

        ScreenCapture.CaptureScreenshot(path + name);

        Debug.Log("Title: " + name);
        Debug.Log("Directory: " + path);
        yield break;
    }

    //ファイルパスの確認
    private void imagePathCheck(string path)
    {
        if (Directory.Exists(path))
        {
            Debug.Log("The path exists");
        }
        else
        {
            //パスが存在しなければフォルダを作成
            Directory.CreateDirectory(path);
            Debug.Log("CreateFolder: " + path);
        }
    }

    //タイムスタンプ
    private string getTimeStamp(TIME_STAMP type)
    {
        string time;

        //タイムスタンプの設定書き足せます
        switch (type)
        {
            case TIME_STAMP.MMDDHHMMSS:
                time = DateTime.Now.ToString("MMddHHmmss");
                return time;
            case TIME_STAMP.YYYYMMDDHHMMSS:
                time = DateTime.Now.ToString("yyyyMMddHHmmss");
                return time;
            default:
                time = DateTime.Now.ToString("yyyyMMddHHmmss");
                return time;
        }
    }

}

セットアップ

1.「ScreenShot」という名前でC#のスクリプトを作成する。
2.自動で作成されるテンプレートを全て消し、上記のスクリプトを全てコピペする。
3.適当なGameobjectにScreenShotをアタッチする。
4.実行して「P」キーを押す。
5.「Assets/ScreenShots/」のフォルダの中にタイムスタンプ付きで画像が作成される。
※5はデフォルト設定

使い方

_imageTitle
タイムスタンプの末尾につくファイル名が変わります。モデルの名前にするとよいかも。

_screenShotFolder
デフォルトは Assets/ScreenShots/ に保存されます。
「ディレクトリが存在する事を確認してから保存、ディレクトリがなければフォルダの作成」をしているので、任意のディレクトリを指定してくださればそれ以降任意のディレクトリへ保存するようになります。

ボタンで撮影
「Q」を押すとボタンを表示できます。
撮影には邪魔ですが、デバック環境によってはボタン制御が必要な事もあるので使い分けれるようにしました。

Gameタブの解像度を指定する方法

gametabu.png
1.Gameタブのあの部分をクリックするとメニューが展開される。
2.+から「Add New Item」を押す。
3.ウインドウでLabel (名前) Width&Height (縦横の解像度)を設定する。
4.プリセットに追加されるので、任意の解像度にできる。

Vtuberの用途的には、Youtubeの基本の解像度を入れておくとよいと思います。
また 128x128 や 1024x1024 等も入れておくと、アイコン作成やテクスチャ作成時に便利です。

参考:【Unity】高解像度のゲーム画面向けにゲームビューを調整する
参考:Youtubeで推奨される解像度とアスペクト比

グリーンバック作成方法

背景としてplane置いてunlit/colorのマテリアルアタッチするでもいいんですが、カメラの設定を変えると色々楽なのでこの際カメラの設定方法を覚えてしまいましょう。

cam.png

Clear Flags を 「Solid Color」に変更すれば、カメラに描画される背景はBackgroundに指定した色になります。
なので、シーンにキャラクターしかいない状態なら、どうカメラを動かしてもグリーンバックにできますし、青にしたければ直接変えればOKです。
背景用のオブジェクトをセットアップするよりも楽ですし、オブジェクト抜けによる事故もありません。

また、Unityのカメラは「CullingMask」を設定すると、映るオブジェクト/移らないオブジェクトをカメラ毎に設定できます。
これにはタグの設定なども必要ですので、あわせて覚える必要がありますが長く使うプロジェクトでしたら、ちゃんとタグ分けして何パターンかのカメラを用意しておくと便利です。
ちなみに、ここらへんの知見はVRChatでも使えます。

参考:Unityリファレンス:タグ
参考:【Unity】Tagの追加ってどうやるの?【初心者向け】
参考:カメラ(Camera)

カメラを移動しながら撮影したい。

【Unity】カメラ移動を制御するスクリプト
上記を参考にしてみてください。キャラクターを撮影するプロジェクトが作れます。
※キー設定が被ってるのでよしなに。

まとめ

Qiita書いてる時間の方が長かった…
個人Vtuberで3D作ってちゃんとプロジェクト自作してる人は大変でしょうが頑張れ!
君だけの最強のUnityを作ろう!!!

Nekomasu
初心者
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away