2Dの物理演算ライブラリの「box2d」をandroid端末で動かしております。
言語はjavaです。
複数の物理オブジェクト(body)を生成しています。
また、body同士を結びつけるためにヒモやバネのようなオブジェクト(joint)を生成しています。
「Testing Box2D」のサンプルプログラムにコードを書き足して使用しております。
googleのplayストアはこちら
https://play.google.com/store/apps/details?id=pl.mg6.testing.box2d&hl=ja
サンプルコードはこちら
https://code.google.com/p/testing-box2d/
はじめに、物理演算BOX2Dで行いたい事は、観覧車のように中心を軸として回転する動作をさせるためです。
そのため、生成したBodyは観覧車のゴンドラのように、画面の中心を軸として回転します。
複数のbody同士では当たり判定がないように設定してあり、実際には重なり合うような状態にしてあります。
この複数のbodyの表示を、画面下にあるものほど最前面に表示させる処理を行いたいです。
簡単に処理ができればよかったのですが、そのような設定は見つかりませんでした。
そのため、「表示順を変更する処理」を実行させたいです。 (★下記にコード貼り付け★)
「表示順を変更する処理」では、
①すべてのbodyの座標を読み取る
②y軸の座標が小さい順に並び替え
③表示順を並び替えるため、「bodyとjointを再度生成する」
ことで実行させることにしました。
「bodyとjointを再度生成する」の処理は (★下記にコード貼り付け★)
すでに生成されているbodyを削除し、画面下のものから一つずつ再度生成していくものです。(早く生成されたbodyほど画面前面に表示されているため)
この処理自体はうまくできているのですが、この処理を入れた状態でbodyのjointを行うとエラーでアプリケーションが停止してしまいます。
何日も同じエラーで悩んでおります。
お力添え頂けますと幸いです。
エラーメッセージです。
09-29 02:57:05.778: E/AndroidRuntime(6811): FATAL EXCEPTION: main 09-29 02:57:05.778: E/AndroidRuntime(6811): java.lang.ArrayIndexOutOfBoundsException: index=13 length=13 09-29 02:57:05.778: E/AndroidRuntime(6811): at org.jbox2d.dynamics.Island.add(Island.java:423) 09-29 02:57:05.778: E/AndroidRuntime(6811): at org.jbox2d.dynamics.World.solve(World.java:976) 09-29 02:57:05.778: E/AndroidRuntime(6811): at org.jbox2d.dynamics.World.step(World.java:560) 09-29 02:57:05.778: E/AndroidRuntime(6811): at mori.ken.tab.breath001.box2d.PhysicsWorld.update(PhysicsWorld.java:1926) 09-29 02:57:05.778: E/AndroidRuntime(6811): at mori.ken.tab.breath001.box2d.Box2dActivity$3.run(Box2dActivity.java:461) 09-29 02:57:05.778: E/AndroidRuntime(6811): at android.os.Handler.handleCallback(Handler.java:587) 09-29 02:57:05.778: E/AndroidRuntime(6811): at android.os.Handler.dispatchMessage(Handler.java:92) 09-29 02:57:05.778: E/AndroidRuntime(6811): at android.os.Looper.loop(Looper.java:132) 09-29 02:57:05.778: E/AndroidRuntime(6811): at android.app.ActivityThread.main(ActivityThread.java:4123) 09-29 02:57:05.778: E/AndroidRuntime(6811): at java.lang.reflect.Method.invokeNative(Native Method) 09-29 02:57:05.778: E/AndroidRuntime(6811): at java.lang.reflect.Method.invoke(Method.java:491) 09-29 02:57:05.778: E/AndroidRuntime(6811): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) 09-29 02:57:05.778: E/AndroidRuntime(6811): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) 09-29 02:57:05.778: E/AndroidRuntime(6811): at dalvik.system.NativeStart.main(Native Method)
の部分より、該当箇所を抜粋します。
java.lang.ArrayIndexOutOfBoundsException: index=13 length=13`
m_jointCapacity=13
なのに対して、
m_jointCount=14
となっております。
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
■バグの推測
デバッグを行うことで、エラー場所は特定できました。
しかし、原因や解決策はまったくわかりません。
動的に変更されるjointの個数がずれてしまい、joint配列の範囲外のものを指定しているようです。
本来であれば動的にjointの個数に合わせてm_jointCapacityの数が変更されていたのですが、なぜか今回の処理を行うとエラーが発生してしまいます。
(bodyやjointを削除・生成を繰り返す過程が問題?)
以下、コードとなります。
※ここに記載してあるコードは、すべてPhysicsWorld.javaの変数・関数です。
Java
1 ///////////////////////////// 2 //「表示順を変更する処理」 3 ///////////////////////////// 4 5 //重なりを調節するために再生成する関数 6 public void ShowItem2Forward( ) { 7 8 //現在のbody(剛体・物体)のリストを取得 9 Body bodies = getWorld().getBodyList(); 10 11 //bodyが空でなければ 12 if (bodies != null) { 13 14 //bodyごとに設定したbodyId(整数)を、並び替えた取得し配列へ代入 15 int[] SortBodyList = GetSortedBodyList(); 16 17 //並び順が前回と同じか否かを取得する。(同じならTrue、異なるならFalse) 18 boolean ArraySameFlag = ArraySameCheck(SortBodyList,SortBodyListPrevious); 19 20 //もし、前回と同じなら 21 if (ArraySameFlag){ 22 //なにもしない 23 }else{ //前回と中身が異なるなら 24 //bodyの数だけ、一つずつ再生成する。 25 for (int iiiaa = 0 ; iiiaa < ItemNum ; iiiaa++){ 26 //順番通り再度生成する。bodyIdの順番を引き渡す 27 Body2Backward( SortBodyList[iiiaa] ); 28 } 29 30 //SortBodyListPrevious(一つ前のBodyList)に、いまのSortBodyListを代入 31 SortBodyListPrevious = SortBodyList; 32 33 } 34 35 } 36 37 }
Body2Backward( SortBodyList[iiiaa] );
の部分でエラーが起きるため、さらに詳しく掲載します。
(一部省略しております。)
Java
1 ///////////////////////////// 2 //「bodyとjointを再度生成する」 3 ///////////////////////////// 4 5 //アイテム順番号が引数として得られる(bodyの個数は6つなので、itemOrderにはItemの番号である0~5が入る。) 6 public void Body2Backward(int itemOrder) { 7 8 int NumNum = itemOrder; 9 10 ////////////////////// 11 //アイテムを削除する処理 12 ////////////////////// 13 14 //Jointを消す 15 world.destroyJoint( JointOfItem2Center[NumNum] ); //body番号(NumNum)ごとにJointを生成してあるため、そのJointを削除する。JointOfItem2Center[body番号]にはjointの情報が入っている。 16 JointOfItem2Center[NumNum] = null; 17 18 19 //Jointを消す 20 //同様の処理なので省略 21 // 22 23 24 //bodyを削除するまえに行う処理 25 //座標を取得する。(再度アイテムを生成するために、同じ座標を記録しておく) 26 Vec2 position = body[NumNum].getWorldCenter(); 27 //速度を取得する。 28 Vec2 velocity = body[NumNum].getLinearVelocity(); 29 30 31 32 //Bodyを消す 33 if (body[NumNum].m_userData != null) { 34 world.destroyBody( body[NumNum] ); 35 body[NumNum].m_userData = null; 36 body[NumNum] = null; 37 } 38 39 40 ////////////////////// 41 //全く同じアイテムを生成する処理 42 ////////////////////// 43 44 body[NumNum] = MakeBody( ItemName[NumNum] , (float) (position.x) , (float)(position.y),1,0,0); 45 46 //速度を割り当てる 47 body[NumNum].setLinearVelocity(velocity); 48 49 50 //生成したアイテムをjoinする (ここでエラーが起きる!!!) 51 JointOfItem2Center[NumNum] = JointBody(CenterPoint1,body[NumNum],"distance"); //CenterPoint1は、画面の中央に生成したbodyです。再度生成したbodyを画面の中央に紐付ける必要があるのですが、この処理を実行するとエラーがおきます。 52 53 54 //生成したアイテムをjoinする 55 //省略 56 // 57 58 59 60 61 62 }
■Bodyの生成やJointの生成を行うために関数を作ってあります。
●Bodyの生成処理
Java
1MakeBody (String ItemWhich,float x,float y ,int MoveAble ,int RotationAble ,int CollisionAble) { 2//String ItemWhich : どのアイテムを生成するか。(if文で判定して、画像や半径などを振り分けます。) 3//float x : 生成するx座標 4//float y : 生成するy座標 5//int MoveAble : 移動できるかのFlag 6//int RotationAble : Bodyを回転させるかのFlag 7//int CollisionAble : 衝突できるかのFlag 8~省略~ 9MakedBody.createFixture(fixtureDef); //この処理で実際に生成する 10return MakedBody; 11}
●Jointの生成処理
Java
1Joint JointBody(Body body1, Body body2 , String JointType ) { 2//Body body1 3//Body body2 4//String JointType 5~省略~ 6MyJoint = world.createJoint(Joint情報); //この処理で実際に生成する 7return MyJoint; 8}
■ご回答頂いた内容についての補足
JointOfItem2Centerという配列について、以下に詳細を記載致します。
PhysicsWorld.javaで使えるグローバル変数として定義しております。
現段階でのBodyの個数は6個です。(int ItemNum = 6; としました。)
Jointを削除する操作をしたいので、Joint型の配列に保存しました。
Java
1//グローバル変数として定義 2 3//アイテムの数だけ、Bodyを生成する。(識別して処理ができる) 4Body body[] = new Body[ItemNum]; 5 6//アイテムの数だけ、Jointを生成する。(識別して処理ができる) 7Joint JointOfItem2Center[] = new Joint[ItemNum]; //→Body(ゴンドラ)ごとに、画面の中心とのJoint情報を保存するため。(常にItemNum個で問題ないはずです。) 8 9Joint JointOfItem2Item[] = new Joint[ItemNum]; //→Body(ゴンドラ)ごとに、隣りのBodyとJointした情報を保存するため。(常にItemNum個で問題ないはずです。)
■現在
生成済みのBodyやJointを入れる配列が問題かと考えてテスト中です。
■おわりに
上手く変数の引き渡しなどができないため、グローバル変数を多用してしまっている節があります。
また、メモリの管理などの理解が足りないため、そのようなご指摘など頂けますと幸いです。
何卒よろしくお願いいたします。
■Island.classについて
Island.classの中身を記載いたします。
ライブラリに入っていた(?)ものだと思います。
.classの拡張子のファイルを扱うのは初めてで、内容を変更できないことなどに戸惑っています。
また、デバッグで呼び出されていることはわかったのですが、どこに保存されているのか、どこから呼び出されているのか がわかっておりません。
public void init(int bodyCapacity, int contactCapacity, int jointCapacity, ContactListener listener) {}
が呼び出された際に、「jointCapacity」などが引数として得られているようです。
ただ、どこで「jointCapacity」を決めてinit()が実行されているのでしょうか?
Island.class
一部抜粋
Java
1 2/* */ public class Island { 3/* */ public ContactListener m_listener; 4/* */ public Body[] m_bodies; 5/* */ public Contact[] m_contacts; 6/* */ public Joint[] m_joints; 7/* */ public Position[] m_positions; 8/* */ public Velocity[] m_velocities; 9/* */ public int m_bodyCount; 10/* */ public int m_jointCount; 11/* */ public int m_contactCount; 12/* */ public int m_bodyCapacity; 13//~~省略~~ 14 15/* */ public void init(int bodyCapacity, int contactCapacity, int jointCapacity, ContactListener listener) { 16/* 189 */ this.m_bodyCapacity = bodyCapacity; 17/* 190 */ this.m_contactCapacity = contactCapacity; 18/* 191 */ this.m_jointCapacity = jointCapacity; 19/* 192 */ this.m_bodyCount = 0; 20/* 193 */ this.m_contactCount = 0; 21/* 194 */ this.m_jointCount = 0; 22 23//~~省略~~ 24 25/* */ } 26/* */ } 27
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/09/29 11:19
2016/09/30 15:16