上級者向け Unity モバイルスクリプティング
Android スプラッシュスクリーンのカスタマイズ

Android 用のプラグインをビルド

このページは Android の プラグイン について記述します。

Android のプラグインをビルドする

Android のプラグインをビルドするには、まずは Android NDK を入手し、共通ライブラリ( shared library )を構築するために必要な手順を理解しておいてください。

プラグインを実装するのに C++ (.cpp) を使用する場合は、名前修飾の問題 を避けるために、関数を C リンケージで宣言してください。

extern "C" {
  float FooPluginFunction ();
}

C\# からプラグインを使用する

ビルドしたら、共通ライブラリを Assets->Plugins->Android にコピーしてください。以下の例のように、Unity は C# スクリプトで関数を定義するときに名前でプラグインを参照します。

[DllImport ("PluginName")]
private static extern float FooPluginFunction ();

PluginName にはファイル名のプレフィックス (‘lib’) や拡張子 (‘.so’) を含めないように気を付けてください。 すべてのネイティブコードメソッドを、追加の C# コードのレイヤーでラップします。このコードは、 Application.platform を確認し、アプリケーションを実際のデバイスで実行するときにのみ、ネイティブメソッドを呼び出します。エディターで実行するときは、ダミーの値が C# コードから返されます。プラットフォーム依存のコードコンパイルを制御するためには、プラットフォームの #define を使用します。

Android ライブラリプロジェクト

You can drop pre-compiled Android library projects into the Assets->Plugins->Android folder. Pre-compiled means all .java files must have been compiled into jar files located in either the bin/ or the libs/ folder of the project. AndroidManifest.xml from these folders will get automatically merged with the main manifest file when the project is built.

詳細については Android Library Projectsを参照してください。

プラグインの配置

For cross platform deployment, your project should include plugins for each supported platform (ie, libPlugin.so for Android, Plugin.bundle for Mac and Plugin.dll for Windows). Unity automatically picks the right plugin for the target platform and includes it with the player.

特定の Android プラットフォーム (armv7、x86) では、ライブラリ (lib*.so) を以下のように配置する必要があります:

Assets/Plugins/Android/libs/x86/

Assets/Plugins/Android/libs/armeabi-v7a/

Java のプラグインを利用する

Android のプラグインは、Android OS と連動することができます。

Android の Java Plugin をビルドする

There are several ways to create a Java plugin but the result in each case is that you end up with a .jar file containing the .class files for your plugin. One approach is to download the JDK, then compile your .java files from the command line with javac. This will create .class files which you can then package into a .jar with the jar command line tool. Another option is to use the Eclipse IDE together with the ADT.

注意: Unity は Java プラグインを JDK1.6 で作成されていることを想定しています。もし 1.7 を使用した場合はコンパイラのコマンドオプションとして “-source 1.6 -target 1.6” を含めなければいけません

ネイティブコードから Java プラグインを使用する

Java プラグイン (.jar) をビルドしたら、それを Unity プロジェクト内の Assets->Plugins->Android フォルダーにコピーします。Unity はクラスファイルと Java コードの残りの部分をパッケージ化し、Java Native Interface (JNI) を使用してアクセスします。JNI は Java からネイティブコードを呼び出すときと、ネイティブコードから Java (または JavaVM) に更新するときに使用されます。

ネイティブ側から Java コードを見つけるためには、Java VM にアクセスする必要があります。そのアクセスは下のような C/C++ライクなコードの記述で簡単に追加することができます:

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  JNIEnv* jni_env = 0;
  vm->AttachCurrentThread(&jni_env, 0);
  return JNI_VERSION_1_6;
}

C/C++から Java を使い始めるために必要なことはこれで全部です。その他にも JNI を使用すれば、次の例に示すように constructor(<init>)メソッドを含むクラス定義を解決したり、新しいオブジェクトのインスタンスを作成したりすることが可能です。JNI の説明は完全にこのドキュメントの範囲を超えているので、割愛します。:

jobject createJavaObject(JNIEnv* jni_env) {
  jclass cls_JavaClass = jni_env->FindClass("com/your/java/Class");         // find class definition
  jmethodID mid_JavaClass = jni_env->GetMethodID (cls_JavaClass, "<init>", "()V");      // find constructor method
  jobject obj_JavaClass = jni_env->NewObject(cls_JavaClass, mid_JavaClass);     // create object instance
  return jni_env->NewGlobalRef(obj_JavaClass);                      // return object with a global reference
}

ヘルパークラスを利用して Java プラグインを使用する

AndroidJNIHelperAndroidJNI を使えば、比較的簡単に JNI を使用することができます。

AndroidJavaObjectAndroidJavaClass は、多くのタスクを自動化し Java の呼び出しをより高速で行うためにキャッシュを使用します。 AndroidJavaObjectAndroidJavaClassAndroidJNIAndroidJNIHelper を組み合わせて構築されていますが、独自のロジック(オートメーション処理のため)も多くあります。Java クラスの静的なメンバーにアクセスするために、これらのクラスの ‘静的’ バージョンも備わっています。

AndroidJNI クラスの関数を通して raw の JNI を利用するか、 AndroidJNI を設定して AndroidJNIHelper を使用するか、やがては、最大限の自動化と便宜化のために AndroidJavaObject/AndroidJavaClass を使用するか、用途にあったものをどれでも選択できます。

UnityEngine.AndroidJNI は(上記の) C 言語から JNI を参照するためのラッパーです。このクラスに含まれているメソッドはすべての Java ネイティブインターフェースに対応する static なメソッドを持っています。UnityEngine.AndroidJNIHelper は次のレベルで使用されたヘルパー関数を提供していますが、しかし特殊なケースに対応するためいくつかの関数は puhlic メソッドとして公開しています。

UnityEngine.AndroidJavaObjectUnityEngine.AndroidJavaClass のインスタンスは Java 側の java.lang.Class (またはそのサブクラス)のインスタンスに対応しています。これらは基本的に 3 つの機能を提供します。

  • メソッドの呼び出し
  • 変数の値を取得
  • 変数の値を設定

Call は大きく 2 つのカテゴリに分けることができます: ‘void’ 関数を Call する場合と、Call が void 以外の型を返すタイプの場合です。ジェネリックの型は、void 以外の型を返す関数に使用されます。GetSet は常に field 型を示すジェネリック型を取得します。

例 1

//The comments describe what you would need to do if you were using raw JNI
 AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); 
 // jni.FindClass("java.lang.String"); 
 // jni.GetMethodID(classID, "<init>", "(Ljava/lang/String;)V"); 
 // jni.NewStringUTF("some_string"); 
 // jni.NewObject(classID, methodID, javaString); 
 int hash = jo.Call<int>("hashCode"); 
 // jni.GetMethodID(classID, "hashCode", "()I"); 
 // jni.CallIntMethod(objectID, methodID);


Here, we’re creating an instance of java.lang.String, initialized with a string of our choice and retrieving the hash value for that string.

AndroidJavaObject のコンストラクターは少なくとも 1つのパラメーター - インスタンスをコンストラクトしたいクラスの名前 - を取得します。クラス名以降の任意のパラメーターは、コンストラクターがオブジェクトを呼び出すのに使用されます。この場合は文字列 “some_string” です。次の hashCode() への Call は ‘int’ を返します。これが、Call 関数に対しジェネリック型のパラメーターを使用する理由です。

注意: ドット表記法を使用してネストされた Java クラスをインスタンス化することはできません。インナークラスは $ セパレーターを使用してください。それはドット (.) とスラッシュ (/) どちらの形式でも使用できます。そのため、android.view.ViewGroup$LayoutParamsandroid/view/ViewGroup$LayoutParams はどちらも、 LayoutParams クラスが ViewGroup クラスにネストされている場合に使用できます。

例 2

このプラグインサンプルは、現在のアプリケーションのキャッシュディレクトリを取得する方法を示します。この方法はプラグイン無しで C# から使用することができます。

 AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); 
 // jni.FindClass("com.unity3d.player.UnityPlayer"); 
 AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); 
 // jni.GetStaticFieldID(classID, "Ljava/lang/Object;"); 
 // jni.GetStaticObjectField(classID, fieldID); 
 // jni.FindClass("java.lang.Object"); 

 Debug.Log(jo.Call<AndroidJavaObject>("getCacheDir").Call<string>("getCanonicalPath")); 
 // jni.GetMethodID(classID, "getCacheDir", "()Ljava/io/File;"); // or any baseclass thereof! 
 // jni.CallObjectMethod(objectID, methodID); 
 // jni.FindClass("java.io.File"); 
 // jni.GetMethodID(classID, "getCanonicalPath", "()Ljava/lang/String;"); 
 // jni.CallObjectMethod(objectID, methodID); 
 // jni.GetStringUTFChars(javaString);


このケースではまず、AndroidJavaObject ではなく AndroidJavaClass で開始します。なぜなら、新しいオブジェクトを作成する (Android UnityPlayer によってインスタンスが自動的に作成されます) よりもむしろ com.unity3d.player.UnityPlayer の静的メンバーににアクセスしたいためです。そして、静的フィールドの “currentActivity” にアクセスします。ただし、今回はジェネリックのパラメーターとして AndroidJavaObject を使用します。これは、実際のフィールドの型 (android.app.Activity) が java.lang.Object のサブクラスであり、すべての プリミティブでない型AndroidJavaObject としてアクセスされなければならないからです。なお文字列は例外的に、それが java のプリミティブ型ではないにもかかわらず、直接アクセスすることができます。

その後は、getCacheDir() を通して Activity をスキャンし、キャッシュディレクトリを示すファイルオブジェクトを取得し、getCanonicalPath() を呼び出し、文字列を取得するだけです。

もちろん、最近は Unity がアプリケーションのキャッシュフォルダー( Application.temporaryCachePath )とファイルのフォルダーパスへ( Application.persistentDataPath )のアクセスをサポートしているので、キャッシュディレクトリをプラグインから取得する必要はありません。

例 3

最後は、UnitySendMessage を使用して Java からスクリプトコードにデータを渡す方法についてです。

using UnityEngine; 
public class NewBehaviourScript : MonoBehaviour { 

    void Start () { 
        AndroidJNIHelper.debug = true; 
        using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { 
            jc.CallStatic("UnitySendMessage", "Main Camera", "JavaMessage", "whoowhoo"); 
        } 
    } 

    void JavaMessage(string message) { 
        Debug.Log("message from java: " + message); 
    }
} 


Java クラスの com.unity3d.player.UnityPlayer には、ネイティブサイドにある iOS の UnitySendMessage に相当する UnitySendMessage があります。Java でこれを使用して、スクリプトコードにデータを渡すことができます。

この例では、Java 側でメッセージを中継スクリプトから直接呼び出しを行なっています。native/Unity コードの “Main Camera” オブジェクトにメッセージにコールバックし、そのオブジェクトが持っている “JavaMessage” メソッドを呼び出します。

Unity で Java プラグインを使用するベストプラクティス

このセクションは、主に JIN、Java、Android に関する広範囲な経験を持っていない人を対象にしています。そのような場合、AndroidJavaObject/AndroidJavaClass を使用して Unity から Java にアプローチしていると仮定されます。

最初に注意すべきことは AndroidJavaObjectAndroidJavaClass で行う操作はどれも (raw の JNI アプローチと同様に) 計算処理的に高負荷ということです。そのため、パフォーマンスやコードの可読性を維持するためにマネージドコードと、ネイティブ/Java コード間の転換を最小限にすることをお勧めします。

実際の作業をすべて Java 関数で行い、AndroidJavaObject / AndroidJavaClass を使用してその関数と通信し、結果を得ることも可能です。ただし、注意しなくてはならないのは、JNI helper クラスはパフォーマンスを向上させるため、できるだけ多くのデータを蓄える性質があるということです。

//The first time you call a Java function like 
AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); // somewhat expensive
int hash = jo.Call<int>("hashCode"); // first time - expensive
int hash = jo.Call<int>("hashCode"); // second time - not as expensive as we already know the java method and can call it directly


Mono ガベージコレクタは、作成されたすべての AndroidJavaObjectAndroidJavaClass のインスタンスを使用後すぐに開放する必要があります。ただし、それらを using(){} ステートメント内に置き、できるだけ早く削除されるように確実にしておきます。こうしないと、インスタンスがいつ破棄されるかわかりません。なお、 AndroidJNIHelper.debug を true に設定すると、デバッグ出力でガベージコレクタの活動記録を見られます。

//Getting the system language with the safe approach
void Start () { 
    using (AndroidJavaClass cls = new AndroidJavaClass("java.util.Locale")) { 
        using(AndroidJavaObject locale = cls.CallStatic<AndroidJavaObject>("getDefault")) { 
            Debug.Log("current lang = " + locale.Call<string>("getDisplayLanguage")); 

        } 
    } 
}


また、直接 .Dispose() 関数を呼び出し Java オブジェクト残留がないことを確実にできます。実際の C# オブジェクトは少し長く保留されるかもしれませんが、そのうち Mono のガベージコレクションで処理されます。

UnityPlayerActivity Java コードの拡張

Unity Android では、標準の UnityPlayerActivity クラス (iOS の AppController.mm のような Android 上で Unity プレイヤーを動かすための主要な Java クラス) を継承することができます。

アプリケーションは Android OS と Unity Android 間で連携するどんな処理でも、また、全部をオーバーライドできます。これは、UnityPlayerActivity から派生する新しい Activityを作成することにより可能です。UnityPlayerActivity.java は、Mac の場合は /Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/src/com/unity3d/player で、Windows の場合は通常 C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\src\com\unity3d\player にあります。

To do this, first locate the classes.jar shipped with Unity Android. It is found in the installation folder (usually C:\Program Files\Unity\Editor\Data (on Windows) or /Applications/Unity (on Mac)) in a sub-folder called PlaybackEngines/AndroidPlayer/Variations/mono or il2cpp/Development or Release/Classes/. Then add classes.jar to the classpath used to compile the new Activity. The resulting .class file(s) should be compressed into a .jar file and placed in the Assets->Plugins->Android folder. Since the manifest dictates which activity to launch it is also necessary to create a new AndroidManifest.xml. The AndroidManifest.xml file should also be placed in the Assets->Plugins->Android folder (placing a custom manifest completely overrides the default Unity Android manifest).

新しい activity は、OverrideExample.java のようになります。

package com.company.product;

import com.unity3d.player.UnityPlayerActivity;

import android.os.Bundle;
import android.util.Log;

public class OverrideExample extends UnityPlayerActivity {

  protected void onCreate(Bundle savedInstanceState) {

    // call UnityPlayerActivity.onCreate()
    super.onCreate(savedInstanceState);

    // print debug message to logcat
    Log.d("OverrideActivity", "onCreate called!");
  }

  public void onBackPressed()
  {
    // instead of calling UnityPlayerActivity.onBackPressed() we just ignore the back button event
    // super.onBackPressed();
  }
}

そして、これは対応する AndroidManifest.xml の記述です。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product">
  <application android:icon="@drawable/app_icon" android:label="@string/app_name">
    <activity android:name=".OverrideExample"
             android:label="@string/app_name"
             android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
  </application>
</manifest>

UnityPlayerNativeActivity

It is also possible to create your own subclass of UnityPlayerNativeActivity. This has much the same effect as subclassing UnityPlayerActivity, but with improved input latency. Because touch/motion events are processed in native code, Java views do normally not see those events. There is, however, a forwarding mechanism in Unity which allows events to be propagated to the DalvikVM. To access this mechanism, you need to modify the manifest file as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product">
  <application android:icon="@drawable/app_icon" android:label="@string/app_name">
    <activity android:name=".OverrideExampleNative"
             android:label="@string/app_name"
             android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
  <meta-data android:name="android.app.lib_name" android:value="unity" />
  <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
  </application>
</manifest> 


“.OverrideExampleNative” activity に 2 つの meta-data が追加されたことに注意してください。一つ目の meta-data は libunity.so を利用するための命令です。2 つ目の meta-data は UnityPlayerNativeActivity のカスタムサブクラスに渡すイベントを有効にします。

ネイティブプラグインのサンプル

ネイティブプラグインの簡単な例は、ここ を参照してください。

This sample demonstrates how C code can be invoked from a Unity Android application. The package includes a scene which displays the sum of two values as calculated by the native plugin. Please note that you will need the Android NDK to compile the plugin.

上級者向け Unity モバイルスクリプティング
Android スプラッシュスクリーンのカスタマイズ