アットウィキロゴ

gihyo



第2回 物理エンジンをセットアップし,箱を落とすFlashを作る

サンプルコード

+ ...
package {
import Box2D.Collision.b2AABB;
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 flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

/**
 * 床の上に箱を落とすFlash
 */
public class DropBox extends Sprite {
	private var world:b2World;
	
	public function DropBox():void {
		// イベントハンドラを登録する
		stage.addEventListener(MouseEvent.CLICK, clickHandler);
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
	}
	
	private function clickHandler(event:MouseEvent):void {
		////////////////////////////////////////
		// 物理エンジンのセットアップ
		
		// 外枠を定義する
		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);
		
		////////////////////////////////////////
		// 箱の設置
		// 箱は床の上から落として、少しはねるようにします
		
		// 箱の位置を左から2.5m、上から1mとする
		var boxBodyDef:b2BodyDef = new b2BodyDef();
		boxBodyDef.position.Set(2.5, 1);
		
		// 箱の形を、幅60cm、高さ40cmとして45度ぐらい右に回す
		var boxShapeDef:b2PolygonDef= new b2PolygonDef();
		boxShapeDef.SetAsOrientedBox(0.3, 0.2, new b2Vec2(0, 0), 0.8);
		boxShapeDef.density = 1;        // 密度 [kg/m^2]
		boxShapeDef.restitution = 0.2;  // 反発係数、通常は0~1
		
		// 箱を動く物体として作る
		var boxBody:b2Body = world.CreateDynamicBody(boxBodyDef);
		boxBody.CreateShape(boxShapeDef);
		boxBody.SetMassFromShapes();
		
		////////////////////////////////////////
		// 描画設定
		
		var debugDraw:b2DebugDraw = new b2DebugDraw();
		debugDraw.m_sprite = this;
		debugDraw.m_drawScale = 100; // 1mを100ピクセルにする
		debugDraw.m_fillAlpha = 0.3; // 不透明度
		debugDraw.m_lineThickness = 1; // 線の太さ
		debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;
		world.SetDebugDraw(debugDraw);
	}
	
	private function enterFrameHandler(event:Event):void {
		if (world == null) {
			return;
		}
		// Flashはデフォルトで秒間24フレームなので、
		// 物理シミュレーションを1/24秒進める
		world.Step(1 / 24, 10);
	}
}
}


メモ

+ ...
  • 単位系はSIを使う
    • メートル、キログラム、秒

  • スケール
    • 扱いやすいのは0.1m~10m
      • 理由は?

  • ワールドの登録
1. 大きさの定義
var worldAABB:b2AABB = new b2AABB();
worldAABB.lowerBound.Set(-100, -100);
worldAABB.upperBound.Set(100, 100);
2. 重力の設定
var gravity:b2Vec2 = new b2Vec2(0, 10);
3. new World
world = new b2World(worldAABB, gravity, true);
    • 外枠はAABB(Axis-Aligned Bounding Box)
      • 外枠の定義はメートル/ピクセルのスケールを考慮して
      • topleftとbottomrightを設定
    • 重力の方向は自由


  • オブジェクトの設置
    • 動くもの
1. 位置の定義
var boxBodyDef:b2BodyDef = new b2BodyDef();
boxBodyDef.position.Set(2.5, 1);
2. 形の定義
var boxShapeDef:b2PolygonDef= new b2PolygonDef();
boxShapeDef.SetAsOrientedBox(0.3, 0.2, new b2Vec2(0, 0), 0.8);
3. 重さ等の定義
boxShapeDef.density = 1; // 密度 [kg/m^2]
boxShapeDef.restitution = 0.2; // 反発係数、通常は0~1
4. ワールドへの登録
var boxBody:b2Body = world.CreateDynamicBody(boxBodyDef);
boxBody.CreateShape(boxShapeDef);
boxBody.SetMassFromShapes();

    • 動かないもの
1. 位置の定義
var floorBodyDef:b2BodyDef = new b2BodyDef();
floorBodyDef.position.Set(2.5, 3);
2. 形の定義
var floorShapeDef:b2PolygonDef = new b2PolygonDef();
floorShapeDef.SetAsBox(2, 0.1);
3. ワールドへの登録
var floor:b2Body = world.CreateStaticBody(floorBodyDef);
floor.CreateShape(floorShapeDef);
      • なんで位置と形の定義が別々なんだろう?

  • 描画スケール
    • 1mあたりのピクセル数で指定
var debugDraw:b2DebugDraw = new b2DebugDraw();
debugDraw.m_sprite = this;
debugDraw.m_drawScale = 100; // 1mを100ピクセルにする

  • 時間進行
    • メインループ内で1ステップずつ進める
private function enterFrameHandler(event:Event):void {
// Flashはデフォルトで秒間24フレームなので、
// 物理シミュレーションを1/24秒進める
world.Step(1 / 24, 10);
}

第3回 マウスのドラッグ&ドロップで,好きなサイズの箱を作る


サンプルコード

+ ...
package {
import Box2D.Collision.b2AABB;
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 flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

/**
 * 床の上に箱を落とすFlash
 */
public class Tsumiki extends Sprite {
	private static const DRAW_SCALE:Number = 100;
	private var world:b2World;
	private var boxBegin:Point = new Point();
	
	public function Tsumiki():void {
		////////////////////////////////////////
		// 物理エンジンのセットアップ
		
		// 外枠を定義する
		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 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;
		world.SetDebugDraw(debugDraw);
		
		// イベントハンドラを登録する
		stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
		stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
	}
	
	private function mouseDownHandler(event:MouseEvent):void {
		// マウスが押された場所を記憶しておく
		boxBegin.x = event.stageX;
		boxBegin.y = event.stageY;
	}
	
	private function mouseUpHandler(event:MouseEvent):void {
		// 箱の場所と大きさを計算する
		var x:Number = (event.stageX + boxBegin.x) / 2 / DRAW_SCALE;
		var y:Number = (event.stageY + boxBegin.y) / 2 / DRAW_SCALE;
		var halfWidth:Number = Math.abs(event.stageX - boxBegin.x) / 2 / DRAW_SCALE;
		var halfHeight:Number = Math.abs(event.stageY - boxBegin.y) / 2 / DRAW_SCALE;
		
		// 小さすぎる箱ができるのを防ぐ
		if (halfWidth < 0.05 || halfHeight < 0.05) {
			return;
		}
		
		// 箱の場所を設定する
		var bodyDef:b2BodyDef = new b2BodyDef();
		bodyDef.position.Set(x, y);
		
		// 箱の大きさなどを設定する
		var shapeDef:b2PolygonDef= new b2PolygonDef();
		shapeDef.SetAsBox(halfWidth, halfHeight);
		shapeDef.density = 1;     // 密度 [kg/m^2]
		shapeDef.restitution = 0;  // 反発係数、通常は0~1
		
		// 箱を動く物体として作る
		var body:b2Body = world.CreateDynamicBody(bodyDef);
		body.CreateShape(shapeDef);
		body.SetMassFromShapes();
	}
	
	private function enterFrameHandler(event:Event):void {
		// Flashはデフォルトで秒間24フレームなので、
		// 物理シミュレーションを1/24秒進める
		world.Step(1 / 24, 10);
	}
}
}

メモ

+ ...
  • マウス座標とワールド座標の変換
    • 位置: SI
    • マウス: ピクセル
物理エンジン内の位置 = マウスの座標 / m_drawScale


第4回 いろいろな形の物体を作る

サンプルコード

+ ...
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 flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

public class DrawingCircle extends Sprite {
	// 物理エンジン内の1mを表すためのピクセル数
	private static const DRAW_SCALE:Number = 100;
	// 物理エンジンの管理クラス
	private var world:b2World;
	// 円の中心
	private var circleCenter:Point = new Point();
	
	public function DrawingCircle() {
		////////////////////////////////////////
		// 物理エンジンのセットアップ
		
		// 外枠を定義する
		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 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;
		world.SetDebugDraw(debugDraw);
		
		// イベントハンドラを登録する
		stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
		stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
	}
	
	private function mouseDownHandler(event:MouseEvent):void {
		// マウスが押された場所を記憶しておく
		circleCenter.x = event.stageX;
		circleCenter.y = event.stageY;
	}
	
	private function mouseUpHandler(event:MouseEvent):void {
		// 円の中心座標
		var x:Number = circleCenter.x / DRAW_SCALE;
		var y:Number = circleCenter.y / DRAW_SCALE;
		// 円の中心からマウスが離された位置までの距離
		var dx:Number = (event.stageX - circleCenter.x) / DRAW_SCALE;
		var dy:Number = (event.stageY - circleCenter.y) / DRAW_SCALE;
		// 円の半径
		var radius:Number = Math.sqrt(dx * dx + dy * dy);
		
		// 小さすぎる円ができるのを防ぐ
		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;     // 密度 [kg/m^2]
		shapeDef.restitution = 0.5;  // 反発係数、通常は0~1
		
		// 円を動く物体として作る
		var body:b2Body = world.CreateDynamicBody(bodyDef);
		body.CreateShape(shapeDef);
		body.SetMassFromShapes();
	}
	
	private function enterFrameHandler(event:Event):void {
		// Flashはデフォルトで秒間24フレームなので、
		// 物理シミュレーションを1/24秒進める
		world.Step(1 / 24, 10);
	}
}
}
+ ...
package {
import Box2D.Collision.b2AABB;
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 flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

public class DrawingPolygon extends Sprite {
	// 物理エンジン内の1mを表すためのピクセル数
	private static const DRAW_SCALE:Number = 100;
	// 物理エンジンの管理クラス
	private var world:b2World;
	// ポリゴンの頂点。配列の要素数を増やすこともできます
	private var points:Array = [new Point(), new Point(), new Point(), new Point(), new Point()];
	// クリックしたときに打たれる頂点の番号
	private var pointIndex:int = 0;
	
	public function DrawingPolygon() {
		////////////////////////////////////////
		// 物理エンジンのセットアップ
		
		// 外枠を定義する
		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 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;
		world.SetDebugDraw(debugDraw);
		
		// イベントハンドラを登録する
		stage.addEventListener(MouseEvent.CLICK, clickHandler);
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
	}
	
	private function clickHandler(event:MouseEvent):void {
		// 点を打った場所を配列に保存する
		points[pointIndex].x = event.stageX / DRAW_SCALE;
		points[pointIndex].y = event.stageY / DRAW_SCALE;
		
		// 打っている点の番号を進め、全部の点を打っていなければここで終わる
		pointIndex++;
		if (pointIndex < points.length) {
			return;
		}
		// 点の番号をリセットする
		pointIndex = 0;
		
		// ポリゴンの中心座標を計算する
		var center:Point = new Point();
		var i:int;
		for (i = 0; i < points.length; ++i) {
			center.x += points[i].x;
			center.y += points[i].y;
		}
		center.x /= points.length;
		center.y /= points.length;
		
		// ポリゴンの中心座標を設定する
		var bodyDef:b2BodyDef = new b2BodyDef();
		bodyDef.position.Set(center.x, center.y);
		
		// ポリゴンの頂点などを設定する
		var shapeDef:b2PolygonDef = new b2PolygonDef();
		// 頂点の数
		shapeDef.vertexCount = points.length;
		// それぞれの頂点
		for (i = 0; i < points.length; ++i) {
			shapeDef.vertices[i].Set(points[i].x - center.x, points[i].y - center.y);
		}
		shapeDef.density = 1;     // 密度 [kg/m^2]
		shapeDef.restitution = 0; // 反発係数、通常は0~1
		shapeDef.friction = 0.5;  // 摩擦係数、通常は0~1
		
		// ポリゴンを動く物体として作る
		var body:b2Body = world.CreateDynamicBody(bodyDef);
		body.CreateShape(shapeDef);
		body.SetMassFromShapes();
	}
	
	private function enterFrameHandler(event:Event):void {
		// Flashはデフォルトで秒間24フレームなので、
		// 物理シミュレーションを1/24秒進める
		world.Step(1 / 24, 10);
		
		// 点を打った場所に円を描く
		var i:int;
		for (i = 0; i < pointIndex; ++i) {
			graphics.drawCircle(
				points[i].x * DRAW_SCALE,
				points[i].y * DRAW_SCALE,
				5);
		}
	}
}
}

メモ

+ ...
  • 円を置く
b2PolygonDefとb2CircleDefはb2ShapeDefクラスの派生クラスになっています。
1. 位置の定義
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(x, y);
2. 形の定義
var shapeDef:b2CircleDef = new b2CircleDef();
shapeDef.radius = radius;
3. 重さ(密度)等の定義
shapeDef.density = 1; // 密度 [kg/m^2]
shapeDef.restitution = 0.5; // 反発係数、通常は0~1

  • 三角形ポリゴンを置く
    • 三角形が描ければ(だいたい)何でも描ける!(CG的な意味で
1. 頂点の定義
三角形の場合はvertexCountとverticesを使います。
    • 四角形の場合はきっと内部でこれをやってくれてる
var shapeDef:b2PolygonDef = new b2PolygonDef();
shapeDef.vertexCount = points.length;
for (i = 0; i < points.length; ++i) {
shapeDef.vertices[i].Set(points[i].x - center.x, points[i].y - center.y);
}
2. 重さ等の定義
shapeDef.density = 1; // 密度 [kg/m^2]
shapeDef.restitution = 0; // 反発係数、通常は0~1
shapeDef.friction = 0.5; // 摩擦係数、通常は0~1
3. ワールドへの登録
var body:b2Body = world.CreateDynamicBody(bodyDef);
body.CreateShape(shapeDef);
body.SetMassFromShapes();
最終更新:2008年12月06日 00:23