ちょび日記

明日は明日の風が吹く

2016-10-19

StreamingAssetsのデータをAAssetManagerから取得する


UnityのStreamingAssetsに配置されたコンテンツはAndroidの場合apkのassets直下に配置されます。 御存知の通りapkはzipなので、Unityから取得する場合はWWW経由でアクセスする羽目になります。

とはいえWWWはいろいろ制御が面倒なので、NDK側からデータを直接とりたいなー、ということで よくわからないながらも調べてみました。

おおまかなステップ

  • JNI+C#を使ってAAssetManagerを取得
  • 保持したポインタを使ってAAssetManager経由でデータの読み取り

と、なるんですが自分はAndroidもJNIもよくわからん、という状態ですしそもそもNDK使って StreamingAssetsからデータ読もうなんて人はそんなにおらずちっとばかし時間がかかりました。

NDK側の作業

Android.mkにLOCAL_LDLIBS := -landroidを追加してAssetManagerを使えるようにしておきます。

ヘッダに以下のincludeを追加します。

#include <jni.h>
#include <android/asset_manager_jni.h>
#include <android/asset_manager.h>
#include <android/log.h>

ソースコードにこんな定義を追加しときます。

extern "C" void  SetupAssetManager(jobject assetManager)  
{  
    __android_log_print(ANDROID_LOG_DEBUG, "Unity", "JNI SetupAssetManager");
    mgr = AAssetManager_fromJava(jni_env, assetManager);  
    if(mgr == NULL)  
    {
        __android_log_print(ANDROID_LOG_DEBUG, "Unity", "AAssetManager is null");
        return ;  
    }
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)  
{
    // 初めてC++の関数がよばれたときにきそう
    __android_log_print(ANDROID_LOG_DEBUG, "Unity", "JNI OnLoad");
    vm->AttachCurrentThread(&jni_env, 0);
    return JNI_VERSION_1_6;
}

JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved)
{  
    return;  
}  

extern "C" void getStreamingContents(char* fileName)
{  
        if(mgr == NULL)
        {
            __android_log_print(ANDROID_LOG_DEBUG,"Unity","manager is null");
            return;
        }

        AAsset* asset = AAssetManager_open(mgr, fileName, AASSET_MODE_UNKNOWN);  
        if(asset == NULL)
        {
            __android_log_print(ANDROID_LOG_DEBUG,"Unity","asset is null");
            return;
        }

        int srcLen = AAsset_getLength(asset);
        // ここらへんは適当に変えてね!
        unsigned char* src = new unsigned char[srcLen];
        AAsset_seek(asset, 0, 0);
        AAsset_read(asset, (void*)src, srcLen);
        __android_log_print(ANDROID_LOG_DEBUG,"Unity","asset: %s", src);
        AAsset_close(asset);
}

C#側の作業

適当なタイミングでAssetManagerのObjectをC++側の関数に渡してポインタを保持しておきます。

IntPtr unity_player = (IntPtr)AndroidJNI.FindClass("com/unity3d/player/UnityPlayer");
IntPtr field_id = AndroidJNI.GetStaticFieldID(unity_player, "currentActivity", "Landroid/app/Activity;");
IntPtr activity = AndroidJNI.GetStaticObjectField(unity_player, field_id);

IntPtr activity_klass = AndroidJNI.GetObjectClass(activity);
IntPtr asset_func = AndroidJNI.GetMethodID(activity_klass, "getAssets", "()Landroid/content/res/AssetManager;");
jvalue[] asset_array = new jvalue[0];
IntPtr assetManager = AndroidJNI.CallObjectMethod(activity, asset_func, asset_array);
if (assetManager != null)
{
    SetupAssetManager(assetManager);
}

あとはC++側でgetStreamingContentsを好みの場所で呼んであげればok。

例えばStreamingAssets/example.txtを配置していたのであればgetStreamingContents(“example.txt”)で読み込めます。 上記例では中身をlogに書き出すだけなのでデータのハンドリングはよしなに変えて下さい。

と、こんなかんじでStreamingAssetsからデータを読み込む事ができるようになりました。 さっぱりJNIもAndroidもわからないので、NDKを使ってもっと楽にStreamingAssestsの中身が取れる方法があればtwitter等で教えてもらえると助かります。

でわでわ。



Copyright© 2016, chobie All rights reserved.