おもちゃラボ

Unityで遊びを作っていきます

【Unity】スクショをカメラロールに保存する簡単な手順

Unityでスクリーンショットを撮影するのはとても簡単なのですが、これをiOSのカメラロールに保存するのは案外手間がかかります。ここではその方法を簡単に紹介したいと思います。

スクリーンショットをカメラロールに保存する流れは次のとおりです。

Unityでスクリーンショットを撮影する

Unityで画面のキャプチャするにはScreenCapture.CaptureScreenshotメソッドを使います。(少し前まではApplication.CaptureScreenshotでしたが、機能は同じです)

このCaptureScreenshotメソッドの引数にファイル名を渡すことで、Application.persistentDataPathが指すフォルダにスクショ画像が保存されます。

iOSでApplication.persistentDataPathは、作成中のアプリ内のDocumentフォルダを指しています。なので、カメラロールに保存したい場合には、保存したフォルダからカメラロールに画像をコピーする必要があるわけです。

f:id:nn_hokuson:20180501200226p:plain:w380

スクショをカメラロールにコピーするPluginを作る

ただし、Unity標準のAPIを使って、アプリ内のフォルダからカメラロールに画像を移すことはできません。そこで、上の工程を行うiOS用のPluginを作成する必要があります。

それではPluginを作っていきましょう。Assetフォルダの下にPluginsフォルダを作成し、その下にiOSフォルダを作成してください。そしてiOSの中にScreenShot.mmという名前でファイルを作成します。.mmの拡張子はObjective-C++のファイルを表します。

f:id:nn_hokuson:20180501200456p:plain:w400

作成したScreenShot.mmファイルに次のプログラムを入力してください。

#import <Foundation/Foundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import <AVFoundation/AVFoundation.h>

extern "C" void SaveToAlbum (const char* path)
{
    UIImage *image = [UIImage imageWithContentsOfFile:[NSString stringWithUTF8String:path]];
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
    [library writeImageToSavedPhotosAlbum:image.CGImage metadata:metadata completionBlock:^(NSURL *assetURL, NSError *error) {}];
}

このプログラムでは引数で受け取ったパスにある画像ファイルを、カメラロールに保存し直しています。カメラロールに保存するにはALAssetsLibraryクラスのwriteImageToSavedPhotosAlbumメソッドを使用します。

このPluginではAssetsLibraryをインポートしているので、AssetsLibrary Frameworkをプロジェクトに追加しておかないとXcodeのリンク時にリンカエラーが出ます。

AssetsLibraryを追加するにはScreenShot.mmファイルを選択して、インスペクタの「Platform settings→Rarely used frameworks→AssetsLibrary」にチェックを入れます。

f:id:nn_hokuson:20180501200549p:plain

Pluginでスクショをアプリ内からカメラロールにコピーする

画像ファイルをカメラロールに保存するPluginが出来たので、UnityからこのPluginを呼び出す部分を作りましょう。ScreenShot.csファイルを作成(別にPluginのファイル名と対応する必要はありません)して、空のGameオブジェクトにアタッチします。

f:id:nn_hokuson:20180501200805j:plain

次にScreenShot.csファイルに次のスクリプトを入力してください。

using System.Collections;
using UnityEngine;
using System.IO;
using System.Runtime.InteropServices;

public class ScreenShot : MonoBehaviour
{
    [DllImport("__Internal")]
    private static extern void SaveToAlbum(string path);

    IEnumerator SaveToCameraroll(string path)
    {
        // ファイルが生成されるまで待つ
        while(true)
        {
            if(File.Exists(path))
                break;            
            yield return null;
        }
        
        SaveToAlbum(path);
    }

    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
#if UNITY_EDITOR
#else
        string filename = "test.png";
        string path = Application.persistentDataPath + "/" + filename;

        // 以前のスクリーンショットを削除する
        File.Delete(path);

        // スクリーンショットを撮影する
        ScreenCapture.CaptureScreenshot(filename);
        
        // カメラロールに保存する
        StartCoroutine(SaveToCameraroll(path));
#endif
    }
}

このプログラムでは、上で紹介したCaptureScreenshotメソッドを使ってスクリーンショットを撮影しています。撮影したパスをPluginのSaveToAlbumメソッドに渡すことで、アプリ内にあるスクリーンショットをカメラロールにコピーしています。

注意点として、CaptureScreenshotはコールしたタイミングで画像を保存するわけではなく非同期に画像を保存します(ヤヤコシイ・・・)従って、画像の保存が完了するまでは、Pluginで画像をコピーするのを待つ必要があります。

これをやっているのがSaveToCamerarollコルーチンです。File.Existsメソッドを使って毎フレームスクショ画像が保存されたかをチェックして、保存されていればPluginを使って画像をカメラロールにコピーします。

Photo Library Additions Usage Descriptionを追加する

iOS11以降では、Photo LibraryにアクセスするためにはInfo.plistに「Privacy - Photo Library Additions Usage Description」を追加する必要があります。

これを設定し忘れると、フォトライブラリにアクセスしようとした時点でアプリがクラッシュします(クラッシュさせなくても、コンパイルエラーで良いと思うのですが・・・)

Unityでビルドしたプロジェクトファイルを開き、Info.plistに「Privacy - Photo Library Additions Usage Description」追加してからXcodeでビルドしましょう。

f:id:nn_hokuson:20180501201158j:plain:w600

これで、Unityで撮影したスクリーンショットを、iOSのカメラロールに保存することができるようになります。