inline bool MODEL :: collision(VECTOR pos, VECTOR rotate){ MV1_COLL_RESULT_POLY_DIM result; if(!cflag){ r += 2.5f; if(r > 250.0f) r = 250.0f; VECTOR pos1 = VGet(pos.x+600*sinf(rotate.y), pos.y+100.0f+200.0f-500*sinf(rotate.x), pos.z+600*cosf(rotate.y)); VECTOR pos2 = VGet(pos.x-200*sinf(rotate.y), pos.y+100.0f+200.0f+500*sinf(rotate.x), pos.z-200*cosf(rotate.y)); result = MV1CollCheck_Capsule(ModelHandle, 39, pos1, pos2, r); }else{ r -= 5.0f; if(r < 100.0f) r = 100.0f; VECTOR pos1 = VGet(pos.x+600*sinf(rotate.y), pos.y+100.0f+200.0f-500*sinf(rotate.x), pos.z+600*cosf(rotate.y)); VECTOR pos2 = VGet(pos.x-200*sinf(rotate.y), pos.y+100.0f+200.0f+500*sinf(rotate.x), pos.z-200*cosf(rotate.y)); result = MV1CollCheck_Capsule(ModelHandle, 39, pos1, pos2, r); } if(r == 100.0){ cflag = true; return false; } if(result.HitNum > 0){ MV1CollResultPolyDimTerminate(result); cflag = true; return true; } MV1CollResultPolyDimTerminate(result); cflag = false; return false; }
inline bool TARGET :: collision(VECTOR pos, VECTOR rotate){ VECTOR pos1 = VGet(pos.x+600*sinf(rotate.y), pos.y+100.0f+200.0f-500*sinf(rotate.x), pos.z+600*cosf(rotate.y)); VECTOR pos2 = VGet(pos.x-200*sinf(rotate.y), pos.y+100.0f+200.0f+500*sinf(rotate.x), pos.z-200*cosf(rotate.y)); MV1_COLL_RESULT_POLY_DIM result = MV1CollCheck_Capsule(ModelHandle, -1, pos1, pos2, 210.0f); if(result.HitNum > 0){ MV1CollResultPolyDimTerminate(result); return true; } MV1CollResultPolyDimTerminate(result); return false; }
bool Collision::Octree_Capsule(int modelID, const Capsule& capsule, HitInfo& hitInfo) { MV1_COLL_RESULT_POLY_DIM mv1HitInfo; mv1HitInfo = MV1CollCheck_Capsule(modelID, -1, Convert::ToVECTOR(capsule.p1), Convert::ToVECTOR(capsule.p2), capsule.radius); if (mv1HitInfo.HitNum < 1) { return false; } int hitNum = mv1HitInfo.HitNum; Vector3 pos; Vector3 normal; for (int i = 0; i < hitNum; ++i) { VECTOR p = mv1HitInfo.Dim[i].HitPosition; pos += Vector3(p.x, p.y, p.z); VECTOR n = mv1HitInfo.Dim[i].Normal; normal += Vector3(n.x, n.y, n.z); } pos /= hitNum; normal /= hitNum; hitInfo.intersect = pos; hitInfo.normal = normal.GetNormal(); MV1CollResultPolyDimTerminate(mv1HitInfo); return true; }
// モデルとカプセルの当たり判定 CollisionParameter Collisin::ModelCapsule(const ModelData& model, const Capsule& c) const{ CollisionParameter colpara; MV1_COLL_RESULT_POLY_DIM HitPolyDim; HitPolyDim = MV1CollCheck_Capsule( model.MHandle, model.MFrameIndex, c.startPos.ToVECTOR(), c.endPos.ToVECTOR(), c.radius); if (HitPolyDim.HitNum > 0) colpara.colFlag = true; MV1CollResultPolyDimTerminate(HitPolyDim); return colpara; }
// モデルと球の当たり判定 CollisionParameter Collisin::ModelSphere(const ModelData& model, const Sphere& s) const { CollisionParameter colpara; MV1_COLL_RESULT_POLY_DIM HitPolyDim; HitPolyDim = MV1CollCheck_Sphere( model.MHandle, model.MFrameIndex, s.position.ToVECTOR(), s.radius); if (HitPolyDim.HitNum >= 1){ colpara.colPos = HitPolyDim.Dim[0].Normal; colpara.colFlag = true; } MV1CollResultPolyDimTerminate(HitPolyDim); return colpara; }
// キャラクターの移動処理 void Chara_Move( CHARA *ch, VECTOR MoveVector ) { int i, j, k ; // 汎用カウンタ変数 int MoveFlag ; // 水平方向に移動したかどうかのフラグ( 0:移動していない 1:移動した ) int HitFlag ; // ポリゴンに当たったかどうかを記憶しておくのに使う変数( 0:当たっていない 1:当たった ) MV1_COLL_RESULT_POLY_DIM HitDim ; // キャラクターの周囲にあるポリゴンを検出した結果が代入される当たり判定結果構造体 int KabeNum ; // 壁ポリゴンと判断されたポリゴンの数 int YukaNum ; // 床ポリゴンと判断されたポリゴンの数 MV1_COLL_RESULT_POLY *Kabe[ CHARA_MAX_HITCOLL ] ; // 壁ポリゴンと判断されたポリゴンの構造体のアドレスを保存しておくためのポインタ配列 MV1_COLL_RESULT_POLY *Yuka[ CHARA_MAX_HITCOLL ] ; // 床ポリゴンと判断されたポリゴンの構造体のアドレスを保存しておくためのポインタ配列 MV1_COLL_RESULT_POLY *Poly ; // ポリゴンの構造体にアクセスするために使用するポインタ( 使わなくても済ませられますがプログラムが長くなるので・・・ ) HITRESULT_LINE LineRes ; // 線分とポリゴンとの当たり判定の結果を代入する構造体 VECTOR OldPos ; // 移動前の座標 VECTOR NowPos ; // 移動後の座標 // 移動前の座標を保存 OldPos = ch->Position ; // 移動後の座標を算出 NowPos = VAdd( ch->Position, MoveVector ) ; // キャラクターの周囲にあるステージポリゴンを取得する // ( 検出する範囲は移動距離も考慮する ) HitDim = MV1CollCheck_Sphere( stg.ModelHandle, -1, ch->Position, CHARA_ENUM_DEFAULT_SIZE + VSize( MoveVector ) ) ; // x軸かy軸方向に 0.01f 以上移動した場合は「移動した」フラグを1にする if( fabs( MoveVector.x ) > 0.01f || fabs( MoveVector.z ) > 0.01f ) { MoveFlag = 1 ; } else { MoveFlag = 0 ; } // 検出されたポリゴンが壁ポリゴン( XZ平面に垂直なポリゴン )か床ポリゴン( XZ平面に垂直ではないポリゴン )かを判断する { // 壁ポリゴンと床ポリゴンの数を初期化する KabeNum = 0 ; YukaNum = 0 ; // 検出されたポリゴンの数だけ繰り返し for( i = 0 ; i < HitDim.HitNum ; i ++ ) { // XZ平面に垂直かどうかはポリゴンの法線のY成分が0に限りなく近いかどうかで判断する if( HitDim.Dim[ i ].Normal.y < 0.000001f && HitDim.Dim[ i ].Normal.y > -0.000001f ) { // 壁ポリゴンと判断された場合でも、キャラクターのY座標+1.0fより高いポリゴンのみ当たり判定を行う if( HitDim.Dim[ i ].Position[ 0 ].y > ch->Position.y + 1.0f || HitDim.Dim[ i ].Position[ 1 ].y > ch->Position.y + 1.0f || HitDim.Dim[ i ].Position[ 2 ].y > ch->Position.y + 1.0f ) { // ポリゴンの数が列挙できる限界数に達していなかったらポリゴンを配列に追加 if( KabeNum < CHARA_MAX_HITCOLL ) { // ポリゴンの構造体のアドレスを壁ポリゴンポインタ配列に保存する Kabe[ KabeNum ] = &HitDim.Dim[ i ] ; // 壁ポリゴンの数を加算する KabeNum ++ ; } } } else { // ポリゴンの数が列挙できる限界数に達していなかったらポリゴンを配列に追加 if( YukaNum < CHARA_MAX_HITCOLL ) { // ポリゴンの構造体のアドレスを床ポリゴンポインタ配列に保存する Yuka[ YukaNum ] = &HitDim.Dim[ i ] ; // 床ポリゴンの数を加算する YukaNum ++ ; } } } } // 壁ポリゴンとの当たり判定処理 if( KabeNum != 0 ) { // 壁に当たったかどうかのフラグは初期状態では「当たっていない」にしておく HitFlag = 0 ; // 移動したかどうかで処理を分岐 if( MoveFlag == 1 ) { // 壁ポリゴンの数だけ繰り返し for( i = 0 ; i < KabeNum ; i ++ ) { // i番目の壁ポリゴンのアドレスを壁ポリゴンポインタ配列から取得 Poly = Kabe[ i ] ; // ポリゴンとキャラクターが当たっていなかったら次のカウントへ if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, CHARA_HIT_HEIGHT, 0.0f ) ), CHARA_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == FALSE ) continue ; // ここにきたらポリゴンとキャラクターが当たっているということなので、ポリゴンに当たったフラグを立てる HitFlag = 1 ; // 壁に当たったら壁に遮られない移動成分分だけ移動する { VECTOR SlideVec ; // キャラクターをスライドさせるベクトル // 進行方向ベクトルと壁ポリゴンの法線ベクトルに垂直なベクトルを算出 SlideVec = VCross( MoveVector, Poly->Normal ) ; // 算出したベクトルと壁ポリゴンの法線ベクトルに垂直なベクトルを算出、これが // 元の移動成分から壁方向の移動成分を抜いたベクトル SlideVec = VCross( Poly->Normal, SlideVec ) ; // それを移動前の座標に足したものを新たな座標とする NowPos = VAdd( OldPos, SlideVec ) ; } // 新たな移動座標で壁ポリゴンと当たっていないかどうかを判定する for( j = 0 ; j < KabeNum ; j ++ ) { // j番目の壁ポリゴンのアドレスを壁ポリゴンポインタ配列から取得 Poly = Kabe[ j ] ; // 当たっていたらループから抜ける if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, CHARA_HIT_HEIGHT, 0.0f ) ), CHARA_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == TRUE ) break ; } // j が KabeNum だった場合はどのポリゴンとも当たらなかったということなので // 壁に当たったフラグを倒した上でループから抜ける if( j == KabeNum ) { HitFlag = 0 ; break ; } } } else { // 移動していない場合の処理 // 壁ポリゴンの数だけ繰り返し for( i = 0 ; i < KabeNum ; i ++ ) { // i番目の壁ポリゴンのアドレスを壁ポリゴンポインタ配列から取得 Poly = Kabe[ i ] ; // ポリゴンに当たっていたら当たったフラグを立てた上でループから抜ける if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, CHARA_HIT_HEIGHT, 0.0f ) ), CHARA_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == TRUE ) { HitFlag = 1 ; break ; } } } // 壁に当たっていたら壁から押し出す処理を行う if( HitFlag == 1 ) { // 壁からの押し出し処理を試みる最大数だけ繰り返し for( k = 0 ; k < CHARA_HIT_TRYNUM ; k ++ ) { // 壁ポリゴンの数だけ繰り返し for( i = 0 ; i < KabeNum ; i ++ ) { // i番目の壁ポリゴンのアドレスを壁ポリゴンポインタ配列から取得 Poly = Kabe[ i ] ; // キャラクターと当たっているかを判定 if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, CHARA_HIT_HEIGHT, 0.0f ) ), CHARA_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == FALSE ) continue ; // 当たっていたら規定距離分キャラクターを壁の法線方向に移動させる NowPos = VAdd( NowPos, VScale( Poly->Normal, CHARA_HIT_SLIDE_LENGTH ) ) ; // 移動した上で壁ポリゴンと接触しているかどうかを判定 for( j = 0 ; j < KabeNum ; j ++ ) { // 当たっていたらループを抜ける Poly = Kabe[ j ] ; if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, CHARA_HIT_HEIGHT, 0.0f ) ), CHARA_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == TRUE ) break ; } // 全てのポリゴンと当たっていなかったらここでループ終了 if( j == KabeNum ) break ; } // i が KabeNum ではない場合は全部のポリゴンで押し出しを試みる前に全ての壁ポリゴンと接触しなくなったということなのでループから抜ける if( i != KabeNum ) break ; } } } // 床ポリゴンとの当たり判定 if( YukaNum != 0 ) { // ジャンプ中且つ上昇中の場合は処理を分岐 if( ch->State == 2 && ch->JumpPower > 0.0f ) { float MinY ; // 天井に頭をぶつける処理を行う // 一番低い天井にぶつける為の判定用変数を初期化 MinY = 0.0f ; // 当たったかどうかのフラグを当たっていないを意味する0にしておく HitFlag = 0 ; // 床ポリゴンの数だけ繰り返し for( i = 0 ; i < YukaNum ; i ++ ) { // i番目の床ポリゴンのアドレスを床ポリゴンポインタ配列から取得 Poly = Yuka[ i ] ; // 足先から頭の高さまでの間でポリゴンと接触しているかどうかを判定 LineRes = HitCheck_Line_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, CHARA_HIT_HEIGHT, 0.0f ) ), Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) ; // 接触していなかったら何もしない if( LineRes.HitFlag == FALSE ) continue ; // 既にポリゴンに当たっていて、且つ今まで検出した天井ポリゴンより高い場合は何もしない if( HitFlag == 1 && MinY < LineRes.Position.y ) continue ; // ポリゴンに当たったフラグを立てる HitFlag = 1 ; // 接触したY座標を保存する MinY = LineRes.Position.y ; } // 接触したポリゴンがあったかどうかで処理を分岐 if( HitFlag == 1 ) { // 接触した場合はキャラクターのY座標を接触座標を元に更新 NowPos.y = MinY - CHARA_HIT_HEIGHT ; // Y軸方向の速度は反転 ch->JumpPower = -ch->JumpPower ; } } else { float MaxY ; // 下降中かジャンプ中ではない場合の処理 // 床ポリゴンに当たったかどうかのフラグを倒しておく HitFlag = 0 ; // 一番高い床ポリゴンにぶつける為の判定用変数を初期化 MaxY = 0.0f ; // 床ポリゴンの数だけ繰り返し for( i = 0 ; i < YukaNum ; i ++ ) { // i番目の床ポリゴンのアドレスを床ポリゴンポインタ配列から取得 Poly = Yuka[ i ] ; // ジャンプ中かどうかで処理を分岐 if( ch->State == 2 ) { // ジャンプ中の場合は頭の先から足先より少し低い位置の間で当たっているかを判定 LineRes = HitCheck_Line_Triangle( VAdd( NowPos, VGet( 0.0f, CHARA_HIT_HEIGHT, 0.0f ) ), VAdd( NowPos, VGet( 0.0f, -1.0f, 0.0f ) ), Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) ; } else { // 走っている場合は頭の先からそこそこ低い位置の間で当たっているかを判定( 傾斜で落下状態に移行してしまわない為 ) LineRes = HitCheck_Line_Triangle( VAdd( NowPos, VGet( 0.0f, CHARA_HIT_HEIGHT, 0.0f ) ), VAdd( NowPos, VGet( 0.0f, -40.0f, 0.0f ) ), Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) ; } // 当たっていなかったら何もしない if( LineRes.HitFlag == FALSE ) continue ; // 既に当たったポリゴンがあり、且つ今まで検出した床ポリゴンより低い場合は何もしない if( HitFlag == 1 && MaxY > LineRes.Position.y ) continue ; // ポリゴンに当たったフラグを立てる HitFlag = 1 ; // 接触したY座標を保存する MaxY = LineRes.Position.y ; } // 床ポリゴンに当たったかどうかで処理を分岐 if( HitFlag == 1 ) { // 当たった場合 // 接触したポリゴンで一番高いY座標をキャラクターのY座標にする NowPos.y = MaxY ; // Y軸方向の移動速度は0に ch->JumpPower = 0.0f ; // もしジャンプ中だった場合は着地状態にする if( ch->State == 2 ) { // 移動していたかどうかで着地後の状態と再生するアニメーションを分岐する if( MoveFlag ) { // 移動している場合は走り状態に Chara_PlayAnim( ch, 1 ) ; ch->State = 1 ; } else { // 移動していない場合は立ち止り状態に Chara_PlayAnim( ch, 4 ) ; ch->State = 0 ; } // 着地時はアニメーションのブレンドは行わない ch->AnimBlendRate = 1.0f ; } } else { // 床コリジョンに当たっていなくて且つジャンプ状態ではなかった場合は if( ch->State != 2 ) { // ジャンプ中にする ch->State = 2 ; // ちょっとだけジャンプする ch->JumpPower = CHARA_FALL_UP_POWER ; // アニメーションは落下中のものにする Chara_PlayAnim( ch, 3 ) ; } } } } // 新しい座標を保存する ch->Position = NowPos ; // キャラクターのモデルの座標を更新する MV1SetPosition( ch->ModelHandle, ch->Position ) ; // 検出したキャラクターの周囲のポリゴン情報を開放する MV1CollResultPolyDimTerminate( HitDim ) ; }
// カメラの処理 void Camera_Process( void ) { // パッドの3ボタンか、シフトキーが押されている場合のみ角度変更操作を行う if( CheckHitKey( KEY_INPUT_LSHIFT ) || ( inp.NowInput & PAD_INPUT_C ) ) { // 「←」ボタンが押されていたら水平角度をマイナスする if( inp.NowInput & PAD_INPUT_LEFT ) { cam.AngleH -= CAMERA_ANGLE_SPEED ; // −180度以下になったら角度値が大きくなりすぎないように360度を足す if( cam.AngleH < -DX_PI_F ) { cam.AngleH += DX_TWO_PI_F ; } } // 「→」ボタンが押されていたら水平角度をプラスする if( inp.NowInput & PAD_INPUT_RIGHT ) { cam.AngleH += CAMERA_ANGLE_SPEED ; // 180度以上になったら角度値が大きくなりすぎないように360度を引く if( cam.AngleH > DX_PI_F ) { cam.AngleH -= DX_TWO_PI_F ; } } // 「↑」ボタンが押されていたら垂直角度をマイナスする if( inp.NowInput & PAD_INPUT_UP ) { cam.AngleV -= CAMERA_ANGLE_SPEED ; // ある一定角度以下にはならないようにする if( cam.AngleV < -DX_PI_F / 2.0f + 0.6f ) { cam.AngleV = -DX_PI_F / 2.0f + 0.6f ; } } // 「↓」ボタンが押されていたら垂直角度をプラスする if( inp.NowInput & PAD_INPUT_DOWN ) { cam.AngleV += CAMERA_ANGLE_SPEED ; // ある一定角度以上にはならないようにする if( cam.AngleV > DX_PI_F / 2.0f - 0.6f ) { cam.AngleV = DX_PI_F / 2.0f - 0.6f ; } } } // カメラの注視点はプレイヤー座標から規定値分高い座標 cam.Target = VAdd( pl.CharaInfo.Position, VGet( 0.0f, CAMERA_PLAYER_TARGET_HEIGHT, 0.0f ) ) ; // カメラの座標を決定する { MATRIX RotZ, RotY ; float Camera_Player_Length ; MV1_COLL_RESULT_POLY_DIM HRes ; int HitNum ; // 水平方向の回転はY軸回転 RotY = MGetRotY( cam.AngleH ) ; // 垂直方向の回転はZ軸回転 ) RotZ = MGetRotZ( cam.AngleV ) ; // カメラからプレイヤーまでの初期距離をセット Camera_Player_Length = CAMERA_PLAYER_LENGTH ; // カメラの座標を算出 // X軸にカメラとプレイヤーとの距離分だけ伸びたベクトルを // 垂直方向回転( Z軸回転 )させたあと水平方向回転( Y軸回転 )して更に // 注視点の座標を足したものがカメラの座標 cam.Eye = VAdd( VTransform( VTransform( VGet( -Camera_Player_Length, 0.0f, 0.0f ), RotZ ), RotY ), cam.Target ) ; // 注視点からカメラの座標までの間にステージのポリゴンがあるか調べる HRes = MV1CollCheck_Capsule( stg.ModelHandle, -1, cam.Target, cam.Eye, CAMERA_COLLISION_SIZE ); HitNum = HRes.HitNum ; MV1CollResultPolyDimTerminate( HRes ) ; if( HitNum != 0 ) { float NotHitLength ; float HitLength ; float TestLength ; VECTOR TestPosition ; // あったら無い位置までプレイヤーに近づく // ポリゴンに当たらない距離をセット NotHitLength = 0.0f ; // ポリゴンに当たる距離をセット HitLength = Camera_Player_Length ; do { // 当たるかどうかテストする距離をセット( 当たらない距離と当たる距離の中間 ) TestLength = NotHitLength + ( HitLength - NotHitLength ) / 2.0f ; // テスト用のカメラ座標を算出 TestPosition = VAdd( VTransform( VTransform( VGet( -TestLength, 0.0f, 0.0f ), RotZ ), RotY ), cam.Target ) ; // 新しい座標で壁に当たるかテスト HRes = MV1CollCheck_Capsule( stg.ModelHandle, -1, cam.Target, TestPosition, CAMERA_COLLISION_SIZE ); HitNum = HRes.HitNum ; MV1CollResultPolyDimTerminate( HRes ) ; if( HitNum != 0 ) { // 当たったら当たる距離を TestLength に変更する HitLength = TestLength ; } else { // 当たらなかったら当たらない距離を TestLength に変更する NotHitLength = TestLength ; } // HitLength と NoHitLength が十分に近づいていなかったらループ }while( HitLength - NotHitLength > 0.1f ) ; // カメラの座標をセット cam.Eye = TestPosition ; } } }