« [as]Matrix の scale と rotate のこと | MAIN | [as]ちらっとProxyでAdapterみたいな »

[as]ちらっとBox2D

いまさらながらBox2Dを勉強してみる。物理計算(特に力学系)をやる場合、毎回必要な分だけ生書きしてたのですが、やはりライブラリ使ったほうが効率的かなぁ(特にプロトタイプとか)、という当たり前な理由でまずは触ってみようということで。
勉強するにあたって「Flash OOP本の10章」と、「Box2DでActionScript物理プログラミング」にお世話になります。わかりやすいなぁ。
使用したのは、いつのまにかバージョンが上がっていた、Box2DFlashAS3 2.0.1です。

上記「OOP本」と「Box2DでActionScript物理プログラミング」を眺めるに、大体の流れは、

1.ワールドをつくる
2.BodyなDefとShapeなDefで位置とか形状をつくる
3.BodyとShapeの実体をつくる
4.計算してみる
5.表示に反映させる

ってことみたいですね(今回はJointとか衝突イベントやってないです)。「5.表示に反映させる」については、テスト用に「b2DebugDraw」というクラスがあるので、それを使えばすぐに確認できてお手軽です。
※「OOP本」はBox2Dのバージョンがわかりませんが1.x系なんでしょうか、ちょっとメソッドとか異なるようです。でも流れは同じようなので参考になります。「Box2DでActionScript物理プログラミング」のほうは 2.0.0 なのでそのままいけそうなんですが、b2World の CreateStaticBody,CreateDynamicBodyが 2.0.1 ではなくなってる様子。CreateBodyにしとけば大丈夫ぽい。

で、とりあえず試してみたかったのは、重加速度とShape属性(サイズとか)の動的変更で、その辺りはBox2Dのソースを追いながらアテを探してみることに。ソースを追うにはFlashDevelopが便利ですね。しばらく見たところ、重加速度は、b2World の m_gravity 、Shapeの属性は「3.BodyとShapeの実体をつくる」でつくったb2Shapeから辿ればいけるっぽい。感じ。

そんな流れでつくってみた、ちょっと気持ち悪いBox2Dのサンプルです。

マウスの位置で重加速度が変化して、クリックで円が増えます。で、円をsinで大きさを変えてます。ソースは以下のような感じ。

TestWorld.as

package  
{
	import Box2D.Collision.b2AABB;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2World;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Collision.Shapes.b2PolygonDef;
	import Box2D.Collision.Shapes.b2CircleDef;
	import Box2D.Dynamics.b2Body;
	import Box2D.Collision.Shapes.b2Shape;
	import Box2D.Collision.Shapes.b2CircleShape;
	import Box2D.Dynamics.b2DebugDraw;
	import flash.geom.Rectangle;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	/**
	* Box2Dなテスト
	* @author nutsu
	*/
	public class TestWorld extends MovieClip
	{
		private var world:b2World;
		
		private var WORLD_SCALE:Number = 100; // 100pixel/m
		private var VIEW_RECT:Rectangle;
		private var half_width:Number;
		private var half_height:Number;
		
		private var circles:Array;
		
		public function TestWorld() 
		{
			super();
			
			VIEW_RECT   = new Rectangle( 0, 0, stage.stageWidth/WORLD_SCALE, stage.stageHeight/WORLD_SCALE );
			half_width  = stage.stageWidth * 0.5;
			half_height = stage.stageHeight * 0.5;
			
			//動的オブジェクト用の配列 :b2CircleShape[]
			circles = [];
			
			//とりあえずワールドつくる
			initWorld();
			//静的なオブジェクト(四方壁)をつくる
			initObjects();
			//テストドローな設定
			initDraw();
			//とりあえず1こオブジェクトつくる
			addCircle( VIEW_RECT.width*0.5, VIEW_RECT.height*0.5 );
			
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
			stage.addEventListener( MouseEvent.CLICK, onClick );
			
			stage.quality = "low";
		}
		
		private function initWorld():void
		{
			//物理計算エリア? お約束らしい
			var aabb:b2AABB = new b2AABB();
			aabb.lowerBound.Set( -1000, -1000 );
			aabb.upperBound.Set( 1000, 1000 );
			
			//重加速度。後でマウス位置で変えるので0にしとく
			var gravity:b2Vec2 = new b2Vec2( 0, 0 );
			
			//力が作用してないオブジェクトをスリープするか否か。今回はずっと動いてるのでfalse
			var dosleep:Boolean = false;
			
			//ワールドつくる
			world = new b2World( aabb, gravity, dosleep );
		}
		
		private function initObjects():void
		{
			//四方壁つくる
			//大きさの単位はメートルなそうな
			var wall_size:Number = 0.025;
			
			//ShapeなDef(形状決め)
			var wall_side_shape:b2PolygonDef = new b2PolygonDef();
			wall_side_shape.SetAsBox( wall_size, VIEW_RECT.height * 0.5 );
			wall_side_shape.density = 0; //静的な物体は0なそうな
			
			var wall_vt_shape:b2PolygonDef = new b2PolygonDef();
			wall_vt_shape.SetAsBox( VIEW_RECT.width*0.5, wall_size );
			wall_vt_shape.density = 0;
			
			//BodyなDef(位置決め)
			var wall_left:b2BodyDef   = new b2BodyDef();
			var wall_right:b2BodyDef  = new b2BodyDef();
			var wall_bottom:b2BodyDef = new b2BodyDef();
			var wall_top:b2BodyDef    = new b2BodyDef();
			
			//set position
			wall_left.position.Set(  wall_size, VIEW_RECT.height * 0.5 );
			wall_right.position.Set( VIEW_RECT.width - wall_size, VIEW_RECT.height * 0.5 );
			wall_bottom.position.Set( VIEW_RECT.width * 0.5 , VIEW_RECT.height - wall_size);
			wall_top.position.Set( VIEW_RECT.width*0.5 ,  wall_size);
			
			//worldに入れてく
			world.CreateBody( wall_left   ).CreateShape( wall_side_shape );
			world.CreateBody( wall_right  ).CreateShape( wall_side_shape );
			world.CreateBody( wall_bottom ).CreateShape( wall_vt_shape );
			world.CreateBody( wall_top    ).CreateShape( wall_vt_shape );
		}
		
		private function initDraw():void
		{
			//デバッグ用のドロー設定
			var debug_draw:b2DebugDraw = new b2DebugDraw();
			debug_draw.m_sprite        = this;
			debug_draw.m_drawScale     = WORLD_SCALE;
			debug_draw.m_fillAlpha     = 1;
			debug_draw.m_lineThickness = 0;
			debug_draw.m_drawFlags     = b2DebugDraw.e_shapeBit;
			world.SetDebugDraw( debug_draw );
		}
		
		private function addCircle( ax:Number, ay:Number ):void
		{
			//任意の位置にCircleオブジェクト追加してく
			//手順 ShapeなDef→BodyなDef→worldで実体をつくる感じ
			var s:b2CircleDef = new b2CircleDef();
			s.density = 1;
			s.restitution = 0.2;
			s.radius = 0.2;
			
			var b:b2BodyDef = new b2BodyDef();
			b.position.Set( ax, ay );
			
			var bb:b2Body  = world.CreateBody( b );
			var bs:b2Shape = bb.CreateShape( s );
			bb.SetMassFromShapes();
			
			//Shapeはあとでいじるので配列にいれとく
			bs.m_userData = Number(0);
			circles.push( b2CircleShape(bs) );
		}
		
		private function onClick( e:MouseEvent ):void
		{
			addCircle(mouseX/WORLD_SCALE, mouseY/WORLD_SCALE);
		}
		
		private function onEnterFrame( e:Event ):void
		{
			if ( world )
			{
				//マウス位置で重加速度変更
				world.m_gravity.x = -2 * (half_width - mouseX) / half_width;
				world.m_gravity.y = -2 * (half_height - mouseY) / half_height;
				
				//円をうにょうにょ
				for (var i:int = 0; i < circles.length ; i++) 
				{
					var bs:b2CircleShape = circles[i];
					bs.m_radius = 0.2 + Math.sin( bs.m_userData ) * 0.1;
					bs.m_userData += 0.05;
				}
				
				//計算 Step(計算時間,計算精度) きびっとしたいので計算時間を長めにとる
				world.Step(1/15, 4);
			}
		}
	}
}

なんと言いますか、お手軽だなぁという印象です。ライブラリって便利。

誰かに向けたメモ:
Box2Dのソースみていると、virtual があるんですが、AS に virtual ってあったんですか?自分で書いてみたらコンパイル通るみたいなんですけど、何か意味あるのかな…。

COMMENT

はじめまして、私もvirtualにはちょっと悩みました。調べた結果現在のActionScript3.0ではvirtualは使用されていません。しかし将来的に使用する可能性があるので、現段階では使用しないほうがよいとのこと。
#出典を忘れましたが。。。。
多分もとのC++で書かれたソースの名残かと思います。
私もBox2Dを使用したプログラムを書いていたので参考になります。これからもよろしくお願いします。

コメントありがとうございます。使っていないのにコンパイルが通るのが気持ち悪いですね…。
というより学校の先輩で汗しました。僕は画像です。
今度ともよろしくお願いいたします。

COMMENT+

いままで、ここでコメントしたことがないときは、コメントを表示する前にこのブログのオーナーの承認が必要になることがあります。承認されるまではコメントは表示されません。そのときはしばらく待ってください。

TRACKBACK

URL : http://nutsu.com/cgi/mt/mt-tb.cgi/89