« Box2D FLASH その4 MovieClipの表示 | トップページ | Box2D FLASH その6 2つのオブジェクトをつなげる »

2008年4月26日 (土)

Box2D FLASH その5 マウスでドラッグ

位置に移動させるとかstartDragを使うとかありますが、そういう感じでは
ないみたいです。
(こういうのと同じ感覚でできるやり方もあるかもしれないけど未確認)

Box2Dでは「Joint」というものでオブジェクト同士をつなげることができます。
マウスでオブジェクトをドラッグする場合はマウスとオブジェクトを
この「Joint」でつなげることによって「マウスでオブジェクトを引っ張る」
ことができ、「オブジェクトをマウスでドラッグする」ように見せられます。

●ドラッグの対象となるオブジェクトを取得
ドラッグを開始させるタイミングMOUSE_DOWNイベントが発生した時になります。
各オブジェクトにMOUSE_DOWNイベントを登録すれば良さそうですが、
このサンプルでは違います。(でもこの方法もできそうな気がします)
サンプルではstageに対してMOUSE_DOWNイベントを登録し、MOUSE_DOWNされたら
その時マウスの下にあるオブジェクトを調べています。

Box2Dにそういう関数が用意されているわけではないみたいで、
サンプルでは「GetBodyAtMouse」という関数の中にこの処理が
がーっと書かれています。
中身はなんとなく分かるけど、ここは「おまじない」としてしまいます。。

●Jointの作成
ドラッグの対象となるオブジェクトを取得できたら、マウスとそのオブジェクトを
Jointでつなげます。
関数「mDown」内の↓この部分です。

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();

> var md:b2MouseJointDef = new b2MouseJointDef();
Jointを作成します。マウスとつなげるので「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;
1回の処理で経過させる時間です。
通常は「m_world.Step(m_timeStep, m_iterations);」で指定する
「m_timeStep」と同じでいいと思います。

> m_mouseJoint = m_world.CreateJoint(md) as b2MouseJoint;
作成したJointを登録させます。

> body.WakeUp();
これはまだよく分からない。。

Jointが作成されました。
マウスは動くのでJointのターゲットは常に更新する必要があります。
そのためのENTER_FRAMEを用意します。

addEventListener(Event.ENTER_FRAME, MouseDrag);

内容はコチラ。

public function MouseDrag(e:Event):void{
	var p2:b2Vec2 = new b2Vec2(mouseX, mouseY);
	m_mouseJoint.SetTarget(p2);
}

Jointのターゲットをその時のマウスの座標に変更します。
これでマウスにJointが引っ張られて、それによってつながっている
オブジェクトも引っ張られていきます。

マウスを離したらJointを削除します。

private function mUp(e:MouseEvent):void {
	if (m_mouseJoint) {
		m_world.DestroyJoint(m_mouseJoint);
		m_mouseJoint = null;
		removeEventListener(Event.ENTER_FRAME, MouseDrag);
	}
}

これでドラッグできるようになりました。

テストしにくいので、天井と壁も作成してます。

//天井
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);

boxDef、bodyDefはすでに作成してあるので、違う部分だけ
設定しなおしてCreateBodyに渡してあげればOKです。

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 Box2D0430Test04 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 Box2D0430Test04() {
			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 body:b2Body = m_world.CreateBody(bodyDef);
			addChild(bodyDef.userData);
			
			//地面の作成
			//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);
					}   
				}
			}
		}
		
		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;
		}
	}
}

|

« Box2D FLASH その4 MovieClipの表示 | トップページ | Box2D FLASH その6 2つのオブジェクトをつなげる »

コメント

なにも力の働いてない状態で、
bodyを作成すると、
bodyがsleep状態になるんで、
WakeUp()でbodyのsleepを解除してやる
必要があるみたいです。

投稿: {el_e3電気を大切に | 2008年5月22日 (木) 18時56分

コメントを書く



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


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



トラックバック


この記事へのトラックバック一覧です: Box2D FLASH その5 マウスでドラッグ:

» MyNetFaves : Public Faves Tagged Box2d [MyNetFaves : Web 2.0 Social Bookmarking]
Marked your site as box2d at MyNetFaves! [続きを読む]

受信: 2009年5月 7日 (木) 21時05分

« Box2D FLASH その4 MovieClipの表示 | トップページ | Box2D FLASH その6 2つのオブジェクトをつなげる »