« Papervision3Dをやってみた15 | トップページ | Box2DFlashAS3をやってみた 2 »

2008年5月31日 (土)

Box2DFlashAS3をやってみた

P_26




















Box2DFlashAS3をやってみました。

AS用の物理エンジンです。

付属のサンプルといろいろなサイトを見ながらとりあえずのプログラミング。

memo.at sonicというサイトが非常に参考になりました。

で、サンプル。

とりあえず、こんな感じに。

マウスドラッグなどの関数はサンプルにのっていたので流用(TestBedフォルダの中のソース)。

実際の描写方法が分からず、とても苦労しました。

たまに重なってしまったりと、まだよく分かっていない部分がありますが。

まあ何とかなったと思います。






以下、ソースです。

バージョンは2.0.0を利用しています。

(2009/3 ソース書き直しました。)

package
{
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.*;
	import Box2D.Common.Math.*;
	import Box2D.Dynamics.*;
	import Box2D.Dynamics.Contacts.*;
	import Box2D.Dynamics.Joints.*;
	import General.Input;
	import flash.display.*;
	import flash.events.Event;

	[SWF(backgroundColor="#ffffff")]

	public class Box2D_sample1 extends Sprite
	{
		// 入力用のスプライト
		private var m_sprite:Sprite;

		// Box2Dのワールド
		private var m_world:b2World;

		// 計算精度
		private var m_iterations:int;

		// フレームレートのようなもの
		private var m_timeStep:Number;

		// physical Scale
		private var m_physScale:Number;

		// ジョイントの作成
		private var m_mouseJoint:b2MouseJoint;

		// 以下4つはマウス座標の変数
		private var mouseXWorldPhys:Number;
		private var mouseYWorldPhys:Number;
		private var mouseXWorld:Number;
		private var mouseYWorld:Number;

		// マウス位置のベクトル
		private var mousePVec:b2Vec2;

		// 入力用オブジェクト
		private var m_input:Input;

		// コンストラクタ
		public function Box2D_sample1()
		{
			stage.frameRate=60;
			stage.quality=StageQuality.HIGH;
			stage.align=StageAlign.TOP_LEFT;
			stage.scaleMode=StageScaleMode.NO_SCALE;

			m_sprite=new Sprite;
			mousePVec=new b2Vec2;
			m_iterations=30;
			m_timeStep=1 / 60;
			m_physScale=30;

			// 入力の初期設定。Inputの引数にspriteを指定する
			addChild(m_sprite);
			m_input=new Input(m_sprite);

			// 実際の描写を行う一時的な変数
			var sp:Sprite;

			// worldの境界になるボックスを作成。小さすぎるより、大きすぎるほうが良い。
			var worldAABB:b2AABB=new b2AABB;
			worldAABB.lowerBound.Set(-1000.0, -1000.0);
			worldAABB.upperBound.Set(1000.0, 1000.0);

			// 重力の設定
			var gravity:b2Vec2=new b2Vec2(0.0, 10.0);

			// 動かなくなった物体に対して演算を行うかどうか
			var doSleep:Boolean=true;

			// Box2Dのworldを生成
			m_world=new b2World(worldAABB, gravity, doSleep);

			//動く円形オブジェクトを作成
			var circleDef:b2CircleDef=new b2CircleDef;

			// 大きさ、密度、摩擦力、反発力などを設定
			circleDef.radius=30 / m_physScale;
			circleDef.density=1.0;
			circleDef.friction=1.0;
			circleDef.restitution=0.5;

			// Bodyを入れる箱を用意。位置はここで指定
			var circleBD:b2BodyDef=new b2BodyDef;
			circleBD.position.Set(100 / m_physScale, 100 / m_physScale);

			// 実際の円を生成
			sp=new Sprite;
			sp.graphics.beginFill(0x00ff00, 0.6);
			sp.graphics.drawCircle(0, 0, 30);
			sp.graphics.endFill();

			// 円を保存
			circleBD.userData=sp;
			circleBD.userData.width=60;
			circleBD.userData.height=60;
			m_sprite.addChild(circleBD.userData);

			// Bodyを生成して、Box2Dのworldに落とし込む
			var circle:b2Body=m_world.CreateDynamicBody(circleBD);
			circle.CreateShape(circleDef);
			circle.SetMassFromShapes();

			// 正三角形の生成
			var triangleDef:b2PolygonDef=new b2PolygonDef();

			// 底辺の半分の長さと高さを設定
			var radius:Number=30;
			var h:Number=Math.tan(60 * Math.PI / 180) * radius;

			// 角の数を設定して頂点の位置を設定する
			triangleDef.vertexCount=3;
			triangleDef.vertices[0].Set(-1 * radius / m_physScale, -1 * h / 2 / m_physScale);
			triangleDef.vertices[1].Set(radius / m_physScale, -1 * h / 2 / m_physScale);
			triangleDef.vertices[2].Set(0.0, h / 2 / m_physScale);
			triangleDef.density=1.0;
			triangleDef.friction=1.0;
			triangleDef.restitution=0.5;

			// warldに落とし込む
			var triangleBD:b2BodyDef=new b2BodyDef();
			triangleBD.position.Set(70 / m_physScale, 100 / m_physScale);

			// 実際の三角形を生成
			sp=new Sprite;
			sp.graphics.beginFill(0xff0000, 0.6);
			sp.graphics.moveTo(-1 * radius, -1 * h / 2);
			sp.graphics.lineTo(radius, -1 * h / 2);
			sp.graphics.lineTo(0, h / 2);
			sp.graphics.endFill();

			// 三角形を保存
			triangleBD.userData=sp;
			triangleBD.userData.width=60;
			triangleBD.userData.height=h;
			m_sprite.addChild(triangleBD.userData);

			var triangle:b2Body=m_world.CreateDynamicBody(triangleBD);
			triangle.CreateShape(triangleDef);
			triangle.SetMassFromShapes();

			// 円2つと長方形を組み合わせてカプセルを生成
			var capsuleBD:b2BodyDef=new b2BodyDef();
			capsuleBD.position.Set(300 / m_physScale, 100 / m_physScale);

			// 1つ目の円を生成
			var circle1Def:b2CircleDef=new b2CircleDef();

			//b2BodyDef内のローカルの位置を指定する
			circle1Def.localPosition.Set(20 / m_physScale, 0 / m_physScale);

			// 半径などを設定
			circle1Def.radius=10 / m_physScale;
			circle1Def.density=1.0;
			circle1Def.friction=1.0;
			circle1Def.restitution=0.5;

			// 同様に2つ目の円を生成
			var circle2Def:b2CircleDef=new b2CircleDef();
			circle2Def.localPosition.Set(-20 / m_physScale, 0 / m_physScale);
			circle2Def.radius=10 / m_physScale;
			circle2Def.density=1.0;
			circle2Def.friction=1.0;
			circle2Def.restitution=0.2;

			// 真ん中の長方形
			var centerDef:b2PolygonDef=new b2PolygonDef();
			centerDef.SetAsBox(20 / m_physScale, 10 / m_physScale);

			// 実際の図形を生成
			sp=new Sprite;
			sp.graphics.beginFill(0x0000ff, 0.6);
			sp.graphics.drawCircle(20, 0, 10);
			sp.graphics.drawCircle(-20, 0, 10);
			sp.graphics.drawRect(-20, -10, 40, 20);
			sp.graphics.endFill();

			// 図形を保存
			capsuleBD.userData=sp;
			capsuleBD.userData.width=60;
			capsuleBD.userData.height=20;
			m_sprite.addChild(capsuleBD.userData);

			// 3つのオブジェクトをまとめて登録。生成
			var capsule:b2Body=m_world.CreateDynamicBody(capsuleBD);
			capsule.CreateShape(circle1Def);
			capsule.CreateShape(circle2Def);
			capsule.CreateShape(centerDef);
			capsule.SetMassFromShapes();

			// 壁1を生成
			var floorDef1:b2PolygonDef=new b2PolygonDef;
			floorDef1.SetAsBox(250 / m_physScale, 5 / m_physScale);
			floorDef1.density=1000.0;
			floorDef1.friction=1.0;
			var floorBD1:b2BodyDef=new b2BodyDef;
			floorBD1.position.Set(250 / m_physScale, 500 / m_physScale);
			sp=new Sprite;
			sp.graphics.beginFill(0xBBBBBB, 0.6);
			sp.graphics.drawRect(-250, -5, 500, 10);
			sp.graphics.endFill();
			floorBD1.userData=sp;
			floorBD1.userData.width=500;
			floorBD1.userData.height=10;
			m_sprite.addChild(floorBD1.userData);
			var floorBody1:b2Body=m_world.CreateStaticBody(floorBD1);
			floorBody1.CreateShape(floorDef1);
			floorBody1.SetMassFromShapes();

			// 壁2を生成
			var floorDef2:b2PolygonDef=new b2PolygonDef;
			floorDef2.SetAsBox(5 / m_physScale, 250 / m_physScale);
			floorDef2.density=1000.0;
			floorDef2.friction=1.0;
			var floorBD2:b2BodyDef=new b2BodyDef;
			floorBD2.position.Set(5 / m_physScale, 250 / m_physScale);
			sp=new Sprite;
			sp.graphics.beginFill(0xBBBBBB, 0.6);
			sp.graphics.drawRect(-5, -250, 10, 500);
			sp.graphics.endFill();
			floorBD2.userData=sp;
			floorBD2.userData.width=10;
			floorBD2.userData.height=500;
			m_sprite.addChild(floorBD2.userData);
			var floorBody2:b2Body=m_world.CreateStaticBody(floorBD2);
			floorBody2.CreateShape(floorDef2);
			floorBody2.SetMassFromShapes();

			// 壁3を生成
			var floorDef3:b2PolygonDef=new b2PolygonDef;
			floorDef3.SetAsBox(5 / m_physScale, 250 / m_physScale);
			floorDef3.density=1000.0;
			floorDef3.friction=1.0;
			var floorBD3:b2BodyDef=new b2BodyDef;
			floorBD3.position.Set(495 / m_physScale, 250 / m_physScale);
			sp=new Sprite;
			sp.graphics.beginFill(0xBBBBBB, 0.6);
			sp.graphics.drawRect(-5, -250, 10, 500);
			sp.graphics.endFill();
			floorBD3.userData=sp;
			floorBD3.userData.width=10;
			floorBD3.userData.height=500;
			m_sprite.addChild(floorBD3.userData);
			var floorBody3:b2Body=m_world.CreateStaticBody(floorBD3);
			floorBody3.CreateShape(floorDef3);
			floorBody3.SetMassFromShapes();

			// 壁4を生成
			var floorDef4:b2PolygonDef=new b2PolygonDef;
			floorDef4.SetAsBox(250 / m_physScale, 5 / m_physScale);
			floorDef4.density=1000.0;
			floorDef4.friction=1.0;
			var floorBD4:b2BodyDef=new b2BodyDef;
			floorBD4.position.Set(250 / m_physScale, 5 / m_physScale);
			sp=new Sprite;
			sp.graphics.beginFill(0xBBBBBB, 0.6);
			sp.graphics.drawRect(-250, -5, 500, 10);
			sp.graphics.endFill();
			floorBD4.userData=sp;
			floorBD4.userData.width=500;
			floorBD4.userData.height=10;
			m_sprite.addChild(floorBD4.userData);
			var floorBody4:b2Body=m_world.CreateStaticBody(floorBD4);
			floorBody4.CreateShape(floorDef4);
			floorBody4.SetMassFromShapes();

			addEventListener(Event.ENTER_FRAME, Update);

			//setDebug();
		}

		// フレームイベント用関数
		public function Update(e:Event):void
		{
			m_world.Step(m_timeStep, m_iterations);
			UpdateMouseWorld();
			MouseDrag();
			Input.update();

			for (var bb:b2Body=m_world.m_bodyList; bb; bb=bb.m_next)
			{
				if (bb.m_userData is Sprite)
				{
					bb.m_userData.x=(bb.GetPosition().x) * m_physScale;
					bb.m_userData.y=(bb.GetPosition().y) * m_physScale;
					bb.m_userData.rotation=bb.GetAngle() * (180 / Math.PI);
				}
			}
		}

		// デバッグ用の関数
		public function setDebug():void
		{
			var dbgDraw:b2DebugDraw=new b2DebugDraw;
			var dbgSprite:Sprite=new Sprite;
			addChild(dbgSprite);
			dbgDraw.m_sprite=dbgSprite;
			dbgDraw.m_drawScale=m_physScale;
			dbgDraw.m_fillAlpha=0.5;
			dbgDraw.m_lineThickness=0;
			dbgDraw.m_drawFlags=0xFFFFFFFF;
			m_world.SetDebugDraw(dbgDraw);
		}

		// マウスの座標を獲得する関数
		public function UpdateMouseWorld():void
		{
			mouseXWorldPhys=(Input.mouseX) / m_physScale;
			mouseYWorldPhys=(Input.mouseY) / m_physScale;
			mouseXWorld=(Input.mouseX);
			mouseYWorld=(Input.mouseY);
		}

		// マウスの位置にあるオブジェクトを取得する関数
		public function GetBodyAtMouse():b2Body
		{
			mousePVec.Set(mouseXWorldPhys, mouseYWorldPhys);

			var aabb:b2AABB=new b2AABB();
			aabb.lowerBound.Set(mouseXWorldPhys - 0.001, mouseYWorldPhys - 0.001);
			aabb.upperBound.Set(mouseXWorldPhys + 0.001, mouseYWorldPhys + 0.001);

			var k_maxCount:int=10;
			var shapes:Array=new Array();
			var count:int=m_world.Query(aabb, shapes, k_maxCount);
			var body:b2Body=null;
			for (var i:int=0; i < count; ++i)
			{
				if (shapes[i].m_body.IsStatic() == false)
				{
					var tShape:b2Shape=shapes[i] as b2Shape;
					var inside:Boolean=tShape.TestPoint(tShape.m_body.GetXForm(), mousePVec);
					if (inside)
					{
						body=tShape.m_body;
						break;
					}
				}
			}
			return body;
		}

		// ドラッグ用の関数
		public function MouseDrag():void
		{
			if (Input.mouseDown && !m_mouseJoint)
			{
				buttonMode=true;
				var body:b2Body=GetBodyAtMouse();

				if (body)
				{
					var md:b2MouseJointDef=new b2MouseJointDef();
					md.body1=m_world.m_groundBody;
					md.body2=body;
					md.target.Set(mouseXWorldPhys, mouseYWorldPhys);
					md.maxForce=300.0 * body.m_mass;
					md.timeStep=m_timeStep;
					m_mouseJoint=m_world.CreateJoint(md) as b2MouseJoint;
					body.WakeUp();
				}
			}
			if (!Input.mouseDown && m_mouseJoint)
			{
				m_world.DestroyJoint(m_mouseJoint);
				m_mouseJoint=null;
			}
			if (m_mouseJoint)
			{
				var p2:b2Vec2=new b2Vec2(mouseXWorldPhys, mouseYWorldPhys);
				m_mouseJoint.SetTarget(p2);
			}
		}
	}
}

|

« Papervision3Dをやってみた15 | トップページ | Box2DFlashAS3をやってみた 2 »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1040194/21319730

この記事へのトラックバック一覧です: Box2DFlashAS3をやってみた:

« Papervision3Dをやってみた15 | トップページ | Box2DFlashAS3をやってみた 2 »