« Box2D FLASH その5 マウスでドラッグ | トップページ | Box2D FLASH その7 BitmapDataでエフェクト »

2008年4月27日 (日)

Box2D FLASH その6 2つのオブジェクトをつなげる

Jointを使って2つのオブジェクトをつなげます。

今までのはオブジェクトが1個だけだったのでもう1個作成します。

//2つめのBOX作成
bodyDef.userData = new BoxFace();
bodyDef.userData.width = boxDef.extents.x * 2;
bodyDef.userData.height = boxDef.extents.y * 2;
bodyDef.position.x = 400;
bodyDef.position.y = 200;
var body2 = m_world.CreateBody(bodyDef);
addChild(bodyDef.userData);

1つめのBOXの横に同じBOXを作成します。
すでに1つ作成しているので、違う部分のみでできます。
userDataのとこも同じだけど表示するインスタンス自体はその数だけ
作らないといけません。

●Jointの作成
では2つのBOXをつなげます。

//Jointの作成
var jd:b2RevoluteJointDef = new b2RevoluteJointDef();
jd.enableLimit = true;
jd.lowerAngle = -50 / (180/Math.PI);
jd.upperAngle = 50 / (180/Math.PI);
jd.anchorPoint.Set(200, 200);
jd.body1 = body1;
jd.body2 = body2;
m_world.CreateJoint(jd);

> var jd:b2RevoluteJointDef = new b2RevoluteJointDef();
新しいJointを作成します。
なぜ「RevoluteJoint」という名前なのかは分かりません。

> jd.enableLimit = true;
Jointの曲がる角度を制限するか無制限にするかの設定です。
trueにするとこの後に設定する角度の範囲内で動きます。
falseにすると360度動きます。

> jd.lowerAngle = -50 / (180/Math.PI);
> jd.upperAngle = 50 / (180/Math.PI);
enableLimitをtrueにした場合にここで指定する角度の範囲内で
動くようになります。
上記の場合だと-50度~50度の範囲で動きます。

> jd.anchorPoint.Set(200, 200);
2つのオブジェクトがこの点を支点として動きます。
単に2つのオブジェクトをつなげる場合はどちらかのオブジェクトの
位置と同じにしておきます。
数字を(300, 100)とかに変えてみると違いが分かります。

> jd.body1 = body1;
> jd.body2 = body2;
つなげるオブジェクトを指定します。
body1、body2は型は「b2Body」です。CreateBodyの戻り値になってます。

> m_world.CreateJoint(jd);
作成したJointを登録します。

1つのJointでつなげられるオブジェクトは2つまでです。
複数のJointを使用すれば複数のオブジェクトをつなげることができます。

●Jointの描画
以上ので動くと思いますが、動作を分かりやすくするためJoint自体も
描画してあげます。
Jointを描画する機能もBox2D自体には無いので自作することになりますが
サンプルのものをそのまま使えば大丈夫です。

毎フレームで以下を実行します。

//Jointの描画
for (var jj:b2Joint = m_world.m_jointList; jj; jj = jj.m_next){
	DrawJoint(jj);
}

関数「DrawJoint」はいろいろなタイプのJointを描画するようになっています。
前回やったマウスとのJointもこれで描画されるようになります。

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;

	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Dynamics.Joints.*;
	import Box2D.Common.Math.*;
	public class Box2D0430Test05 extends Sprite {
		public var m_world:b2World;
		public var m_iterations:int = 1;
		public var m_timeStep:Number = 1/60;
		public var m_sprite:Sprite;
		public var m_physScale:Number = 1;
		private var m_mouseJoint:b2MouseJoint;
		function Box2D0430Test05() {
			init();
		}
		public function init():void {
			//Box2D 計算&描画用のENTER_FRAMEの設定
			addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
			
			//シェイプ描画用のSpriteを作成
			m_sprite = new Sprite();
			addChild(m_sprite);
			
			//Box2Dのステージを作成
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.minVertex.Set(-1000.0, -1000.0);
			worldAABB.maxVertex.Set(1000.0, 1000.0);
			var gravity:b2Vec2 = new b2Vec2(0.0, 100.0);	//重力(X方向、Y方向)
			var doSleep:Boolean = false;	//これまだ不明。。
			m_world = new b2World(worldAABB, gravity, doSleep);

			//BOXの作成
			var boxDef:b2BoxDef = new b2BoxDef();
			boxDef.extents.Set(40, 20);
			boxDef.density = 0.2;
			boxDef.friction = 0.8;
			boxDef.restitution = 0.6;
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.AddShape(boxDef);
			bodyDef.position.x = 200;
			bodyDef.position.y = 200;
			bodyDef.userData = new BoxFace();	//BoxFaceはリンケージしてあるもの
			bodyDef.userData.width = boxDef.extents.x * 2;
			bodyDef.userData.height = boxDef.extents.y * 2;
			var body1:b2Body = m_world.CreateBody(bodyDef);
			addChild(bodyDef.userData);
			
			//2つめのBOX作成
			bodyDef.userData = new BoxFace();
			bodyDef.userData.width = boxDef.extents.x * 2;
			bodyDef.userData.height = boxDef.extents.y * 2;
			bodyDef.position.x = 400;
			bodyDef.position.y = 200;
			var body2 = m_world.CreateBody(bodyDef);
			addChild(bodyDef.userData);
			
			//Jointの作成
			var jd:b2RevoluteJointDef = new b2RevoluteJointDef();
			jd.enableLimit = true;
			jd.lowerAngle = -50 / (180/Math.PI);
			jd.upperAngle = 50 / (180/Math.PI);
			jd.anchorPoint.Set(400, 200);
			jd.body1 = body1;
			jd.body2 = body2;
			m_world.CreateJoint(jd);
			
			//地面の作成
			//var boxDef:b2BoxDef = new b2BoxDef();
			boxDef.extents.Set(400, 20);
			boxDef.density = 0.0; //ここを0にすると動かない!
			boxDef.friction = 0.8;
			boxDef.restitution = 0.6;
			//var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.AddShape(boxDef);
			bodyDef.position.x = 400;
			bodyDef.position.y = 500;
			bodyDef.userData = { };
			bodyDef.userData.fillColor = 0x000000;
			m_world.CreateBody(bodyDef);
			//天井
			bodyDef.position.y = 0;
			m_world.CreateBody(bodyDef);
			//左の壁
			boxDef.extents.Set(20, 300);
			bodyDef.position.x = 0;
			bodyDef.position.y = 300;
			m_world.CreateBody(bodyDef);
			//右の壁
			bodyDef.position.x = 800;
			m_world.CreateBody(bodyDef);
			
			//マウスイベントを登録
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, mUp);
		}
		
		public function Update(e:Event):void {
			//Box2Dに計算をさせる
			m_world.Step(m_timeStep, m_iterations);
			
			//シェイプを全て削除
			this.m_sprite.graphics.clear();
			
			for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next) {
				if (bb.m_userData is Sprite) {
					//bb.m_userDataがSprite、またはMovieClipの時
					bb.m_userData.x = bb.m_position.x;
					bb.m_userData.y = bb.m_position.y;
					bb.m_userData.rotation = bb.m_rotation * (180/Math.PI);
				} else {
					//上記以外の時。この場合はシェイプ
					for (var s:b2Shape = bb.GetShapeList(); s != null; s = s.GetNext()) {
						drawShape(s, bb.m_userData.fillColor);
					}   
				}
			}
			//Jointの描画
			for (var jj:b2Joint = m_world.m_jointList; jj; jj = jj.m_next){
				DrawJoint(jj);
			}
		}
		
		public function drawShape(_shape:b2Shape, _fillColor:int = 0xFFFFFF):void {
			//シェイプの描画 このへんはコピペで。。
			var i:int;
			var v:b2Vec2;
			var poly:b2PolyShape = _shape as b2PolyShape;
			var tV:b2Vec2 = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
			m_sprite.graphics.beginFill(_fillColor, 1);
			m_sprite.graphics.lineStyle(0, 0, 0);
			m_sprite.graphics.moveTo(tV.x * m_physScale, tV.y * m_physScale);
			for (i = 0; i < poly.m_vertexCount; ++i){
				v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i]));
				m_sprite.graphics.lineTo(v.x * m_physScale, v.y * m_physScale);
			}
			m_sprite.graphics.lineTo(tV.x * m_physScale, tV.y * m_physScale);
			m_sprite.graphics.endFill();
		}

		private function mDown(e:MouseEvent):void {
			var body:b2Body = GetBodyAtMouse();	//マウスの下にあるオブジェクトを取得
			if (body) {
				var md:b2MouseJointDef = new b2MouseJointDef();
				md.body1 = m_world.m_groundBody;
				md.body2 = body;
				md.target.Set(mouseX, mouseY);
				md.maxForce = 30000.0 * body.m_mass;
				md.timeStep = m_timeStep;
				m_mouseJoint = m_world.CreateJoint(md) as b2MouseJoint;
				body.WakeUp();
				
				addEventListener(Event.ENTER_FRAME, MouseDrag);
			}
		}
		private function mUp(e:MouseEvent):void {
			if (m_mouseJoint) {
				m_world.DestroyJoint(m_mouseJoint);
				m_mouseJoint = null;
				removeEventListener(Event.ENTER_FRAME, MouseDrag);
			}
		}
		public function MouseDrag(e:Event):void{
			var p2:b2Vec2 = new b2Vec2(mouseX, mouseY);
			m_mouseJoint.SetTarget(p2);
		}

		private var mousePVec:b2Vec2 = new b2Vec2();
		public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
			// Make a small box.
			mousePVec.Set(mouseX, mouseY);
			var aabb:b2AABB = new b2AABB();
			aabb.minVertex.Set(mouseX - 0.001, mouseY - 0.001);
			aabb.maxVertex.Set(mouseX + 0.001, mouseY + 0.001);
			// Query the world for overlapping shapes.
			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 || includeStatic) {
					var inside:Boolean = shapes[i].TestPoint(mousePVec);
					if (inside) {
						body = shapes[i].m_body;
						break;
					}
				}
			}
			return body;
		}
		public function DrawJoint(joint:b2Joint):void {
			var b1:b2Body = joint.m_body1;
			var b2:b2Body = joint.m_body2;
			var x1:b2Vec2 = b1.m_position;
			var x2:b2Vec2 = b2.m_position;
			var p1:b2Vec2 = joint.GetAnchor1();
			var p2:b2Vec2 = joint.GetAnchor2();
			m_sprite.graphics.lineStyle(1,0x44aaff,1/1);
			switch (joint.m_type) {
			case b2Joint.e_distanceJoint:
			case b2Joint.e_mouseJoint:
				m_sprite.graphics.moveTo(p1.x * m_physScale, p1.y * m_physScale);
				m_sprite.graphics.lineTo(p2.x * m_physScale, p2.y * m_physScale);
				break;
				
			case b2Joint.e_pulleyJoint:
				var pulley:b2PulleyJoint = joint as b2PulleyJoint;
				var s1:b2Vec2 = pulley.GetGroundPoint1();
				var s2:b2Vec2 = pulley.GetGroundPoint2();
				m_sprite.graphics.moveTo(s1.x * m_physScale, s1.y * m_physScale);
				m_sprite.graphics.lineTo(p1.x * m_physScale, p1.y * m_physScale);
				m_sprite.graphics.moveTo(s2.x * m_physScale, s2.y * m_physScale);
				m_sprite.graphics.lineTo(p2.x * m_physScale, p2.y * m_physScale);
				break;
				
			default:
				if (b1 == m_world.m_groundBody) {
					m_sprite.graphics.moveTo(p1.x * m_physScale, p1.y * m_physScale);
					m_sprite.graphics.lineTo(x2.x * m_physScale, x2.y * m_physScale);
				} else if (b2 == m_world.m_groundBody) {
					m_sprite.graphics.moveTo(p1.x * m_physScale, p1.y * m_physScale);
					m_sprite.graphics.lineTo(x1.x * m_physScale, x1.y * m_physScale);
				} else{
					m_sprite.graphics.moveTo(x1.x * m_physScale, x1.y * m_physScale);
					m_sprite.graphics.lineTo(p1.x * m_physScale, p1.y * m_physScale);
					m_sprite.graphics.lineTo(x2.x * m_physScale, x2.y * m_physScale);
					m_sprite.graphics.lineTo(p2.x * m_physScale, p2.y * m_physScale);
				}
			}
		}
	}
}

|

« Box2D FLASH その5 マウスでドラッグ | トップページ | Box2D FLASH その7 BitmapDataでエフェクト »

コメント

コメントを書く



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


コメントは記事投稿者が公開するまで表示されません。



トラックバック


この記事へのトラックバック一覧です: Box2D FLASH その6 2つのオブジェクトをつなげる:

« Box2D FLASH その5 マウスでドラッグ | トップページ | Box2D FLASH その7 BitmapDataでエフェクト »