mizoguche.info

Unity の Android プラグインつくった

iOS / Android でツイートしたらコールバック貰える Unity のアセット探してたけどなかったのでつくった。

mizoguche/TweetSharer

デザインパターン厨としてはここで使わないとどこで使うって感じだったので Bridge パターンを使ってみた。

やっぱり偉い人たちが言うだけあって、かなりコードが見やすくなって気持ちいい。

Bridge パターン使わなかったら #if UNITY_ANDROID#elsif UNITY_IOS による精神汚染が激しくなってたと思われる。

iOS 対応は anchan828/social-connector を参考にした。

Android 対応

めんどくさい部分

Android でただただツイートを他アプリに任せるなら暗黙的インテント使えばいいだけの話やけど、今回はツイートしたのかキャンセルしたのかを知りたいので startActivityForResult で Activity 呼んで onActivityResult で受け取って、みたいなめんどくささがある。

Android Studio で jar をつくるよ

Android Studio のプロジェクトをつくる

リポジトリのルートが Unity プロジェクトのルートで、その直下に Androi Studio プロジェクトのディレクトリを置いた。

特にこれが良さそうという根拠はなかったけど、今のところこれが良さそう。理由は後述。

ビルドパスに Unity の classes.jar を追加する

Unity の Java クラスを参照したいので、 Unity の classes.jar を ビルドパスに追加する。

Unity で Android のネイティブプラグインの実装 - ローカル通知を送信する - nirasan’s tech blog

上記記事では Eclipse を使ってはるみたい。Android Studio では、build.gradledependencies

compile files('/Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/release/bin/classes.jar')

を追加すればよろしい(Unity 4.6.1 だと、上記記事と classes.jar のパスが違った)。

jar を書き出す

【Android】AndroidStudioでjarを出力する | AdMax Tech Blog

上記記事の clearJarmakeJar をいただいた。

ここで、makeJarinto

into('../../Assets/Plugins/Android/')

と指定すると、

./gradlew <modulename>:clean <modulename>:assembleDebug <modulename>:makeJar

とするだけで、Unity の Android プラグインのディレクトリにビルドしてくれるので便利。

Android Studio プロジェクトと Unity プロジェクトの場所が結果的に良かった。

java.lang.UnsatisfiedLinkError: No implementation found for void com.unity3d.player.UnityPlayer.UnitySendMessage

Unity でコールバックを受けようとしたら、下記のような例外が出た。

Caused by: java.lang.UnsatisfiedLinkError: No implementation found for void com.unity3d.player.UnityPlayer.UnitySendMessage(...)
  at com.unity3d.player.UnityPlayer.UnitySendMessage(Native Method)

UnitySendMessage Unsatisfied Link Error Native Method Not Found | Unity Community

One way that you can run into this problem is, if you have any logic that is triggered very early, such as in response to override onCreate() of your own custom Application class, and that results in calling UnitySendMessage, you might be trying to call Unity before Unity has actually loaded its native libraries. This will result in this crash. So you have to make sure to defer such calls until later (such as in response to the C# calling you and telling you that it’s ready, or other such schemes).

「めっちゃ早く呼びすぎやね! Unity がネイティブライブラリーを読み込む前に UnitySendMessage 呼んでるからクラッシュするんやわ!」(意訳)ということだそうで。

UnitySendMessage を呼んでも大丈夫か検証しないとダメっぽいのでやっつけで書いた。

private class TestLoadedTask extends AsyncTask<Void, Void, Void>{
    @Override
    protected Void doInBackground(Void... params) {
        boolean loading = true;
        while(loading){
            try {
                // test if Unity native library loaded
                UnityPlayer.UnitySendMessage(GAMEOBJECT, CALLBACK_METHOD, DAMMY_MESSAGE);
                loading = false;
            }catch(UnsatisfiedLinkError e){
                Log.i(TAG, "Unity is loading native libraries");
                try {
                  Thread.sleep(300);
                } catch (InterruptedException e1) {
                }
            }
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        UnityPlayer.UnitySendMessage(GAMEOBJECT, CALLBACK_METHOD, MESSAGE);
    }
}

たぶんタイムアウトとかした方が安全なんでしょうけども正直めんどくさかった。今は反省している。