【Unity】iOSネイティブで処理後コールバックを受け取る


コーディング作業

Unityでアプリ開発をするとき、各OSのデフォルトの機能を使うためにNativePluginを使用することがあります。

この記事ではUnityからiOSネイティブを呼び出し、処理後にUnityに処理結果を返す方法を記載します。

UnityでScriptを書きますのでXcodeで編集せずUnityで完結します。
例として「UnityだけでiOSのアルバムへ保存の機能を実装」を行います。

iOSプラグインの作成(ObjC)

プラグインの作成はXcodeを通じてiOSで動作する言語で処理を記載し、Unityから呼び出せるようにします。
ここではObjCを使用しています。

C#(Unity)→ C++ → ObjC(Xcode)

NativePluginの作成

ファイルの拡張子は.mmを使用し、Objective-c++にします。
これでObjCメソッドをextern "C" { }で囲むことでC++の処理にできます。

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

//指定したパスの画像をカメラロールに保存する。
extern "C" {
    void albumSave(const char* path, const char* gameObjectName, const char* callbackMethodName) {
        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) {
            // 書き込み終了後、Unity側へコールバック。
            UnitySendMessage(gameObjectName, callbackMethodName, [error.description UTF8String]);
        }];
    }
}

albumSaveメソッドはiOSでアルバムに画像を保存するための処理を行います。

Codeの中で重要なのはUnitySendMessage()です。
UnitySendMessageはUnityのGameObject名とメソッド名を渡すことで、指定したGameObjectにアタッチ(追加)されているScriptから該当するメソッド名のメソッドを呼び出します。
つまり、NativePluginからUnity側へコールバックします。

UnitySendMessageのコールバック情報をPluginのメソッドの引数にすることでUnity側からコールバックの受け取り先を指定できます。

作成したPluginはUnityのAssetsフォルダ以下のPlugins/iOS/の下に追加してください。

以下はNativePlugin作成後に発生するエラーです。

Unityで動作しない

NativePluginは作成した環境でしか動作しないので今回だとiOSでしか動作しません。
Unityで実行時は呼ばないようにし、Unity環境では別の処理を実装してください。

No matching function for call to ‘UnitySendMessage’

UnityでBuild後にXcodeで実行できないことがある。
UnitySendMessageの引数がnilの場合発生しているのでnilチェックを行ってください。

SendMessage: object not found!

コールバック先が存在しないエラーです。
UnitySendMessageの第一引数のGameObject名と第二引数のメソッド名が正しいか確認してください。

Unity側の処理(C#)

Unityでは作成したNativePluginの呼び出しと、コールバックの受け取りを行います。

NativePluginの呼び出し

iOSAlbumSaveというクラスを作成しました。
saveAlbumメソッドに画像のパスとコールバックを指定し、NativePluginを呼び出します。

using UnityEngine;
using System;
using System.IO;
using System.Collections;
#if  UNITY_IPHONE && !UNITY_EDITOR
using System.Runtime.InteropServices;
#endif

public class iOSAlbumSave : MonoBehaviour {
#if  UNITY_IPHONE && !UNITY_EDITOR
    [DllImport("__Internal")]
    private static extern void albumSave(string path, string gameObjectName, string callbackMethodName);
#endif
    private string imagePath = null;

    public delegate void albumSaveCallback(bool success, string error);
    private albumSaveCallback saveCallback = null;

    public void saveAlbum (string path, albumSaveCallback callback) {
        if (callback != null) {
            if (path != null && path != "") {
                imagePath = path;
                saveCallback = callback;
#if  UNITY_IPHONE && !UNITY_EDITOR
                //iOS実機ではプラグインの使用
                if (File.Exists(imagePath)) {
                    //画像をカメラロールへ保存する。
                    albumSave(path, gameObject.name, "finishSaveAlbum");
                }
#else
                //iOS実機以外ではcallbackへ素通りするようにする
                saveCallback (true, null);
#endif
            } else {
                callback (false, "IncorrectPath");
            }
        } else {
            Debug.Log ("callback not found");
        }
    }

    /* コールバック処理 */
}

NativePlugin関連の処理はif UNITY_IPHONE && !UNITY_EDITORで囲むことでUnity実行中、もしくはiOS以外の時は動作しないようにしています。

[DllImport("__Internal")]はC++ライブラリを使用するための宣言です。
NativePluginのメソッドをprivate static externで指定することで参照先を作成します。
これでUnity上でNativePluginのメソッドを呼び出すように記載しても参照エラーが起きません。
実行時のXcode上では参照先が実在するのでNativePluginが呼び出されます。

上記で指定しているように保存にはパスを使用しています。
Unityで一度ローカルに画像を保存することでパスで渡すことが可能です。

NativePluginからのコールバックの受け取り

上のiOSAlbumSaveクラスにコールバックの受け取り先を追加しました。

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

public class iOSAlbumSave : MonoBehaviour {

    /* NativePluginの呼びだし処理 */

    // カメラロール保存後、ネイティブ側から呼び出される。
    void finishSaveAlbum (string errorDescription) {
        if (string.IsNullOrEmpty(errorDescription)) {
            //保存した画像は削除
            System.IO.File.Delete(imagePath);

            saveCallback (true, null);
        } else {
            saveCallback (false, errorDescription);
        }
    }
}

一応別クラスでの受け取りも可能です。その場合はUnitySendMessageの引数が正しいか確認してください。

メソッド名も自由に指定できますが、UnitySendMessageの引数とコールバックメソッドの引数がstringであることを確認してください。

あとはUnityからXcode向けにビルドし、iOSで動作確認できます。

UnautholizedAccessException

iOSで実機確認中にこのエラーが出る場合はアルバムに保存したいファイルのパスがiOSからアクセスできない可能性があります。

Leave a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です