【Android】OpenGL ESとJBulletの3Dワールドにキューブ雨を降らせてみた【サンプル】

fallingCubeTop

はじめに


ベクトルの話が長いので一旦お休みし、OpenGL ES×JBulletのPhysXワールドサンプルを改良してみました。

実装したものはキューブ雨で、タッチで晴れと雨を切り替えることが可能です。

雨といっても水分のような軟体ではなく完全に剛体なので、中心に置いてある木箱に当たると吹き飛びます。質量が10kgで、高さ10メートルくらいから時速100キロで洗濯機が落ちてくると考えるとわかりやすいと思います。誰がわかるか。

プロジェクトはいつも通りGitさんに上げておきました。

もしよろしければ感想もコメントやメールより受け付けています。

gitPurobana

https://github.com/rashu404/purobana

具体的なサンプルの説明と仕組み


具体的な処理の流れは以下です。

  1. タッチイベントの発生と同時にWorldクラスfallingSwitch()を呼び出す
  2. fallingSwitch()でonDrawFrame()のbooleanスイッチをオンにする
  3. trueならばint型iTimeに加算演算が行われる
  4. iTimeの数値が8で丁度割り切れる時、キューブ雨がランダム地点に降る
  5. もう一度タッチすると雨が止む

今まで作ったLinearVelocityを使ったメソッド等を使った復習のようなものなので、特に難しいことはしていません。

mRenderer.fallingSwitch(b);
b = !b;
/* タッチでランダム地点にキューブ雨を降らせる */

これはboolean型の変数bを呼びだした直後にtrueとfalseを切り替えています。

public void fallingSwitch(boolean rainSwitch){
	this.rainSwitch = rainSwitch;
}

引数として受け取ったrainSwitchを同名のメンバ変数に代入します。

if(rainSwitch == true){
	if(iTime % 8 == 0){
		fallingCube();
	}

	iTime += 1f;
}

trueの間、常にiTimeが加算されていき、iTime ÷ 8の結果が0になった時だけfallingCube()が呼ばれます。

常に降り続けるとAndroidのメモリ的に耐えられないのと、見た感じがめまぐるしいことになるので、間隔を空けて定期的に呼び出しています。

public void fallingCube(){		
	if(shootNum == -1)
		shootNum = 0;
	else if(shootNum >= 0 && shootNum < bullets - 1)
		shootNum++;
	else {
		for(int i = 0; i < bullets - 1; i++){
			mDynamicsWorld.removeRigidBody(mCubeBullets[i].getRigidBody());
			mCubeBullets[i] = null;
		}
		shootNum = 0;

	}

	Random random = new Random();

	float fallX = random.nextFloat() * 20.f;
	float fallY = random.nextFloat() * -10.f;
	float fallZ = random.nextFloat() * 10.f + 1f;

	if(random.nextBoolean())
		fallX = -fallX;
	if(random.nextBoolean())
		fallY = -fallY;

	mCubeBullets[shootNum] = new Cube(mDynamicsWorld, new Vector3f(fallX, fallY, fallZ));
	mCubeBullets[shootNum].shootCube(new Vector3f(0, 0, -100));
}

これが核の部分である、fallingCube()です。

最初に今あるキューブ雨の数を確認し、既定数より超えるまでワールド上にキューブを増やし続け、超えるとRigidBodyとインスタンスを削除してメモリ消費を削減します。

後半のRandomクラスですが、nextFloat()で0.1~1.0までの数値をランダムで取り出し、Xなら20倍、Yなら-10倍しています。

共に最大値を取り出せた場合はX=20, Y= -10となります。

Zは0以下になると地面の真下に生成されてしまうことに注意しましょう。

ただこのままでは、かなり雨の範囲が狭まってしまいますので、nextBoolean()でtrueの時、符号を反転させると広範囲に降ってくるようになります。

最後にいつものshootCube()に槍のように刺さるベクトルを持たせれば完成です。

fallingCubeVec

 

ちょっとやってみたいこと


これで雨っぽいことはできました。

でもこれではつまらないので、なんとなくやってみたくなるようなことをいくつか挙げてみます。

  • 雨が降る時だけワールドを暗くする

darkSwitch()なんてものがACTION_DOWNにあるので同時にやってみたり。

  • 風を加えてみる

雨は常に地面と垂直に降り続けるものではないです。

風の影響を受けて斜めに振る時もあり、台風レベルだと平行に流れていくのです。

  • ワールドにオブジェクトを増やしてみる

敵キャラとか歩かせてみたい。そんな好奇心。

最後に


以上で解説は終了です。

PhysXワールドサンプルも最初に比べてかなり充実してきました。

こんなの追加してほしい等あればぜひぜひこの記事のコメント欄より受け付けています!