Box2Dで遊んでみた

どうも初の作品UPです。

ActionScriptで利用できるBox2Dという物理エンジンに興味を持ったので
少し遊んでみました。



動きはこんな感じ


この2つのサイトを参考にさせていただきました
鮭とプログラムとか
特集:Box2DでActionScript物理プログラミング|gihyo.jp … 技術評論社

少し気になったのは、あまりにも小さい円や長方形を生成させると上手く動作しなくなるようです(壁をすり抜けたりする)。
なので、小さすぎる物は生成できないようにしています。


以下ソース

package {
	
	import caurina.transitions.Tweener;
	import Box2D.Collision.Shapes.b2CircleDef;
	import Box2D.Collision.Shapes.b2PolygonDef;
	import Box2D.Collision.b2AABB;
	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.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	
	public class RandomCircle extends Sprite
	{
	 private var world:b2World;
	 private static const DRAW_SCALE:int = 100; //1mを100ピクセル
	 private var point:Point = new Point();
	 private var m_sprite:Sprite = new Sprite;

	 public function RandomCircle()
	 {
			
	  addChild(m_sprite);
	  var sp:Sprite;
			
			
	  //外枠の定義
	  var worldAABB:b2AABB = new b2AABB();
	  worldAABB.lowerBound.Set(-100, -100);
	  worldAABB.upperBound.Set(100, 100);
			
	  //重力の定義
	  var gravity:b2Vec2 = new b2Vec2(0, 10);
			
	  //外枠と重力を指定して物理エンジンをセットアップ
	  world = new b2World(worldAABB, gravity, true);
			
	  //床の設置
	  //床の位置の定義
	  var floorBodyDef:b2BodyDef = new b2BodyDef();
	  floorBodyDef.position.Set(2.5, 3);
			
	  //床の形の定義、指定する値は半分の値
	  var floorShapeDef:b2PolygonDef = new b2PolygonDef();
	  floorShapeDef.SetAsBox(2, 0.1);
			
	  sp = new Sprite;
	  sp.graphics.beginFill(0xBBBBBB, 0.6);
	  sp.graphics.drawRect(-2*DRAW_SCALE, -3, 4*DRAW_SCALE, 6);
	  sp.graphics.endFill();
			
	  floorBodyDef.userData = sp;
	  floorBodyDef.userData.width = 4*DRAW_SCALE;
	  floorBodyDef.userData.height = 20;
	  m_sprite.addChild(floorBodyDef.userData);
			
	  //床を生成
	  var floor:b2Body = world.CreateBody(floorBodyDef);
	  floor.CreateShape(floorShapeDef);
						
			
	  //描画設定
             //setDebug();
			
	  //イベントハンドラを登録
			
	  stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
	  addEventListener(Event.ENTER_FRAME, enterFrameHandler);	
	 }
		
      public function setDebug():void{ //デバック用
	  var debugDraw:b2DebugDraw = new b2DebugDraw();
	  debugDraw.m_sprite = this;
	  debugDraw.m_drawScale = DRAW_SCALE;  //1mを100ピクセルと定義
	  debugDraw.m_fillAlpha = 0.3;  //不透明度
	  debugDraw.m_lineThickness = 0;
	  debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
	  world.SetDebugDraw(debugDraw);
	 }
		
           private function mouseUpHandler(event:MouseEvent):void{ 
	   point.x = event.stageX;  //マウスの位置を保存
	   point.y = event.stageY;
	  var ran:int = (Math.random() * 2);  
	  switch(ran){  //乱数で長方形か円を生成するかを決める
	    case 0:
	    createCircle();
	    break;
	    case 1:
	    createRect();
	      break;
	    }
	  }
		
	  private function createRect():void{  //長方形の生成
	  //長方形の中心座標
	  var x:Number = point.x / DRAW_SCALE;  //マウスの位置に指定
	  var y:Number = point.y / DRAW_SCALE;
			
	  //長方形の大きさ
	  var width:Number = Math.random() * 50 / DRAW_SCALE;
	  var height:Number = Math.random() * 50 / DRAW_SCALE;
			
	  //小さすぎる長方形ができるのを防ぐ
	  if(width < 0.05 || height < 0.05){
	  return;
	   }
			
	  //長方形の位置の定義
	  var bodyDef:b2BodyDef = new b2BodyDef();
	   bodyDef.position.Set(x, y);
			
	  //長方形の大きさの定義
	  var shapeDef:b2PolygonDef = new b2PolygonDef();
	  shapeDef.SetAsOrientedBox(width, height, new b2Vec2(), 0);
	  shapeDef.density = 1;  //密度
	  shapeDef.restitution = 0.3;  //反発係数
			
	  var sp:Sprite = new Sprite;
	   sp.graphics.beginFill(0xFFFFFF * Math.random(), 0.6);
	  sp.graphics.drawRect(-width*DRAW_SCALE/2, -height*DRAW_SCALE/2, width*DRAW_SCALE, height*DRAW_SCALE);
	  sp.alpha = 0;
	  sp.graphics.endFill();
			
	  bodyDef.userData = sp;
	  bodyDef.userData.width = width*DRAW_SCALE*2;
	  bodyDef.userData.height = height*DRAW_SCALE*2;
	  m_sprite.addChild(bodyDef.userData);
			
	  var body:b2Body = world.CreateBody(bodyDef);
	  body.CreateShape(shapeDef);
	  body.SetMassFromShapes();
	  Tweener.addTween(bodyDef.userData, {alpha:1, time:4});
		}
		
	  private function createCircle():void{  //円の生成
	  //円の中心座標
	  var x:Number = point.x / DRAW_SCALE;  //マウスの位置に指定
	  var y:Number = point.y / DRAW_SCALE;
			
	  //円の半径(ランダム)
	  var radius:Number = (Math.random() * 50) / DRAW_SCALE;
			
	  //小さすぎる円ができるのを防ぐ
	  if(radius < 0.05){
	   return;
	  }
			
	  //円の場所を指定する
	  var bodyDef:b2BodyDef = new b2BodyDef();
	  bodyDef.position.Set(x, y); 
			
	  //円の大きさなどの設定
	  var shapeDef:b2CircleDef = new b2CircleDef();
	  shapeDef.radius = radius; //半径
	  shapeDef.density = 1; //密度
	  shapeDef.restitution = 0.5; //反発係数
			
	  var sp:Sprite = new Sprite;
	  sp.graphics.beginFill(0xFFFFFF * Math.random(), 0.6);
	  sp.graphics.drawCircle(0,0,radius*DRAW_SCALE);
	  sp.alpha = 0;
	  sp.graphics.endFill();
			
	  bodyDef.userData = sp;
	  bodyDef.userData.width = radius*2*DRAW_SCALE;
	  bodyDef.userData.height = radius*2*DRAW_SCALE;
	  m_sprite.addChild(bodyDef.userData);
			
	  //円を生成
	  var body:b2Body = world.CreateBody(bodyDef);
	  body.CreateShape(shapeDef);
	  body.SetMassFromShapes();
			
	  Tweener.addTween(bodyDef.userData, {alpha:1, time:4});
	 }
		
	 private function enterFrameHandler(event:Event):void{
	  // Flashはデフォルトで秒間24フレームなので、
	  // 物理シミュレーションを1/24秒進める
	  world.Step(1/24, 10);
					
       for(var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next){
        if(bb.m_userData is Sprite){
	     bb.m_userData.x = (bb.GetPosition().x) * DRAW_SCALE;
		bb.m_userData.y = (bb.GetPosition().y) * DRAW_SCALE;
	
                  bb.m_userData.rotation = bb.GetAngle() * (180/Math.PI);
	     }
				
	   }
	}
   }
}