Unityで作成したAndroidアプリをAndroid向けに拡張した時にやったことメモ
はじめに
UnityからAndroidやiOS向けバイナリを無料で吐き出せるようになりました。
しかし、モバイルOSのネイティブ機能をフルで活かそうとすると幾つかそれぞれのプラットフォーム向けに拡張したくなる時があると思います。
その際に、それぞれのプラットフォーム向けにライブラリを作成しそのコードをJNI経由等で呼び出す方法があります。
// Androidの場合
AndroidJavaClass obj = new AndroidJavaClass("classへのフルパス");
obj.CallStatic("呼び出すメソッド名");
しかし、特にAndroid環境では、画面のライフサイクルに合わせた拡張がしたいという要望が高いので、何とかできないかと試してみました。
UnityPlayerNativeActivityとは
幾つか情報を調べると、Android環境下ではUnityPlayerNativeActivityと呼ばれるActivity(多分NativeActivityのサブクラス系)でUnityフレームワークが実行され更にその上でユーザーが作成したプログラムが動作するような作りになっているらしい。
Android 2.3以下だとUnityPlayerActivityがベースになるらしいが実機未確認
つまり、このベースとして実行されるUnityPlayerNativeActivityをうまくサブクラス化し、ライフサイクルに関係するメソッドをオーバーライドしてやれば好きなことができそうです。
実手順
1. Unityから基本となるバイナリの作成
まずは、Unityが吐き出すAndroidコードを捕まえないことにはどうにもならないので、UnityでAndroidバイナリを吐き出せるようにします。
また、この手順はWindows 8.1 、Unity 4.5.4環境下で試しています。
-
Add Currentをタップして、初期起動させたい画面を選択する(ここは不明)
-
Android 環境を選択してることを確認し、下のOther Settingsを開く
-
Bundle Identifierを変更する。これはAndroidの パッケージ名 と同じにする必要が有るので注意
-
Bundle Version 及び Bundle Version Code を変更する。これはそれぞれ、 versionName と versionCode に相当する。
-
Publishing Settingsを開いて証明書関係の設定をする ← 実際にはこの後の作業で再証明つけるからいらない気がする
-
設定が終わったら、Build Settingsウィンドウの Build ボタンを押してビルドする
-
失敗する場合は適度に直して作成する
2. 成果物の確認
上記の手順が終わり、無事APKファイルが作成されると、作業ディレクトリのルートにTempフォルダができているはず。
これは、Unity公式サンプルのShootingGameをビルドした直後のディレクトリ
このTempフォルダ内にStagingAreaというフォルダが存在し、この下に生成されたAndroid用コードが入っています。
StagingAreaの内部構造
3. 生成されたコードを拡張
2.でAndroid向けコードが生成されたので、このコードを元に拡張を行います。
幾つかネットを検索した限り、出回っている手順情報としてあったのは下の2つに大別されそうです。
- eclipseを使用し、このStagingAreaをライブラリ化してメインのプロジェクトにインポート 参考:UnityでAndroidの機能を拡張する2つの手法とは (3/3)
- この生成されたコードからeclipseでライブラリプロジェクトを作成し、jarファイルを生成し、それをインポート
eclipseでのインポート情報が多いのは、この出力されたディレクトリ構成がeclipseでのプロジェクトのフォルダ構成に添っているからだと思います。
Android Studioでのインポート方法
現在すでにAndroid開発はAndroid Studioに流れているので、可能であればASで動かせたほうが今後ハッピーです。
基本となるプロジェクトの生成
- Android Studioで New Projectを作成する。この時Package name:をUnityで設定したBundle Identifierと同じにする
- Minimum SDK もUnityのOther Settingsで設定したものと同じにする
- Blank Activityを選択し、Activityが1つ存在するプロジェクトを作成する
実行に必要なファイルのインポート
- app\src\mainディレクトリの下に assets ディレクトリを作成する
- 作成した assets ディレクトリ配下に、Unityで生成した StagingArea¥assets ディレクトリ以下を全てコピーする
- app\src\mainディレクトリの下に jniLibs ディレクトリを作成する
- 作成した jniLibs ディレクトリ配下に、Unityで生成した StagingArea¥libs ディレクトリ以下を全てコピーする
- app\src\main\resディレクトリの下にUnityで生成した StagingArea¥res ディレクトリ下の drawable ディレクトリ自体をコピーする
- app\libsディレクトリの下に、Unityをインストールしたディレクトリの Editor¥Data¥PlaybackEngines¥androidplayer¥release¥bin¥classes.jar をコピーする
ここまで終わると、だいたいこんなディレクトリ構造ができていると思います
xmlファイルのマージ
- StagingArea¥res¥values¥strings.xml の <string name="app_name">アプリ名</string> を app\src\main\res\values\strings.xml にマージする
- app\src\main\AndroidManifest.xml へ StagingArea\AndroidManifest.xml をマージします
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test.test.test"
android:installLocation="preferExternal"
android:theme="@android:style/Theme.NoTitleBar"
>
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<application
android:allowBackup="true"
android:icon="@drawable/app_icon"
android:label="@string/app_name">
<activity
android:name="test.test.test.MyActivity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"
android:label="@string/title_activity_my"
android:launchMode="singleTask"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<meta-data
android:name="unityplayer.UnityActivity"
android:value="true" />
<meta-data
android:name="unityplayer.ForwardNativeEventsToDalvik"
android:value="false" />
</activity>
</application>
<uses-feature android:glEsVersion="0x00020000" />
</manifest>
application->activityのandroid:nameが自分が作成したActivity名になっているかを注意してください。
Activityのサブクラス化
やっとやりたいことに追い付きました。
最初に自動生成されたActivityの中身を書き換えます。
注意点は、onCreateの中でsetContentViewを呼んではいけない点です。
package test.test.test;
import com.unity3d.player.UnityPlayerNativeActivity;
public class MyActivity extends UnityPlayerNativeActivity {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
}
}
// あとはお好きに
ここで、com.unity3d.player.UnityPlayerNativeActivity
が見つからなくて怒られる場合には、sync project with gradle filesボタンを押すと直ると思います。
4.実行
通常のAndroidアプリの実行方法で実行できる
最後に
本当であれば多分ライブラリモジュール化して取り込むのが正解な気もするんですが、起動Activityの関係とか考えると直取り込みのほうが個人レベルだと楽な気がします。
この後Unity側でアプリの変更を行っても、基本的には、
- Unityで更新後Androidビルド
- AS側の assets ディレクトリ以下を全削除
- Unityで生成した StagingArea¥assets ディレクトリ以下を assets ディレクトリ以下に全コピー
で済みます。
おまけ
Activityのサブクラスでゴニョゴニョできるってことはちょっと危ない橋も渡れるということで・・・
起動Activityをこんな感じにすると
package test.test.test;
import com.unity3d.player.UnityPlayerNativeActivity;
public class MyActivity extends UnityPlayerNativeActivity {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
View decor = this.getWindow().getDecorView();
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE);
}
}
}
// あとはお好きに
当然画面の座標軸系がずれるんでそこら辺は自分で要調整してになりますけど。