Game Physics Simulation
以下のエントリーが詳しいです。今回はこちらで公開されているサンプルを実行してみました。
OpenGLによる3D描画とBulletによる物理演算の縫合メモ – 麗ちゃんとママの実験ノート
自分でビルドしてみようと思ってハマったポイントをいくつか書いておきます。ちなみに元のサンプルは何も問題ないです。余計なことをして個人的にハマっただけです。
bulletのバージョン
bulletのライブラリのバージョンが新しくなっているのでAndroid.mkは更新する必要があります。
bullet – Bullet is a professional free 3D Game Multiphysics Library – Google Project Hosting
僕はbullet-2.81-rev2613.zipをダウンロードしました。
jniフォルダにsrcの中身を展開します。Makefile.amを開いてリストを確認します。今回はBulletMultiThreadedを除いた、拡張子cppのファイルのリストを全部コピーして、Android.mkに貼り付けました。
ヘッダファイルは作りなおした方が良さそうです
作りなおすとヘッダファイルの内容が少し増えます。
もしくはサンプル通りの名前空間でクラスファイルを作りましょう。
後述するUnsatisfiedLinkErrorの関係で、正確なメソッド名がわからなくて困っていました。
javahコマンドで作成できます。
C:\Users\test\Documents\workspace\BulletTest>javah -classpath bin/classes -o jni/info_play_smart_android_bullettest_Cube.h info.play_smart.android.bullettest.Cube
クラスパスが間違っていると以下のようなエラーになります。
C:\Users\test\Documents\workspace\BulletTest>javah -classpath bin -o jni/info_play_smart_android_bullettest_Cube.h info.play_smart.android.bullettest.Cube
エラー:info.play_smart.android.bullettest.Cube にアクセスできません。
info.play_smart.android.bullettest.Cube のクラスファイルが見つかりません
javadoc: エラー - クラス info.play_smart.android.bullettest.Cube が見つかりません。
Error: コマンド行でクラスが指定されませんでした。-help で確認してください。
ただ、引数の名前と数が変わってしまうので、Cube.cppファイルから引数の部分だけコピーしてヘッダファイルを更新しました。
名前空間とアンダースコア
名前空間にアンダースコアが含まれるクラスを作っていたので、以下のようなエラーが発生しました。
01-05 12:56:43.351: W/dalvikvm(20100): threadid=11: thread exiting with uncaught exception (group=0x40c5d300)
01-05 12:56:43.390: E/AndroidRuntime(20100): FATAL EXCEPTION: AsyncTask #1
01-05 12:56:43.390: E/AndroidRuntime(20100): java.lang.RuntimeException: An error occured while executing doInBackground()
01-05 12:56:43.390: E/AndroidRuntime(20100): at android.os.AsyncTask$3.done(AsyncTask.java:299)
01-05 12:56:43.390: E/AndroidRuntime(20100): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
01-05 12:56:43.390: E/AndroidRuntime(20100): at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
01-05 12:56:43.390: E/AndroidRuntime(20100): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
01-05 12:56:43.390: E/AndroidRuntime(20100): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
01-05 12:56:43.390: E/AndroidRuntime(20100): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
01-05 12:56:43.390: E/AndroidRuntime(20100): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
01-05 12:56:43.390: E/AndroidRuntime(20100): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
01-05 12:56:43.390: E/AndroidRuntime(20100): at java.lang.Thread.run(Thread.java:856)
01-05 12:56:43.390: E/AndroidRuntime(20100): Caused by: java.lang.UnsatisfiedLinkError: Native method not found: info.play_smart.android.bullettest.Cube.simulate:(Linfo/play_smart/android/bullettest/Cube;)V
01-05 12:56:43.390: E/AndroidRuntime(20100): at info.play_smart.android.bullettest.Cube.simulate(Native Method)
01-05 12:56:43.390: E/AndroidRuntime(20100): at info.play_smart.android.bullettest.Cube.simulate(Cube.java:75)
01-05 12:56:43.390: E/AndroidRuntime(20100): at info.play_smart.android.bullettest.Cube$AsyncTestTask.doInBackground(Cube.java:69)
01-05 12:56:43.390: E/AndroidRuntime(20100): at info.play_smart.android.bullettest.Cube$AsyncTestTask.doInBackground(Cube.java:1)
01-05 12:56:43.390: E/AndroidRuntime(20100): at android.os.AsyncTask$2.call(AsyncTask.java:287)
01-05 12:56:43.390: E/AndroidRuntime(20100): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
01-05 12:56:43.390: E/AndroidRuntime(20100): ... 5 more
JNIの呼び出しは
JNIEXPORT void JNICALL Java_名前空間_クラス名_関数名
で定義された関数を呼び出します。
.(ドット)は_(アンダースコア)に変換します。
今回、名前空間にinfo.play_smartを使ったのでinfo_play_smartとしていたのですが、それが間違いでした。
すでにあるアンダースコアは「_1」に変換しておく必要があります。
この辺は上記ヘッダファイルを作ったときに初めて知りました。(ヘッダファイル中に記述されるので)
名前空間を元のサンプルに合わせておけばもっと楽にできたので残念です。
一応これで動くようになりました。
LuaかSquirrelとバインドして動くようにできたら便利だろうなぁ。誰か作らないかなぁ。