球と面の衝突判定実装ログ

状況

線と円柱の衝突判定を組み込むも、上手く動かず。そこをオフにするととりあえず跳ねるので、平面の方は上手くいっているはず。やはり土日までかかる。
→少しいじって直した。ボールを動かして確認しないと大丈夫とは言えないが。土日で「プレイヤーを動かせるようにする:十字キーで前後左右に移動し、スペースキーでジャンプする」を組み込んでチェックする。

コード

そろそろ1000行を越えそうなので、そのまま転載というのはつらいかも。タブをそのままコピペしてるから枠をはみ出てるし。あとで整理するから、多少は縮まるだろうけど。

//mxmlc Main.as
//author Show=O=Healer

package {
	//Common
	import flash.display.*;
	import flash.events.*;

	//Debug
	import flash.text.*;
	import flash.ui.*;
	import flash.utils.*;

	//Input
	import flash.ui.Keyboard;

	//Class
	public class Main extends Sprite {
		//Block
		private var m_BlockManager:BlockManager;

		//Input
		private var m_InputL:int = 0;
		private var m_InputR:int = 0;
		private var m_InputU:int = 0;
		private var m_InputD:int = 0;

		//Param
		private const PLAYER_VEL:Number = 160.0;
		private const GRAVITY:Number = 0.0;//-1000.0;

		//Debug
		public static var m_Label:TextField;


		//Constructor
		public function Main():void {
			{//Init Block
				m_BlockManager = new BlockManager();
				addChild(m_BlockManager);
			}

			{//Init Input
				stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
				stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
			}

			{//Init Common
				//リサイズ対応
//				stage.addEventListener(Event.RESIZE, m_BlockManager.onStageResize);

				//定期的にupdateを呼ばせる
				addEventListener(Event.ENTER_FRAME, update);
			}

			{//Init Debug
				m_Label = new TextField();
				m_Label.autoSize = TextFieldAutoSize.LEFT;
				m_Label.selectable=false
				addChild(m_Label);
			}
		}


		//=Common=

		private function update( event:Event ):void {
			//プレイヤーの移動速度をセット
//			m_BlockManager.m_PlayerSphere.m_CollisionVel.x = PLAYER_VEL * (m_InputR - m_InputL);
//			if(m_InputU > 0){
//				m_BlockManager.m_PlayerSphere.m_CollisionVel.y = -PLAYER_VEL;
//			}

			//コリジョンを元に位置更新
			m_BlockManager.UpdateCollision();

			//描画
			m_BlockManager.Render();

			//更新した位置を表示に反映
//			m_Player.x = m_BlockManager.m_PlayerSphere.m_CollisionPos.x;
//			m_Player.y = m_BlockManager.m_PlayerSphere.m_CollisionPos.y;
		}

		//=Input=
		private function onKeyDown(event:KeyboardEvent):void{
			if(event.keyCode == Keyboard.LEFT){
				m_InputL = 1;
			}
			if(event.keyCode == Keyboard.RIGHT){
				m_InputR = 1;
			}
			if(event.keyCode == Keyboard.UP){
				m_InputU = 1;
			}
			if(event.keyCode == Keyboard.DOWN){
				m_InputD = 1;
			}
		}
		private function onKeyUp(event:KeyboardEvent):void{
			if(event.keyCode == Keyboard.LEFT){
				m_InputL = 0;
			}
			if(event.keyCode == Keyboard.RIGHT){
				m_InputR = 0;
			}
			if(event.keyCode == Keyboard.UP){
				m_InputU = 0;
			}
			if(event.keyCode == Keyboard.DOWN){
				m_InputD = 0;
			}
		}
	}
}


//=Local Class=

//Common
import flash.display.*;
import flash.events.*;

//Debug
import flash.text.*;
import flash.ui.*;
import flash.utils.*;

//Input
import flash.ui.Keyboard;

//PV3D
import org.papervision3d.core.*;
import org.papervision3d.core.proto.*;
import org.papervision3d.scenes.*;
import org.papervision3d.objects.*;
import org.papervision3d.cameras.*;
import org.papervision3d.materials.*;



//=球体コリジョン=
class CollisionSphere
{
	//中心位置
	public var m_CollisionPos:CVector;
	//移動速度
	public var m_CollisionVel:CVector = new CVector(0, 0, 0);
	//加速度
	public var m_CollisionAcc:CVector = new CVector(0, 0, 0);

	//半径
	public var m_CollisionRad:Number;

	//コンストラクタ
	public function CollisionSphere(in_Pos:CVector, in_R:Number):void {
		m_CollisionPos = in_Pos;

		m_CollisionRad = in_R;
	}
}


//=面コリジョン=
class CollisionPlane
{
	//中心位置
	public var m_CollisionPos:CVector;
//	public var m_CollisionVel:CVector = new CVector(0, 0, 0);
//	public var m_CollisionAcc:CVector = new CVector(0, 0, 0);

	//縦の方向
	public var m_AxisA:CVector;
	//縦の長さ(端から端まで)
	public var m_WidthA:Number;
	//横の方向
	public var m_AxisB:CVector;
	//横の長さ(端から端まで)
	public var m_WidthB:Number;

	//コンストラクタ
	public function CollisionPlane(in_Pos:CVector, in_AxisA:CVector, in_WidthA:Number, in_AxisB:CVector, in_WidthB:Number):void {
		m_CollisionPos = in_Pos;

		m_AxisA = in_AxisA;
		m_WidthA = in_WidthA;
		m_AxisB = in_AxisB;
		m_WidthB = in_WidthB;
	}
}


//=3次元ベクトル=
class CVector
{
	//メンバ
	public var x:Number = 0;
	public var y:Number = 0;
	public var z:Number = 0;

	//コンストラクタ
	public function CVector(in_x:Number = 0, in_y:Number = 0, in_z:Number = 0):void{
		x = in_x;
		y = in_y;
		z = in_z;
	}


	//長さ
	public function Length():Number{
		return Math.sqrt(x*x + y*y + z*z);
	}


	//=各種オペレータ=
	//本体には影響を与えない
	//vecA + vecB の代わりに vecA.Plus(vecB) とやる感じ
	//あるいは vecA.Dot(vecB) の延長で vecA.Plus(vecB) がある感じ

	//和
	public function Plus(rhs:CVector):CVector{
		return new CVector(x + rhs.x, y + rhs.y, z + rhs.z);
	}

	//差
	public function Minus(rhs:CVector):CVector{
		return new CVector(x - rhs.x, y - rhs.y, z - rhs.z);
	}

	//内積
	public function Dot(rhs:CVector):Number{
		return x * rhs.x + y * rhs.y + z * rhs.z;
	}

	//外積
	public function Cross(rhs:CVector):CVector{
		return new CVector(y * rhs.z - z * rhs.y, z * rhs.x - x * rhs.z, x * rhs.y - y * rhs.x);
	}

	//スケール
	public function Scale(scl:Number):CVector{
		return new CVector(x * scl, y * scl, z * scl);
	}

	//正規化
	public function Normal():CVector{
		var distance:Number = Length();
		if(distance > 0.0){
			return new CVector(x/distance, y/distance, z/distance);
		}
		return new CVector(0, 0, 0);//err
	}

	//線形補間
	public function Lerp(rhs:CVector, ratio:Number):CVector{
		//this * (1 - ratio) + rhs * ratio
		return Scale(1.0 - ratio).Plus(rhs.Scale(ratio));
	}

	//値の比較
	public function Equals(rhs:CVector):Boolean{
		return (x == rhs.x) && (y == rhs.y) && (z == rhs.z);
	}
}


//=ブロックの配置=
var BLOCK_DATA:Array = [
	[
		[0, 0, 1, 0, 0],
		[0, 1, 1, 1, 0],
		[1, 1, 1, 1, 1],
		[0, 1, 1, 1, 0],
		[0, 0, 1, 0, 0],
	],
	[
		[0, 0, 0, 0, 0],
		[0, 0, 1, 0, 0],
		[0, 1, 1, 1, 0],
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
	],
/*
	[
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
	],
//*/
];


//=ブロックの管理と衝突管理(できれば分けたい)=
class BlockManager extends Sprite
{

	//Common
	private var m_Container	: Sprite;

	//PV3D
	private var m_Scene		: Scene3D;
	private var m_Camera	: Camera3D;
//	private var m_Camera	: FreeCamera3D;
	private var m_RootNode	: DisplayObject3D;
//		private var m_BoundArea	: DisplayObject3D;
	private var m_Sphere	: DisplayObject3D;
	private var m_Cube		: DisplayObject3D;


	//=CollisionSphere=

	//プレイヤの球コリジョン(できればもっと自由に登録できるようにしたい)
	public var m_PlayerSphere:CollisionSphere = new CollisionSphere(new CVector(SPHERE_INIT_X, SPHERE_INIT_Y, SPHERE_INIT_Z), SPHERE_RAD);


	//=CollisionPlane=

	//面コリジョン
	private var m_PlaneCollisionList:Array = new Array();


	//Param
	private const BOUND_AREA_W:Number = 600;
	private const BOUND_AREA_H:Number = 200;
	private const BOUND_AREA_D:Number = 500;

	private const SPHERE_INIT_X:Number = 0;
	private const SPHERE_INIT_Y:Number = 50;//10;
	private const SPHERE_INIT_Z:Number = 0;//20;

	private const SPHERE_RAD:Number = 8;

	private const CAMERA_DISTANCE:Number = 400;

	private const GRAVITY:Number = -100.0;

	private const AXIS_X:CVector = new CVector(1, 0, 0);
	private const AXIS_Y:CVector = new CVector(0, 1, 0);
	private const AXIS_Z:CVector = new CVector(0, 0, 1);

	//ブロックの幅
	private const W:int = 20;
	private const H:int = 20;
	private const D:int = 20;


	//Constructor
	public function BlockManager():void {
		{//Init PV3D
			{//m_Container
				// 表示用の Sprite オブジェクトを生成
				m_Container = new Sprite();
				m_Container.x = 400 / 2; // at center : swf width	= 400
				m_Container.y = 400 / 2; // at center : swf height = 400
				addChild(m_Container);
			}

			{//m_Scene
				// シーンオブジェクトを作る
				m_Scene = new Scene3D(m_Container);
			}

			{//m_Camera
				// カメラオブジェクトを作る
				m_Camera = new Camera3D();
//				m_Camera = new FreeCamera3D();
				m_Camera.x = 10;
				m_Camera.y = 20;
				m_Camera.z = -50;
				m_Camera.focus = 500;
				m_Camera.zoom = 1;
			}

			{//m_RootNode
				// ルートノードを作る
				m_RootNode = new DisplayObject3D();
				m_Scene.addChild(m_RootNode);
			}

			{//m_Sphere
				m_Sphere = function():DisplayObject3D
				{
					var material:ColorMaterial = new ColorMaterial(0x000000);

					var sphere:DisplayObject3D = new Sphere(material, SPHERE_RAD);
					sphere.x = SPHERE_INIT_X;
					sphere.y = SPHERE_INIT_Y;
					sphere.z = SPHERE_INIT_Z;

					return sphere;
				}();

				m_RootNode.addChild(m_Sphere);
			}

			{//m_PlayerSphere
				m_PlayerSphere.m_CollisionAcc.y = GRAVITY;
			}

			//ブロックの描画の登録と、その境界線上にコリジョン配置
			//配列の中心が原点に来るようにする
			var centerPos:CVector = new CVector(0.5*W * BLOCK_DATA[0][0].length, 0.5*H * BLOCK_DATA.length, 0.5*D * BLOCK_DATA[0].length);
			BLOCK_DATA.forEach(
				function(innerArray:Array, indexY:int, array:Array):void{
					innerArray.forEach(
						function(innerArray2:Array, indexZ:int, array:Array):void{
							innerArray2.forEach(
								function(val:int, indexX:int, array2:Array):void{
									//境界線上に描画面とコリジョンを配置(端が抜けているが、今回は気にしない)
									var pos:CVector;
/*
									var val0:int;
									var val1:int;

									{//X
										{//0:1 〜 NUM-1:NUM
											val0 = val;
											if(indexX == BLOCK_DATA[0][0].length-1){
												val1 = 0;
											}else{
												val1 = BLOCK_DATA[indexY][indexZ][indexX+1];
											}

											if(val0 != val1){
												createPlane(indexX, indexX, indexZ, TYPE_X, val0);
											}
										}


										{//-1:0
											if(indexX == 0){
												if(0 != val){
													createPlane(indexX, indexX, indexZ, TYPE_X, 0);
												}
											}
										}
									}
//*/

									if(indexX > 0){
										if(BLOCK_DATA[indexY][indexZ][indexX-1] != val){
											pos = (new CVector(W * (indexX), H * (indexY+0.5), D * (indexZ+0.5))).Minus(centerPos);
											m_PlaneCollisionList.push(new CollisionPlane(pos, AXIS_Y, H, AXIS_Z, D));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCCCFF), D, H, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													if(val == 1){
														obj.rotationY = 90;
													}else{
														obj.rotationY = -90;
													}
													return obj;
												}()
											);

										}
										if(indexX == BLOCK_DATA[0][0].length-1){
											if(val == 1){
												pos = (new CVector(W * (indexX+1), H * (indexY+0.5), D * (indexZ+0.5))).Minus(centerPos);
												m_PlaneCollisionList.push(new CollisionPlane(pos, AXIS_Y, H, AXIS_Z, D));

												//描画用の面
												m_RootNode.addChild(
													function():DisplayObject3D
													{
														var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCCCFF), D, H, 8);
														obj.x = pos.x;
														obj.y = pos.y;
														obj.z = pos.z;
														obj.rotationY = -90;
														return obj;
													}()
												);
											}
										}
									}else{
										if(val == 1){
											pos = (new CVector(W * (indexX), H * (indexY+0.5), D * (indexZ+0.5))).Minus(centerPos);
											m_PlaneCollisionList.push(new CollisionPlane(pos, AXIS_Y, H, AXIS_Z, D));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCCCFF), D, H, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													obj.rotationY = 90;
													return obj;
												}()
											);

										}
									}

									if(indexY > 0){
										if(BLOCK_DATA[indexY-1][indexZ][indexX] != val){
											pos = (new CVector(W * (indexX+0.5), H * (indexY), D * (indexZ+0.5))).Minus(centerPos);
											m_PlaneCollisionList.push(new CollisionPlane(pos, AXIS_X, W, AXIS_Z, D));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCFFCC), W, D, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													if(val == 1){
														obj.rotationX = 90;
													}else{
														obj.rotationX = -90;
													}
													return obj;
												}()
											);
										}
										if(indexY == BLOCK_DATA.length-1){
											if(val == 1){
												pos = (new CVector(W * (indexX+0.5), H * (indexY+1), D * (indexZ+0.5))).Minus(centerPos);
												m_PlaneCollisionList.push(new CollisionPlane(pos, AXIS_X, W, AXIS_Z, D));

												//描画用の面
												m_RootNode.addChild(
													function():DisplayObject3D
													{
														var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCFFCC), W, D, 8);
														obj.x = pos.x;
														obj.y = pos.y;
														obj.z = pos.z;
														obj.rotationX = -90;
														return obj;
													}()
												);
											}
										}
									}else{
										if(val == 1){
											pos = (new CVector(W * (indexX+0.5), H * (indexY), D * (indexZ+0.5))).Minus(centerPos);
											m_PlaneCollisionList.push(new CollisionPlane(pos, AXIS_X, W, AXIS_Z, D));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCFFCC), W, D, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													obj.rotationX = 90;
													return obj;
												}()
											);

										}
									}

									if(indexZ > 0){
										if(BLOCK_DATA[indexY][indexZ-1][indexX] != val){
											pos = (new CVector(W * (indexX+0.5), H * (indexY+0.5), D * (indexZ))).Minus(centerPos);
											m_PlaneCollisionList.push(new CollisionPlane(pos, AXIS_X, W, AXIS_Y, H));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xFFCCCC), W, H, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													if(val == 1){
													}else{
														obj.rotationX = 180;
													}
													return obj;
												}()
											);
										}
										if(indexZ == BLOCK_DATA[0].length-1){
											if(val == 1){
												pos = (new CVector(W * (indexX+0.5), H * (indexY+0.5), D * (indexZ+1))).Minus(centerPos);
												m_PlaneCollisionList.push(new CollisionPlane(pos, AXIS_X, W, AXIS_Y, H));

												//描画用の面
												m_RootNode.addChild(
													function():DisplayObject3D
													{
														var obj:DisplayObject3D = new Plane(new ColorMaterial(0xFFCCCC), W, H, 8);
														obj.x = pos.x;
														obj.y = pos.y;
														obj.z = pos.z;
														obj.rotationX = -180;
														return obj;
													}()
												);
											}
										}
									}else{
										if(val == 1){
											pos = (new CVector(W * (indexX+0.5), H * (indexY+0.5), D * (indexZ))).Minus(centerPos);
											m_PlaneCollisionList.push(new CollisionPlane(pos, AXIS_X, W, AXIS_Y, H));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xFFCCCC), W, H, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
//													obj.rotationX = 180;
													return obj;
												}()
											);

										}
									}

								}
							);
						}
					);
				}
			);

		}

	}

	public function UpdateCollision():void{

		//Debug
		Main.m_Label.text = "";

		//この秒数経ったものとして計算
		var deltaTime:Number = 1.0/60.0;

		{//各「球コリジョン」に対して計算(今は一つだけ)
			//球のパラメータ
			var sphere:CollisionSphere = m_PlayerSphere;
			var sphereRad:Number = sphere.m_CollisionRad;
			var sphereBoundRatio:Number = 1.0;//円側の反射率

			//残りの処理時間(衝突のたびに、移動にかかった時間を引いていく)
			var restTime:Number = deltaTime;

			//現時点での位置など(衝突のたびに、その直後のパラメータをセット)
			var nowPos:CVector = sphere.m_CollisionPos;
			var nowVel:CVector = sphere.m_CollisionVel;
			var nowAcc:CVector = sphere.m_CollisionAcc;


			//衝突の計算をcountの許す限り行う
			for(var count:Number = 0; count < 5; count++)//continueでここまで戻ってきてもう一度処理する
			{
				//count以上の計算が必要な場合、すり抜ける可能性がある
				//ただ、これをなくすと無限ループに入る可能性がある

				//計算上の次の位置など
				//nextPos = nowPos + nowVel * restTime + 0.5 * nowAcc * restTime * restTime;
				//nextVel = nowVel + nowAcc * restTime;
				var nextPos:CVector = nowPos.Plus(nowVel.Scale(restTime)).Plus(nowAcc.Scale(0.5 * restTime * restTime));
				var nextVel:CVector = nowVel.Plus(nowAcc.Scale(restTime));
//Main.m_Label.text = Main.m_Label.text + "\n" +nowVel.y + ":"+ nowAcc.y;

				//移動しようとする量
				var movePos:CVector = nextPos.Minus(nowPos);
				//移動の長さ
				var moveDistance:Number = movePos.Length();

				//ヒットしたら、その情報をこれに入れる
				var hitPlane:CollisionPlane= null;
				var hitTime:Number = restTime;
				var hitPos:CVector = nowPos;
				var hitVel:CVector;

				//各面に対して衝突判定
				m_PlaneCollisionList.forEach(
					function(plane:CollisionPlane, index:int, arr:Array):void{
						//球と面のヒットチェック
						//相対的に考えて、点と「丸角で厚みを持つ直方体」のヒットで考える
						//直方体の側を「前後の両面」「四隅の球」「それらをつなぐ4つの円柱」に分けて判定する
						var planeBoundRatio:Number = 0.5;//plane側の反射率

						//=上下の面との衝突チェック=
						{
							var hitCheckPlane:Function = function(dir:Number):void{
								var planeNrm:CVector = plane.m_AxisB.Cross(plane.m_AxisA).Scale(dir).Normal();

								//面の中心位置
								var pos:CVector = plane.m_CollisionPos.Plus(planeNrm.Scale(sphereRad));

								//面の中心位置との相対位置
								var nowGap:CVector = nowPos.Minus(pos);
								var nextGap:CVector = nextPos.Minus(pos);

								if(planeNrm.Dot(nowGap) * planeNrm.Dot(nextGap) <= 0.0){
									//面をまたいで移動している

									if(planeNrm.Dot(movePos) <= 0.0){//こちらの面に向かってきている

										//線分と交差する場合の位置
										var localHitPos:CVector;
										if(planeNrm.Dot(movePos) < 0.0){
											//普通に向かってきている場合
											//交点を求める
											//hitpos = nowPos + (nextPos - nowPos) * (planeNrm.Dot(nowGap)/moveDistance);
											localHitPos = nowPos.Plus(nextPos.Minus(nowPos).Scale(Math.abs(planeNrm.Dot(nowGap)/moveDistance)));
										}else{
											//並行に入ってきているOR静止している
											if(Math.abs(planeNrm.Dot(nowGap)) > sphereRad){
												//中に居ないなら、最初の地点で判定
												localHitPos = nowPos;
											}else{
												//中に居るなら、外へ押し出す
												//nextPos - planeNrm * (sphereRad - Abs(planeNrm.Dot(pos - nextPos)))
												//nextPos + planeNrm * (sphereRad - Abs(planeNrm.Dot(pos - nextPos)))
												localHitPos = nextPos.Plus(planeNrm.Scale(sphereRad - Math.abs(planeNrm.Dot(nextGap))));
//												localHitPos = nextPos.Minus(planeNrm.Scale(sphereRad - Math.abs(planeNrm.Dot(nextGap))));
											}
										}

										//交差点と中心からの距離
										var hitDistanceA:Number = Math.abs(plane.m_AxisA.Dot(localHitPos.Minus(pos)));
										var hitDistanceB:Number = Math.abs(plane.m_AxisB.Dot(localHitPos.Minus(pos)));

										if(hitDistanceA <= 0.5*plane.m_WidthA && hitDistanceB <= 0.5*plane.m_WidthB){
											//その距離が幅以内ならヒットしている
											var ratio:Number;
											if(moveDistance > 0){
												ratio = nowPos.Minus(localHitPos).Length()/moveDistance;
											}else{
												ratio = 0;
											}
											var localHitVel:CVector = nowVel.Lerp(nextVel, ratio);//衝突時点の速度(近似。厳密には逆算が必要)
											//vel -= nrm * (1 + bound)*vel.Dot(nrm)
											localHitVel = localHitVel.Minus(planeNrm.Scale((1.0 + sphereBoundRatio * planeBoundRatio) * localHitVel.Dot(planeNrm)));//→反射時の速度に変更
											var localHitTime:Number = restTime * ratio;//衝突するまでにかかった時間(これも近似)
											if(localHitTime < hitTime){
												//他のより先に当たっていたら採用
												if(! hitPos.Equals(localHitPos)){
													//前回の衝突位置とは違ったら続ける
													hitPlane = plane;
													hitTime = localHitTime;
													hitPos = localHitPos;
													hitVel = localHitVel;
//		Main.m_Label.text = Main.m_Label.text + "\n" +dir + ":"+ ratio;
												}
											}
										}
									}
								}
							};

							hitCheckPlane(1);//片方のチェック
							hitCheckPlane(-1);//もう片方のチェック
						}

						//=四隅の球との衝突チェック=
						{
							var hitCheckSphere:Function = function(dirA:Number, dirB:Number):void{
								//球の中心位置(中心から、片方の軸の端に移動、さらにもう片方の軸に移動、という計算)
								var pos:CVector =
									plane.m_CollisionPos.Plus(
										plane.m_AxisA.Normal().Scale(dirA*0.5*plane.m_WidthA)).Plus(
											plane.m_AxisB.Normal().Scale(dirB*0.5*plane.m_WidthB));

								//球の中心と移動開始点の相対位置
								var gap:CVector = nowPos.Minus(pos);

								//垂線の長さ
								var distance:Number;
								if(moveDistance > 0.0){
									if(gap.Length() > 0.0){
										//球の中心から移動線に向かって下ろした垂線
										var perpendicular:CVector = movePos.Cross(movePos.Cross(gap)).Normal();

										distance = Math.abs(perpendicular.Dot(gap));
									}else{
										distance = 0.0;
									}
								}else{
									//動いてないなら、中心間の距離にしておく
									distance = gap.Length();
								}

								if(distance <= sphereRad){
									//そのまま移動し続ければ球の中を通る

									if(movePos.Dot(pos.Minus(nowPos)) >= 0.0){
										//球の内側に移動する場合のみ判定

										//垂線がdistance、斜線がsphereRadとした時の三角形の残りの辺の長さ
										var diff:Number = Math.sqrt(sphereRad*sphereRad - distance*distance);
										//ヒットするのに必要な距離
										var hitMove:Number;
										if(moveDistance > 0.0){
											hitMove = Math.abs(movePos.Normal().Dot(gap)) - diff;
										}else{
											hitMove = 0.0;//gap.Length();
										}
										if(hitMove <= moveDistance){
											//球の中まで届いた=ヒットした
											var ratio:Number;//想定していた移動量のうち、どれだけ移動したか
											var localHitPos:CVector;//ヒットした位置
											if(moveDistance > 0){
												//移動してたら、それを元に計算
												ratio = hitMove/moveDistance;
												localHitPos = nowPos.Plus(nextPos.Minus(nowPos).Scale(ratio));
											}else{
												//静止していたら、(めり込んでると思われるので)押し出し
												ratio = 0;
												localHitPos = pos.Plus(gap.Scale(sphereRad/gap.Length()));
											}
											var localHitVel:CVector = nowVel.Lerp(nextVel, ratio);//衝突時点の速度(近似)
											var nrm:CVector = localHitPos.Minus(pos).Normal();
											//vel -= nrm * (1 + bound)*vel.Dot(nrm)
											localHitVel = localHitVel.Minus(nrm.Scale((1.0 + sphereBoundRatio * planeBoundRatio) * localHitVel.Dot(nrm)));//→反射時の速度
											var localHitTime:Number = restTime * ratio;//ヒットまでにかかった時間(近似)
											if(localHitTime < hitTime){
												//他のより先に当たっていたら採用
												if(! hitPos.Equals(localHitPos)){
													//前回の衝突位置とは違ったら続ける
													hitPlane = plane;
													hitTime = localHitTime;
													hitPos = localHitPos;
													hitVel = localHitVel;
												}
											}
										}
									}
								}
							};

							hitCheckSphere( 1, 1);
							hitCheckSphere( 1,-1);
							hitCheckSphere(-1, 1);
							hitCheckSphere(-1,-1);
						}

						//=四つの円柱との衝突=
						{
							var hitCheckCylinder:Function = function(axisMain:CVector, axisSub:CVector, widthMain:Number, widthSub:Number):void{
								//円柱の中心位置(中心から、片方の軸の端に移動という計算)
								var pos:CVector =
									plane.m_CollisionPos.Plus(
										axisSub.Normal().Scale(0.5*widthSub));

								//円柱の中心と移動開始点の相対位置
								var gap:CVector = nowPos.Minus(pos);

								//垂線の長さ
								var distance:Number;
								var hoge:CVector;
								if(moveDistance > 0.0){
									//円柱の中心から移動線に向かって下ろした垂線
									var perpendicular:CVector = axisMain.Cross(movePos);

									if(perpendicular.Length() > 0.0){
										distance = Math.abs(perpendicular.Dot(gap));
										hoge = perpendicular.Cross(axisMain).Normal();
									}else{
										//移動方向が軸と同じ
										//下と同じ計算
										hoge = gap.Cross(axisMain).Cross(axisMain).Normal();//nowPosから円柱への垂線を入れておく
										distance = Math.abs(hoge.Dot(gap));
									}
//		Main.m_Label.text = Main.m_Label.text + "\n A:" + distance;
								}else{
									//動いてないなら、円柱から点への距離にしておく
									hoge = gap.Cross(axisMain).Cross(axisMain).Normal();//nowPosから円柱への垂線を入れておく
									distance = Math.abs(hoge.Dot(gap));
								}

								if(distance <= sphereRad){
									//そのまま移動し続ければ円柱の中を通る

									if(movePos.Dot(pos.Minus(nowPos)) >= 0.0){
										//円柱の内側に移動する場合のみ判定

										//円柱の軸方向の移動は相殺して考える

										var relMoveDistance:Number = Math.abs(hoge.Dot(nextPos.Minus(nowPos)));

										//垂線がdistance、斜線がsphereRadとした時の三角形の残りの辺の長さ
										var relDiff:Number = Math.sqrt(sphereRad*sphereRad - distance*distance);
										//ヒットするのに必要な距離
										var relHitMove:Number;
										if(relMoveDistance > 0.0){
											relHitMove = Math.abs(hoge.Dot(gap)) - relDiff;
										}else{
											relHitMove = 0.0;//gap.Length();
										}
										if(relHitMove <= relMoveDistance){
											//円柱の中まで届いた
											var ratio:Number;//想定していた移動量のうち、どれだけ移動したか
											var localHitPos:CVector;//ヒットした位置
											if(relMoveDistance > 0){
												//移動してたら、それを元に計算
												ratio = relHitMove/relMoveDistance;
												localHitPos = nowPos.Plus(nextPos.Minus(nowPos).Scale(ratio));
											}else{
												//静止していたら、(めり込んでると思われるので)押し出し
												ratio = 0;
												localHitPos = nowPos.Plus(hoge.Scale(sphereRad - distance));//円柱的な押し出し
											}
											var localHitVel:CVector = nowVel.Lerp(nextVel, ratio);//衝突時点の速度(近似)
											var nrm:CVector = localHitPos.Minus(pos).Cross(axisMain).Cross(axisMain).Normal();
											//vel -= nrm * (1 + bound)*vel.Dot(nrm)
											localHitVel = localHitVel.Minus(nrm.Scale((1.0 + sphereBoundRatio * planeBoundRatio) * localHitVel.Dot(nrm)));//→反射時の速度
											var localHitTime:Number = restTime * ratio;//ヒットまでにかかった時間(近似)

											var hitHeight:Number = Math.abs(localHitPos.Minus(pos).Dot(axisMain));
											if(hitHeight < widthMain){
												//ヒット箇所が円柱の範囲に収まっている=ヒット
												if(localHitTime < hitTime){
													//他のより先に当たっていたら採用
													if(! hitPos.Equals(localHitPos)){
														//前回の衝突位置とは違ったら続ける
														hitPlane = plane;
														hitTime = localHitTime;
														hitPos = localHitPos;
														hitVel = localHitVel;
//		Main.m_Label.text = Main.m_Label.text + "\n" +hitVel.y + ":"+ hitPos.y;
													}
												}
											}
										}
									}
								}
							};

							hitCheckCylinder(plane.m_AxisA, plane.m_AxisB, plane.m_WidthA, plane.m_WidthB);
							hitCheckCylinder(plane.m_AxisA, plane.m_AxisB.Scale(-1), plane.m_WidthA, plane.m_WidthB);
							hitCheckCylinder(plane.m_AxisB, plane.m_AxisA, plane.m_WidthB, plane.m_WidthA);
							hitCheckCylinder(plane.m_AxisB, plane.m_AxisA.Scale(-1), plane.m_WidthB, plane.m_WidthA);
						}
					}
				);

				//何かとぶつかっていたら、新しい位置と方向を元に再び処理を開始
				if(hitPlane != null){
					restTime -= hitTime;
					nowPos = hitPos;
					nowVel = hitVel;

					nowAcc = new CVector();//微小時間の加速度は無視してみる(地面スレスレを行く場合に止まってしまうので)

					continue;
				}

				//ぶつかっていなければ、求めた移動先に移動して終了
				sphere.m_CollisionPos = nextPos;
				sphere.m_CollisionVel = nextVel;

				break;
			}//end for count
		}//end scope

		//物理演算の結果→PV3D
		m_Sphere.x = m_PlayerSphere.m_CollisionPos.x;
		m_Sphere.y = m_PlayerSphere.m_CollisionPos.y;
		m_Sphere.z = m_PlayerSphere.m_CollisionPos.z;
	}

	//描画
	public function Render():void{
		//描画
		m_Scene.renderCamera(m_Camera);
	}

}