簡単なボールの衝突判定

Filed under AS3, 物理演算



物理計算の練習。

まずは、簡単な衝突判定と衝突後のベクトル計算。

以下のサイトを参考にしました。

サンプルはこちら

以下、ソースです。

package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;

	[SWF(backgroundColor="0xFFFFFF")]
	public class Physics01 extends Sprite
	{
		// はねかえり定数
		private static const E:Number = 0.8;
		// ボールの半径
		private static const RADIUS:Number = 40;
		// 摩擦
		private static const D:Number = 0.99;
		// ボールの数
		private static const BALL_NUM:int = 40;
		// ボールを保存する変数
		private var balls:Vector.<Ball>;
		// ドラッグ用
		private var drag_ball:int;
		private var mouse_x:Number;
		private var mouse_y:Number;

		// コンストラクタ
		public function Physics01()
		{
			// ステージの設定
			stage.frameRate=60;
			stage.quality=StageQuality.HIGH;
			stage.scaleMode=StageScaleMode.NO_SCALE;
			stage.align=StageAlign.TOP_LEFT;

			// ボールを保存するVector
			balls = new Vector.<Ball>(BALL_NUM, true);

			// ボールの作成
			for(var i:int=0 ; i<BALL_NUM ; i++)
			{
				var ball:Ball = addChild(new Ball(Math.random()*(stage.stageWidth - RADIUS), Math.random()*(stage.stageHeight - RADIUS), RADIUS, i)) as Ball;
				balls[i] = ball;
				ball.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
			}

			// ドラッグの用意
			mouse_x = mouseX;
			mouse_y = mouseY;
			drag_ball = -1;

			// フレームイベントの追加
			addEventListener(Event.ENTER_FRAME, onFrame);
		}

		// マウスアップ時に実行する関数
		private function onUp(e:MouseEvent):void
		{
			var ball:Ball = balls[drag_ball];
			drag_ball = -1;
			stage.removeEventListener(MouseEvent.MOUSE_UP , onUp);
			ball.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
		}

		// マウスダウン時に実行する関数
		private function onDown(e:MouseEvent):void
		{
			var ball:Ball = (e.target as Ball);
			drag_ball = ball.id;
			stage.addEventListener(MouseEvent.MOUSE_UP , onUp);
			ball.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
		}

		// フレームイベント用関数
		private function onFrame(evt:Event):void
		{
			// ドラッグ中のノードを計算
			var ball:Ball;
			if(drag_ball != -1)
			{
				var dx:Number = mouseX - mouse_x;
				var dy:Number = mouseY - mouse_y;
				ball = balls[drag_ball];
				ball.dx = dx;
				ball.dy = dy;
				ball.x = mouseX;
				ball.y = mouseY;
			}
			mouse_x = mouseX;
			mouse_y = mouseY;

			// 衝突判定
			var i:int;
			var j:int;
			var a:Ball;
			var b:Ball;
			for(i=0 ; i<BALL_NUM ; i++)
			{
				for(j = 0 ; j<BALL_NUM ; j++)
				{
					if( j<= i) continue;
					a = balls[i];
					b = balls[j];

					// ABベクトルを計算
					var AB_x:Number = b.x - a.x;
					var AB_y:Number = b.y - a.y;

					// 2つの円の中心間の距離
					var tr:Number = a.radius + b.radius;

					// 三平方の定理より衝突判定を行う
					if(AB_x * AB_x + AB_y * AB_y < tr * tr)
					{
						// 円がめりこんだ量を計算
						var len:Number = Math.sqrt(AB_x * AB_x + AB_y * AB_y);
						var distance:Number = (a.radius + b.radius) - len;
						if(len > 0)	len = 1 / len;
						AB_x *= len;
						AB_y *= len;			

						// めり込んだ量の半分ずつを追加
						distance /= 2.0;
						a.x -= AB_x * distance;
						a.y -= AB_y * distance;
						b.x += AB_x * distance;
						b.y += AB_y * distance;

						// 衝突後の速度を計算
						var s1:Number = a.dx * AB_x + a.dy*AB_y;
						var s2:Number = b.dx * AB_x + b.dy*AB_y;						

						var vn1_x:Number = AB_x * s1;
						var vn1_y:Number = AB_y * s1;

						var vn2_x:Number = AB_x * s2;
						var vn2_y:Number = AB_y * s2;

						var vt1_x:Number = a.dx - vn1_x;
						var vt1_y:Number = a.dy - vn1_y;

						var vt2_x:Number = b.dx - vn2_x;
						var vt2_y:Number = b.dy - vn2_y;

						var new_s1:Number = (a.m*s1 + b.m*s2 - b.m*E*(s1 - s2))/(a.m + b.m);
						var new_s2:Number = (a.m*s1 + b.m*s2 + a.m*E*(s1 - s2))/(a.m + b.m);

						var new_vn1_x:Number = AB_x * new_s1;
						var new_vn1_y:Number = AB_y * new_s1;

						var new_vn2_x:Number = AB_x * new_s2;
						var new_vn2_y:Number = AB_y * new_s2;

						a.dx = new_vn1_x + vt1_x;
						a.dy = new_vn1_y + vt1_y;

						b.dx = new_vn2_x + vt2_x;
						b.dy = new_vn2_y + vt2_y;
					}
				}
			}

			// 座標の更新
			for(i=0;i<BALL_NUM;i++)
			{
				ball = balls[i];

				// ドラッグされているものはスキップする
				if(ball.id != drag_ball)
				{
					// 速度に摩擦を乗算
					ball.dx *= D;
					ball.dy *= D;

					// 速度を座標に加算
					ball.x += ball.dx;
					ball.y += ball.dy;

					// 壁との当たり判定
					if(ball.x < RADIUS)
					{
						ball.x = RADIUS;
						ball.dx *= -1;
					}
					if(ball.y < RADIUS)
					{
						ball.y = RADIUS;
						ball.dy *= -1;
					}
					if(ball.x > stage.stageWidth-RADIUS)
					{
						ball.x = stage.stageWidth-RADIUS;
						ball.dx *= -1;
					}
					if(ball.y > stage.stageHeight-RADIUS)
					{
						ball.y = stage.stageHeight-RADIUS;
						ball.dy *= -1;
					}
				}
			}
		}
	}
}

// ボールクラス
import flash.display.Sprite;
import flash.filters.DropShadowFilter;
class Ball extends Sprite
{
	// 色をもつ配列
	private static const colors:Array = [0x770000, 0x007700, 0x000077, 0x777700];

	// 半径・ID・速度・質量
	public var radius:Number;
	public var id:int;
	public var dx:Number;
	public var dy:Number;
	public var m:Number;

	public function Ball(x:Number, y:Number, radius:Number, id:int)
	{
		// 変数初期化
		this.x = x;
		this.y = y;
		this.id = id;
		this.radius = radius;

		// radius・dx・dy・mの初期化
		dx = 6*Math.random() - 3;
		dy = 6*Math.random() - 3;
		m = 10;

		// 円の描写
		graphics.beginFill(colors[id%4],1);
		graphics.drawCircle(0,0, radius);
		graphics.endFill();
		graphics.beginFill(0xffffff,1);
		graphics.drawCircle(0,0,radius*0.5);
		graphics.endFill();

		// フィルタとか
		z = 0;
		cacheAsBitmap = true;
		filters = [new DropShadowFilter(4,45,0x444444, 0.5)];
	}
}

Post a Comment

Your email is never published nor shared.