球と面の衝突判定実装ログ
現状
ある程度、使えるレベルにはなった。あとは、実際にゲームに使いつつ調整する感じで。
動作
十字キーで前後左右に移動。スペースでジャンプ(空中浮遊)。
コード
長いので、試しに「続きを読む」の方に入れてみる。一覧で見たときに重くならないように、上のFlashまで入れといた方が良いかも。
//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; private var m_InputSpace:int = 0; //Param private const PLAYER_VEL:Number = 200.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); m_BlockManager.m_PlayerSphere.m_CollisionVel.z = PLAYER_VEL * (m_InputU - m_InputD); if(m_InputSpace > 0){ m_BlockManager.m_PlayerSphere.m_CollisionVel.y = 150; } //コリジョンを元に位置更新 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; } if(event.keyCode == Keyboard.SPACE){ m_InputSpace = 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; } if(event.keyCode == Keyboard.SPACE){ m_InputSpace = 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, 1, 1], [0, 1, 1, 0, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], ], [ [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 0, 0, 0, 1], [1, 1, 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], ], //*/ ]; //=ブロックの管理と衝突管理(できれば分けたい)= 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 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 GRAVITY:Number = -400.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 = 20;//10; m_Camera.y = 40;//20; m_Camera.z = -100;//-50; m_Camera.focus = 1000; 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{ if(val == 0){ return; } var createPlate:Function = function(dir:CVector):void{ var preIndexX:int = indexX - dir.x; var preIndexY:int = indexY - dir.y; var preIndexZ:int = indexZ - dir.z; var nextIndexX:int = indexX + dir.x; var nextIndexY:int = indexY + dir.y; var nextIndexZ:int = indexZ + dir.z; var preVal:int; if(preIndexX < 0 || preIndexY < 0 || preIndexZ < 0){ preVal = 0; }else{ preVal = BLOCK_DATA[preIndexY][preIndexZ][preIndexX]; } var nextVal:int; if(nextIndexX >= BLOCK_DATA[0][0].length || nextIndexY >= BLOCK_DATA.length || nextIndexZ >= BLOCK_DATA[0].length){ nextVal = 0; }else{ nextVal = BLOCK_DATA[nextIndexY][nextIndexZ][nextIndexX]; } var pos:CVector; if(preVal == 0){ if(dir.x > 0){ 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.rotationY = 90; obj.x = pos.x; obj.y = pos.y; obj.z = pos.z; return obj; }() ); } if(dir.y > 0){ 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.rotationX = 90; obj.x = pos.x; obj.y = pos.y; obj.z = pos.z; return obj; }() ); } if(dir.z > 0){ 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.rotationY = 0; obj.x = pos.x; obj.y = pos.y; obj.z = pos.z; return obj; }() ); } }//end if preVal if(nextVal == 0){ if(dir.x > 0){ 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.rotationY = -90; obj.x = pos.x; obj.y = pos.y; obj.z = pos.z; return obj; }() ); } if(dir.y > 0){ 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.rotationX = -90; obj.x = pos.x; obj.y = pos.y; obj.z = pos.z; return obj; }() ); } if(dir.z > 0){ 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.rotationY = 180; obj.x = pos.x; obj.y = pos.y; obj.z = pos.z; return obj; }() ); } }//end if nextVal }; createPlate(AXIS_X); createPlate(AXIS_Y); createPlate(AXIS_Z); } ); } ); } ); var BOTTOM_W:Number = 200; var BOTTOM_D:Number = 200; //地面用の板を作成 var bottomPos:CVector = new CVector(0, -BLOCK_DATA.length/2 * H, 0); m_PlaneCollisionList.push(new CollisionPlane(bottomPos, AXIS_X, BOTTOM_W, AXIS_Z, BOTTOM_D)); //描画用の面 m_RootNode.addChild( function():DisplayObject3D { var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCCCCC), BOTTOM_W, BOTTOM_D, 8); obj.x = bottomPos.x; obj.y = bottomPos.y; obj.z = bottomPos.z; obj.rotationX = -90; return obj; }() ); } } public function UpdateCollision():void{ //Debug Main.m_Label.text = ""; //この秒数経ったものとして計算 var deltaTime:Number = 1.0/30.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.Normal().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; } // private var m_CameraRot:Number = 0;//カメラ回転用 //描画 public function Render():void{ //カメラを回転させてみる // m_CameraRot += 0.1; // m_Camera.x = 50 * Math.cos(m_CameraRot); // m_Camera.z = 50 * Math.sin(m_CameraRot); //描画 m_Scene.renderCamera(m_Camera); } }