アットウィキロゴ

gihyo2





第5回 ジョイントで物をつなぐ

サンプルコード

+ ...
package {
import Box2D.Collision.b2AABB;
import Box2D.Collision.Shapes.b2CircleDef;
import Box2D.Collision.Shapes.b2PolygonDef;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2DebugDraw;
import Box2D.Dynamics.b2World;
import Box2D.Dynamics.Joints.b2Joint;
import Box2D.Dynamics.Joints.b2MouseJoint;
import Box2D.Dynamics.Joints.b2MouseJointDef;
import Box2D.Dynamics.Joints.b2RevoluteJoint;
import Box2D.Dynamics.Joints.b2RevoluteJointDef;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

public class DraggableCar extends Sprite {
	// 物理エンジン内の1mを表すためのピクセル数
	private static const DRAW_SCALE:Number = 100;
	// 物理エンジンの管理クラス
	private var world:b2World;
	// 車体
	private var body:b2Body;
	// マウスジョイントの定義
	private var mouseJointDef:b2MouseJointDef;
	// マウスジョイント
	private var mouseJoint:b2MouseJoint;
	
	public function DraggableCar() {
		////////////////////////////////////////
		// 物理エンジンのセットアップ
		
		// 外枠を定義する
		var worldAABB:b2AABB = new b2AABB();
		worldAABB.lowerBound.Set(-100, -100);
		worldAABB.upperBound.Set(100, 100);
		
		// 重力を下方向に10m/s^2とする
		var gravity:b2Vec2 = new b2Vec2(0, 10);
		
		// 外枠と重力を指定して、物理エンジン全体をセットアップする
		world = new b2World(worldAABB, gravity, true);
		
		////////////////////////////////////////
		// 床の設置
		// 床は画面の下のほうに設置します
		
		// 床の位置を左から2.5m、上から3mとする
		var floorBodyDef:b2BodyDef = new b2BodyDef();
		floorBodyDef.position.Set(2.5, 3);
		
		// 床の形を、幅4m、厚さ20cmとする
		// 指定するのはその半分の値
		var floorShapeDef:b2PolygonDef = new b2PolygonDef();
		floorShapeDef.SetAsBox(2, 0.1);
		
		// 床を動かない物体として作る
		var floor:b2Body = world.CreateStaticBody(floorBodyDef);
		floor.CreateShape(floorShapeDef);
		
		////////////////////////////////////////
		// 車体(body)を作る
		// 幅が80cm、高さが20cmで、場所は床の中心から少し上のところ
		
		var bodyDef:b2BodyDef = new b2BodyDef();
		bodyDef.position.Set(2.5, 2);
		
		var bodyShape:b2PolygonDef = new b2PolygonDef();
		bodyShape.SetAsBox(0.4, 0.1);
		bodyShape.density = 1;
		
		body = world.CreateDynamicBody(bodyDef);
		body.CreateShape(bodyShape);
		body.SetMassFromShapes();
		
		////////////////////////////////////////
		// 前輪(frontWheel)を作る
		
		// bodyDefは再利用できる
		bodyDef.position.Set(2.8, 2);
		
		var wheelShape:b2CircleDef = new b2CircleDef();
		wheelShape.radius = 0.15;
		wheelShape.density = 1;
		
		var frontWheel:b2Body = world.CreateDynamicBody(bodyDef);
		frontWheel.CreateShape(wheelShape);
		frontWheel.SetMassFromShapes();
		
		////////////////////////////////////////
		// 後輪(rearWheel)を作る
		
		bodyDef.position.Set(2.2, 2);
		
		// wheelShapeも再利用できる
		var rearWheel:b2Body = world.CreateDynamicBody(bodyDef);
		rearWheel.CreateShape(wheelShape);
		rearWheel.SetMassFromShapes();
		
		////////////////////////////////////////
		// 車体と前輪をつなぐ
		
		// ジョイントを定義するjointDef変数
		var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
		// つながれる2つの物体と、つなぐ位置(前輪の中央)を使って、定義を初期化する
		jointDef.Initialize(body, frontWheel, frontWheel.GetWorldCenter());
		// 回転速度の設定(6秒で1回転ぐらい)
		jointDef.motorSpeed = 1;
		// トルクの設定(大きいほど坂道に強くなる)
		jointDef.maxMotorTorque = 1;
		// 車輪を回すようにする
		jointDef.enableMotor = true;
		
		// 回転ジョイントを作る
		var frontJoint:b2RevoluteJoint = b2RevoluteJoint(world.CreateJoint(jointDef));
		
		////////////////////////////////////////
		// 車体と後輪をつなぐ
		
		// つながれる2つの物体と、つなぐ位置(後輪の中央)を使って、定義を初期化する
		jointDef.Initialize(body, rearWheel, rearWheel.GetWorldCenter());
		// 回転ジョイントを作る
		var rearJoint:b2RevoluteJoint = b2RevoluteJoint(world.CreateJoint(jointDef));
		
		////////////////////////////////////////
		// マウスジョイントの定義を作る
		
		// マウスジョイントを定義する
		mouseJointDef = new b2MouseJointDef();
		// 片方のbodyにはワールド全体を設定する
		mouseJointDef.body1 = world.GetGroundBody();
		// もう片方には移動させたい物体(=車体)を設定する
		mouseJointDef.body2 = body;
		// 車体の中心座標を設定する
		mouseJointDef.target = body.GetWorldCenter();
		// マウスで引っ張られるときの力
		mouseJointDef.maxForce = 5;
		// シミュレーションの間隔
		mouseJointDef.timeStep = 1 / 24;
		
		////////////////////////////////////////
		// 描画設定
		
		var debugDraw:b2DebugDraw = new b2DebugDraw();
		debugDraw.m_sprite = this;
		debugDraw.m_drawScale = DRAW_SCALE; // 1mを100ピクセルにする
		debugDraw.m_fillAlpha = 0.3; // 不透明度
		debugDraw.m_lineThickness = 1; // 線の太さ
		debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
		world.SetDebugDraw(debugDraw);
		
		// イベントハンドラを登録する
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
		stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
		stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
	}
	
	private function mouseDownHandler(event:MouseEvent):void {
		// マウスボタンが押されたら、マウスジョイントを作る
		mouseJoint = b2MouseJoint(world.CreateJoint(mouseJointDef));
	}
	
	private function mouseUpHandler(event:MouseEvent):void {
		// マウスボタンが離されたら、マウスボタンを切り離す
		world.DestroyJoint(mouseJoint);
		mouseJoint = null;
	}
	
	private function mouseMoveHandler(event:MouseEvent):void {
		// mouseJointがnullでないときだけ処理を実行する
		if (mouseJoint) {
			// マウスが押された場所を物理エンジン内の座標系に変換する
			var x:Number = event.stageX / DRAW_SCALE;
			var y:Number = event.stageY / DRAW_SCALE;
			// マウスジョイントのカーソル位置を更新
			mouseJoint.SetTarget(new b2Vec2(x, y));
		}
	}
	
	private function enterFrameHandler(event:Event):void {
		// Flashはデフォルトで秒間24フレームなので、
		// 物理シミュレーションを1/24秒進める
		world.Step(1 / 24, 10);
	}
}
}

メモ

+ ...
  • オブジェクトの使い回し
    • 使い回せるもの
      • ワールドに渡したもの
    • 使い回せないもの
      • ワールドから渡されたもの
    • 基本的には、実体渡し・参照受け取り?
      • 「実体渡し」というか、参照渡してBox2D内部でcloneだと思う
      • 微妙にわかりづらくない?
      • 全部参照渡しで統一した方が書きやすい気はする、C++版ではそうなってるのかも

ディスタンスジョイントは,物体と物体の距離を一定に保つだけのシンプルなジョイントです。b2DistanceJointDefクラスで定義されます。古い時計についている振り子のようなイメージです。
    • 回転ジョイント
回転ジョイントは,物体と物体をピンのようなものでつなぎ,回転させるジョイントです。b2RevoluteJointDefクラスで定義されます。人間で例えると,腕や足などの関節です。関節を中心に物を回すこともできます。
    • 直動ジョイント
直動ジョイントは,ピストンのような動きをするジョイントです。b2PrismaticJointDefクラスで定義されます。直線的な動きをするので,ピンボールのボールを発射するところなどで使うといいでしょう。
    • プーリージョイント
プーリージョイントは,名前のとおりプーリーの役割を果たすジョイントです。b2PulleyJointDefクラスで定義されます。ディスタンスジョイントを使っていて途中で方向を曲げたくなったらプーリージョイントを使います。
    • ギアジョイント
ギアジョイントは,ギアによってつながれている関係を表すジョイントです。b2GearJointDefクラスで定義されます。几帳面に多角形を組み合わせてギアを作ることもできますが,このギアジョイントを使えばギアの仕組みが簡単に作れます。
    • マウスジョイント
マウスジョイントは,マウスで物を動かすためのジョイントです。b2MouseJointDefクラスで定義されます。マニュアルにはテスト用と書かれていますが,使い道によっては十分活用できるジョイントです。

  • ジョイント作成の流れ
      • 「ジョイント定義オブジェクト」と「ジョイントオブジェクト」は、基本的に別物
1. ジョイント定義オブジェクトの作成
var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
2. 結びつけるオブジェクトの設定
jointDef.Initialize(body, frontWheel, frontWheel.GetWorldCenter());
3. 諸々のパラメータ登録
jointDef.motorSpeed = 1;
jointDef.maxMotorTorque = 1;
jointDef.enableMotor = true;
4. ジョイントオブジェクトのワールドへの登録
private function mouseDownHandler(event:MouseEvent):void {
// マウスボタンが押されたら、マウスジョイントを作る
mouseJoint = b2MouseJoint(world.CreateJoint(mouseJointDef));
}
private function mouseMoveHandler(event:MouseEvent):void {
// mouseJointがnullでないときだけ処理を実行する
if (mouseJoint) {
	// マウスが押された場所を物理エンジン内の座標系に変換する
	var x:Number = event.stageX / DRAW_SCALE;
	var y:Number = event.stageY / DRAW_SCALE;
	// マウスジョイントのカーソル位置を更新
	mouseJoint.SetTarget(new b2Vec2(x, y));
}
}
5. (必要に応じて)ジョイントオブジェクトのワールドからの登録解除
private function mouseUpHandler(event:MouseEvent):void {
// マウスボタンが離されたら、マウスボタンを切り離す
world.DestroyJoint(mouseJoint);
mouseJoint = null;
}




第6回 画像を使って表現力アップ

サンプルコード

+ ...
package {
import Box2D.Collision.b2AABB;
import Box2D.Collision.Shapes.b2CircleDef;
import Box2D.Collision.Shapes.b2PolygonDef;
import Box2D.Collision.Shapes.b2PolygonShape;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2DebugDraw;
import Box2D.Dynamics.b2World;
import Box2D.Dynamics.Joints.b2MouseJoint;
import Box2D.Dynamics.Joints.b2MouseJointDef;
import Box2D.Dynamics.Joints.b2RevoluteJoint;
import Box2D.Dynamics.Joints.b2RevoluteJointDef;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

public class CarFinal extends Sprite {
	// 物理エンジン内の1mを表すためのピクセル数
	private static const DRAW_SCALE:Number = 100;
	// 物理エンジンの管理クラス
	private var world:b2World;
	// 車体
	private var body:b2Body;
	// 前輪
	private var frontWheel:b2Body;
	// 後輪
	private var rearWheel:b2Body;
	// マウスジョイントの定義
	private var mouseJointDef:b2MouseJointDef;
	// マウスジョイント
	private var mouseJoint:b2MouseJoint;
	
	// Flashに画像を埋め込む
	[Embed(source='floor.png')]
	private static const FloorImage:Class;
	[Embed(source='body.png')]
	private static const BodyImage:Class;
	[Embed(source='wheel.png')]
	private static const WheelImage:Class;
	
	public function CarFinal() {
		////////////////////////////////////////
		// 物理エンジンのセットアップ
		
		// 外枠を定義する
		var worldAABB:b2AABB = new b2AABB();
		worldAABB.lowerBound.Set(-100, -100);
		worldAABB.upperBound.Set(100, 100);
		
		// 重力を下方向に10m/s^2とする
		var gravity:b2Vec2 = new b2Vec2(0, 10);
		
		// 外枠と重力を指定して、物理エンジン全体をセットアップする
		world = new b2World(worldAABB, gravity, true);
		
		////////////////////////////////////////
		// 床の設置
		// 床は画面の下のほうに設置します
		
		// 床の位置を左から2.5m、上から3mとする
		var floorBodyDef:b2BodyDef = new b2BodyDef();
		floorBodyDef.position.Set(2.5, 3);
		
		// 床の形を、幅4m、厚さ20cmとする
		// 指定するのはその半分の値
		var floorShapeDef:b2PolygonDef = new b2PolygonDef();
		floorShapeDef.SetAsBox(2, 0.1);
		
		// 床を動かない物体として作る
		var floor:b2Body = world.CreateStaticBody(floorBodyDef);
		floor.CreateShape(floorShapeDef);
		
		// 床の画像を作る
		var floorImage:Bitmap = new FloorImage();
		// 画像のサイズを合わせる
		floorImage.width = 4 * DRAW_SCALE;
		floorImage.height = 0.2 * DRAW_SCALE;
		// 床の中心が(0, 0)に来るように,左上にずらす
		floorImage.x = -floorImage.width / 2;
		floorImage.y = -floorImage.height / 2;
		
		// b2BodyのユーザデータとしてSpriteを作る
		floor.m_userData = new Sprite();
		// Spriteの場所を物理エンジン内の場所と一致させる
		floor.GetUserData().x = floor.GetWorldCenter().x * DRAW_SCALE;
		floor.GetUserData().y = floor.GetWorldCenter().y * DRAW_SCALE;
		floor.GetUserData().addChild(floorImage);
		addChild(floor.GetUserData());
		
		////////////////////////////////////////
		// 車体(body)を作る
		// 幅が80cm、高さが20cmで、場所は床の中心から少し上のところ
		
		var bodyDef:b2BodyDef = new b2BodyDef();
		bodyDef.position.Set(2.5, 2);
		
		var bodyShape:b2PolygonDef = new b2PolygonDef();
		bodyShape.SetAsBox(0.4, 0.1);
		bodyShape.density = 1;
		
		body = world.CreateDynamicBody(bodyDef);
		body.CreateShape(bodyShape);
		body.SetMassFromShapes();
		
		// 車体の画像を読み込んで表示する
		var bodyImage:Bitmap = new BodyImage()
		bodyImage.width = 0.8 * DRAW_SCALE;
		bodyImage.height = 0.2 * DRAW_SCALE;
		bodyImage.x = -bodyImage.width / 2;
		bodyImage.y = -bodyImage.height / 2;
		body.m_userData = new Sprite();
		body.GetUserData().x = body.GetWorldCenter().x * DRAW_SCALE;
		body.GetUserData().y = body.GetWorldCenter().y * DRAW_SCALE;
		body.GetUserData().addChild(bodyImage);
		addChild(body.GetUserData());
		
		////////////////////////////////////////
		// 前輪(frontWheel)を作る
		
		// bodyDefは再利用できる
		bodyDef.position.Set(2.8, 2);
		
		var wheelShape:b2CircleDef = new b2CircleDef();
		wheelShape.radius = 0.15;
		wheelShape.density = 1;
		
		frontWheel = world.CreateDynamicBody(bodyDef);
		frontWheel.CreateShape(wheelShape);
		frontWheel.SetMassFromShapes();
		
		// 前輪の画像を読み込んで表示する
		var frontWheelImage:Bitmap = new WheelImage();
		frontWheelImage.width = 0.3 * DRAW_SCALE;
		frontWheelImage.height = 0.3 * DRAW_SCALE;
		frontWheelImage.x = -frontWheelImage.width / 2;
		frontWheelImage.y = -frontWheelImage.height / 2;
		frontWheel.m_userData = new Sprite();
		frontWheel.GetUserData().x = frontWheel.GetWorldCenter().x * DRAW_SCALE;
		frontWheel.GetUserData().y = frontWheel.GetWorldCenter().y * DRAW_SCALE;
		frontWheel.GetUserData().addChild(frontWheelImage);
		addChild(frontWheel.GetUserData());
		
		////////////////////////////////////////
		// 後輪(rearWheel)を作る
		
		bodyDef.position.Set(2.2, 2);
		
		// wheelShapeも再利用できる
		rearWheel = world.CreateDynamicBody(bodyDef);
		rearWheel.CreateShape(wheelShape);
		rearWheel.SetMassFromShapes();
		
		// 後輪の画像を読み込んで表示する
		var rearWheelImage:Bitmap = new WheelImage();
		rearWheelImage.width = 0.3 * DRAW_SCALE;
		rearWheelImage.height = 0.3 * DRAW_SCALE;
		rearWheelImage.x = -rearWheelImage.width / 2;
		rearWheelImage.y = -rearWheelImage.height / 2;
		rearWheel.m_userData = new Sprite();
		rearWheel.GetUserData().x = rearWheel.GetWorldCenter().x * DRAW_SCALE;
		rearWheel.GetUserData().y = rearWheel.GetWorldCenter().y * DRAW_SCALE;
		rearWheel.GetUserData().addChild(rearWheelImage);
		addChild(rearWheel.GetUserData());
		
		////////////////////////////////////////
		// 車体と前輪をつなぐ
		
		// ジョイントを定義するjointDef変数
		var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
		// つながれる2つの物体と、つなぐ位置(前輪の中央)を使って、定義を初期化する
		jointDef.Initialize(body, frontWheel, frontWheel.GetWorldCenter());
		// 回転速度の設定(6で1秒に1回転ぐらい)
		jointDef.motorSpeed = 3;
		// トルクの設定(大きいほど坂道に強くなる)
		jointDef.maxMotorTorque = 1;
		// 車輪を回すようにする
		jointDef.enableMotor = true;
		
		// 回転ジョイントを作る
		var frontJoint:b2RevoluteJoint = b2RevoluteJoint(world.CreateJoint(jointDef));
		
		////////////////////////////////////////
		// 車体と後輪をつなぐ
		
		// つながれる2つの物体と、つなぐ位置(後輪の中央)を使って、定義を初期化する
		jointDef.Initialize(body, rearWheel, rearWheel.GetWorldCenter());
		
		// 回転ジョイントを作る
		var rearJoint:b2RevoluteJoint = b2RevoluteJoint(world.CreateJoint(jointDef));
		
		////////////////////////////////////////
		// マウスジョイントの定義を作る
		
		mouseJointDef = new b2MouseJointDef();
		mouseJointDef.body1 = world.GetGroundBody();
		mouseJointDef.body2 = body;
		mouseJointDef.target = body.GetWorldCenter();
		mouseJointDef.maxForce = 5;
		mouseJointDef.timeStep = 1 / 24;
		
		////////////////////////////////////////
		// 描画設定
		
		/*
		var debugDraw:b2DebugDraw = new b2DebugDraw();
		debugDraw.m_sprite = this;
		debugDraw.m_drawScale = DRAW_SCALE; // 1mを100ピクセルにする
		debugDraw.m_fillAlpha = 0.3; // 不透明度
		debugDraw.m_lineThickness = 1; // 線の太さ
		debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
		world.SetDebugDraw(debugDraw);
		*/
		
		// イベントハンドラを登録する
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
		stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
		stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
	}
	
	private function mouseMoveHandler(event:MouseEvent):void {
		if (mouseJoint) {
			// マウスが押された場所を物理エンジン内の座標系に変換する
			var x:Number = event.stageX / DRAW_SCALE;
			var y:Number = event.stageY / DRAW_SCALE;
			// マウスジョイントのカーソル位置を更新
			mouseJoint.SetTarget(new b2Vec2(x, y));
		}
	}
	
	private function mouseDownHandler(event:MouseEvent):void {
		mouseJoint = b2MouseJoint(world.CreateJoint(mouseJointDef));
	}
	
	private function mouseUpHandler(event:MouseEvent):void {
		world.DestroyJoint(mouseJoint);
		mouseJoint = null;
	}
	
	private function enterFrameHandler(event:Event):void {
		// ワールド内の全てのb2Bodyに対する処理
		for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
			if (b.GetUserData() is Sprite) {
				// 物理エンジン内での位置と回転角度を反映させる
				b.GetUserData().x = b.GetWorldCenter().x * DRAW_SCALE;
				b.GetUserData().y = b.GetWorldCenter().y * DRAW_SCALE;
				b.GetUserData().rotation = b.GetAngle() * 180 / Math.PI;
			}
		}
		
		// Flashはデフォルトで秒間24フレームなので、
		// 物理シミュレーションを1/24秒進める
		world.Step(1 / 24, 10);
	}
}
}

メモ

+ ...
  • イテレータ的な何か
    • DebugDrawはあくまで、Box2D内部の描画システムを利用する場合に使う
    • 自前で描画システムを構築するときは真面目に1つずつ更新する
private function enterFrameHandler(event:Event):void {
// ワールド内の全てのb2Bodyに対する処理
for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
	if (b.GetUserData() is Sprite) {
		// 物理エンジン内での位置と回転角度を反映させる
		b.GetUserData().x = b.GetWorldCenter().x * DRAW_SCALE;
		b.GetUserData().y = b.GetWorldCenter().y * DRAW_SCALE;
		b.GetUserData().rotation = b.GetAngle() * 180 / Math.PI;
	}
}

// Flashはデフォルトで秒間24フレームなので、
// 物理シミュレーションを1/24秒進める
world.Step(1 / 24, 10);
}



第7回 円を落として星を飛び散らせる[最終回]

サンプルコード

+ ...
package {
import Box2D.Collision.b2AABB;
import Box2D.Collision.b2ContactPoint;
import Box2D.Collision.Shapes.b2CircleDef;
import Box2D.Collision.Shapes.b2PolygonDef;
import Box2D.Collision.Shapes.b2ShapeDef;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2ContactListener;
import Box2D.Dynamics.b2DebugDraw;
import Box2D.Dynamics.b2World;
import caurina.transitions.Tweener;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

public class KiraKira extends Sprite {
	// 物理エンジン内の1mを表すためのピクセル数
	public static const DRAW_SCALE:Number = 100;
	// 物理エンジンの管理クラス
	private var world:b2World;
	// 星の画像
	[Embed(source='star.png')]
	private static const StarImage:Class;
	
	public function KiraKira() {
		////////////////////////////////////////
		// 物理エンジンのセットアップ
		
		// 外枠を定義する
		var worldAABB:b2AABB = new b2AABB();
		worldAABB.lowerBound.Set(-100, -100);
		worldAABB.upperBound.Set(100, 100);
		
		// 重力を下方向に10m/s^2とする
		var gravity:b2Vec2 = new b2Vec2(0, 10);
		
		// 外枠と重力を指定して、物理エンジン全体をセットアップする
		world = new b2World(worldAABB, gravity, true);
		
		////////////////////////////////////////
		// 床の設置
		// 床は画面の下のほうに設置します
		
		// 床の位置を左から2.5m、上から3mとする
		var floorBodyDef:b2BodyDef = new b2BodyDef();
		floorBodyDef.position.Set(2.5, 3);
		
		// 床の形を、幅4m、厚さ20cmとする
		// 指定するのはその半分の値
		var floorShapeDef:b2PolygonDef = new b2PolygonDef();
		floorShapeDef.SetAsBox(2, 0.1);
		
		// 床を動かない物体として作る
		var floor:b2Body = world.CreateStaticBody(floorBodyDef);
		floor.CreateShape(floorShapeDef);
		
		////////////////////////////////////////
		// コンタクトリスナ
		
		var contactListener:ContactListener = new ContactListener();
		contactListener.parent = this;
		world.SetListener(contactListener);
		
		////////////////////////////////////////
		// 描画設定
		
		var debugDraw:b2DebugDraw = new b2DebugDraw();
		debugDraw.m_sprite = this;
		debugDraw.m_drawScale = DRAW_SCALE; // 1mを100ピクセルにする
		debugDraw.m_fillAlpha = 0.3; // 不透明度
		debugDraw.m_lineThickness = 1; // 線の太さ
		debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
		world.SetDebugDraw(debugDraw);
		
		// イベントハンドラを登録する
		stage.addEventListener(MouseEvent.CLICK, clickHandler);
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
	}
	
	// 物と物が接触したときのエフェクトを発生させる
	public function addContactEffect(point:b2ContactPoint):void {
		// 星の画像を読み込んで、サイズと位置を調整する
		var starImage:Bitmap = new StarImage();
		starImage.width = 50;
		starImage.height = 50;
		starImage.x = -starImage.width / 2;
		starImage.y = -starImage.height / 2;
		
		// 星の画像を表示するためのSpriteを作る
		// 座標はコンタクトが発生した場所とする
		var sprite:Sprite = new Sprite();
		sprite.x = point.position.x * DRAW_SCALE;
		sprite.y = point.position.y * DRAW_SCALE;
		sprite.addChild(starImage);
		addChild(sprite);
		
		// 星が飛んでいく方向と距離をランダムに決める
		// 角度は0~360度、距離は50~150
		var angle:Number = Math.random() * 360;
		var length:Number = Math.random() * 100 + 50;
		// 星が飛んでいく先の座標を計算する
		var dx:Number = sprite.x + length * Math.cos(angle);
		var dy:Number = sprite.y + length * Math.sin(angle);
		
		// Tweenerで星を飛ばす
		Tweener.addTween(sprite, {
			time: 3, // 3秒間
			scaleX: 0, // 消えるまで縮小する
			scaleY: 0,
			alpha: 0, // 完全に透明にする
			rotation: 200, // 少し回転させる
			x: dx,
			y: dy,
			onComplete: function():void {
				removeChild(sprite);
			}
		});
	}
	
	private function clickHandler(event:MouseEvent):void {
		////////////////////////////////////////
		// クリックした点からボールを落とします
		var starDef:b2BodyDef = new b2BodyDef();
		starDef.position.Set(event.stageX / DRAW_SCALE, event.stageY / DRAW_SCALE);
		
		var starShapeDef:b2CircleDef = new b2CircleDef();
		starShapeDef.radius = 0.2;
		starShapeDef.restitution = 0.3;
		starShapeDef.density = 1;
		
		var star:b2Body = world.CreateDynamicBody(starDef);
		star.CreateShape(starShapeDef);
		star.SetMassFromShapes();
	}
	
	private function enterFrameHandler(event:Event):void {
		world.Step(1 / 24, 10);
	}
}
}

+ ...
package {
import Box2D.Collision.b2ContactPoint;
import Box2D.Dynamics.b2ContactListener;

public class ContactListener extends b2ContactListener {
	public var parent:KiraKira;
	
	public function ContactListener() {
	}
	
	// 新しく衝突が発生したときに呼び出されるメソッド
	public override function Add(point:b2ContactPoint):void {
		// 衝突時の力の大きさをもとに、エフェクトを発生させる数を計算する
		var num:int = point.normalForce / 10 + 1;
		// 計算ミスで大きな力がかかることがあるので、値を10までに制限する
		if (num > 10) {
			num = 10;
		}
		for (var i:int = 0; i < num; ++i) {
			parent.addContactEffect(point);
		}
	}
}
}

メモ

+ ...
  • 大雑把な流れ
1. コンタクトリスナクラスの定義
    • ここで定義するのは、あくまで「衝突応答以外の応答」
      • オブジェクト間の衝突検知/応答はBox2D内部で全部やってくれる
public class ContactListener extends b2ContactListener {
public var parent:KiraKira;

public function ContactListener() {
}

// 新しく衝突が発生したときに呼び出されるメソッド
public override function Add(point:b2ContactPoint):void {
	// 衝突時の力の大きさをもとに、エフェクトを発生させる数を計算する
	var num:int = point.normalForce / 10 + 1;
	// 計算ミスで大きな力がかかることがあるので、値を10までに制限する
	if (num > 10) {
		num = 10;
	}
	for (var i:int = 0; i < num; ++i) {
		parent.addContactEffect(point);
}
}

ちなみにnormalForceの単位はニュートンです。

2. コンタクトリスナの作成
var contactListener:ContactListener = new ContactListener();
contactListener.parent = this;
world.SetListener(contactListener);

3. コンタクトリスナから呼ばれる関数の定義
    • 型はvoid function(b2ContactPoint)で固定?
    • b2ContactPoinitの中身は衝突位置と衝突時の力
public function addContactEffect(point:b2ContactPoint):void {
}

最終更新:2008年12月06日 00:51