Box2D開発中バージョンの新機能について

こんにちはtaroです。

Box2Dといえば、PaperVision3D(PV3D)の同様下位バージョンとの互換性が無いこと、 PV3Dよりさらにドキュメントが無いことで有名ですが、今回はBox2Dで導入されようとしているb2Controllerの機能について ご紹介しようと思います。まだリリースされていないバージョンですので、リリース時にまた 今回の記事の内容とBox2Dの仕様が変わる可能性があります。また、バグフィックス中の部分もあるかと思いますので、 その辺ご容赦頂きながら、「この機能Box2Dに欲しかったかも・・・」みたいな感じでご覧いただければ、と思います。

開発中のBox2Dを入手

まず、この記事でご紹介しているバージョンのBox2Dの入手方法からご説明いたします。 WindowsではTortoiseSVNのようなソフトを使って、 こちらのサイトで紹介されている方法等をご参考にBox2DのSourceForgeのSVNリポジトリhttps://box2dflash.svn.sourceforge.net/svnroot/box2dflashに接続し、Box2Dの開発版を入手します。また、 Macではターミナルから適当な階層にいき

svn co https://box2dflash.svn.sourceforge.net/svnroot/box2dflash .

とすればソースを入手することが出来ます。Sourceというフォルダ内にBox2Dというフォルダが入っていますがこちらが開発版のソースコードになります。恐らく、まだ絶賛開発中なのだと思いますが、Examples内のサンプル・コードはコンパイルが通りません(笑)[2009/09/08現在]。

Box2D.Dynamics.b2Controller

現在のBox2D ver2.0.2ではユーザが勝手に画面の中心に向かう重力みたいなことを定義するには、少しBox2Dの中身を理解しないといけませんが、b2Controllerの拡張をすれば、容易に出来、また同じ効果を使いまわすことも簡単になります。まずは、サンプルを見てみましょう。いつも通りwonderflでとはいきません。 こういったブログ記事を書くときにwonderflって凄く便利なサービスだなーと思わされます。swfへの直リンクで見たほうがリーロードできるので何が起こっているのか分かりやすいかもしれません。

 

以下がサンプル・コードになります。

Box2DField.as

package  
{
	import Box2D.Collision.b2AABB;
	import Box2D.Collision.Shapes.b2CircleDef;
	import Box2D.Collision.Shapes.b2ShapeDef;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2DebugDraw;
	import Box2D.Dynamics.b2World;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	
	[SWF(backgroundColor="#333333", frameRate="30", width="500", height="500")]
	public class Box2DField extends Sprite
	{
		private var _world:b2World;
		
		public function Box2DField() 
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			
			// デバッグ描画の設定
			var debugDraw:b2DebugDraw = new b2DebugDraw();
			debugDraw.SetSprite(this);
			debugDraw.SetDrawScale(100);
			debugDraw.SetFillAlpha(0.3);
			debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
			
			// worldを作る
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set( -100, -100);
			worldAABB.upperBound.Set(100, 100);
			// 重力は0に設定
			_world = new b2World(worldAABB, new b2Vec2(0, 0), true);
			_world.SetDebugDraw(debugDraw);
			
			// 250, 250を中心として1[m/sec^2]の力場
			var cfc:CustomForceController = new CustomForceController();
			cfc.fixedPoint.Set(2.5, 2.5);
			cfc.power = 1;
			_world.AddController(cfc);
			
			
			var b2circledef:b2CircleDef;
			var b2body:b2Body;
			var n:int = 25;
			for (var i:int = 0; i < n; ++i) {
				// 円を作る
				b2circledef = new b2CircleDef();
				b2circledef.radius = 0.1 + 0.2 * Math.random();
				b2circledef.density = 0.5;			
				b2body = _world.CreateBody(new b2BodyDef());
				b2body.CreateShape(b2circledef);
				b2body.SetMassFromShapes();
				b2body.SetPosition(new b2Vec2(2.5 + 2 * Math.cos(2 * Math.PI * i / n), 2.5 + 2 * Math.sin(2 * Math.PI * i / n)));
				
				// 上で作った力場に作った円を参加させる
				cfc.AddBody(b2body);
			}
			
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		
		private function enterFrameHandler(e:Event):void 
		{
			_world.Step(1 / 30, 10, 10);
		}
	}
}

CustomForceController.as

package  
{
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2TimeStep;
	import Box2D.Dynamics.Controllers.b2Controller;
	import Box2D.Dynamics.Controllers.b2ControllerEdge;
	
	public class CustomForceController extends b2Controller
	{
		public var fixedPoint:b2Vec2 = new b2Vec2();
		public var power:Number = 0;
		
		override public function Step(step:b2TimeStep):void 
		{
			for (var i:b2ControllerEdge = m_bodyList; i ; i = i.nextBody) {
				var body:b2Body = i.body;
				if (body.IsSleeping())
					continue;
					
				var f:b2Vec2 = fixedPoint.Copy();
				f.Subtract(body.GetPosition());
				f.Normalize();
				f.Multiply(power);
				body.ApplyForce(f, body.GetWorldCenter());
			}
		}
	}
}

Box2DField.asの42~45行目

			var cfc:CustomForceController = new CustomForceController();
			cfc.fixedPoint.Set(2.5, 2.5);
			cfc.power = 1;
			_world.AddController(cfc);

で、画面の中心に向かう力場を作り、

Box2DField.asの62行目で作ったb2Bodyをこの力場の影響下におきます。力場は、Box2D.Dynamics.Controllers.b2Controllerを 拡張することによって作ります。AddBodyというメソッドで追加されたb2Bodyがm_bodyListに入っていますので、 それらに対して、CustomForceController.asの21~25行目

				var f:b2Vec2 = fixedPoint.Copy();
				f.Subtract(body.GetPosition());
				f.Normalize();
				f.Multiply(power);
				body.ApplyForce(f, body.GetWorldCenter());

fixedPoint方向にpowerの長さだけの力をつけています。これによって画面中央に重力がついたb2Worldを作ることができます。 分子間力みたいなのを作って分子の動きのようなものを設定したり、色々できるのではないでしょうか? 次期バーションのBox2Dに入るかまだ分からないことと、今の仕様と少し変わるかもしれないこと等色々ありますが、 この機能がBox2Dに加わることで更に表現力が増すのではないでしょうか?期待です。。。

HTML5飯