3DのFSPの当たり判定が怪しい
Posted: 2011年9月05日(月) 11:31
3DのFPSを作っているのですが
「 ←こんな感じに壁があって、この角の部分に直進し続けると
何秒か抵抗するもののすり抜けてしまいます。
抵抗するということは、当たり判定自体はちゃんと行えているということだと思うんですが。。
これをすり抜けないようにしたいです。
コードは、こちらのDxライブラリ置き場 3Dアクション基本 というページからほとんど流用しています。
アニメの設定とか、ジャンプの細かい設定が無いだけなので
あまり変わらないはずです。
大きな変化点は、移動ベクトルの算出方法ぐらいです。
宜しくお願いします。
「 ←こんな感じに壁があって、この角の部分に直進し続けると
何秒か抵抗するもののすり抜けてしまいます。
抵抗するということは、当たり判定自体はちゃんと行えているということだと思うんですが。。
これをすり抜けないようにしたいです。
コードは、こちらのDxライブラリ置き場 3Dアクション基本 というページからほとんど流用しています。
アニメの設定とか、ジャンプの細かい設定が無いだけなので
あまり変わらないはずです。
大きな変化点は、移動ベクトルの算出方法ぐらいです。
宜しくお願いします。
void Player_Move( void )
{
VECTOR MoveVector = VGet( 0.0f, 0.0f, 0.0f ); //移動予定量ベクトル
if( CheckHitKey( KEY_INPUT_W ) == 1 )
{
MoveVector.z += cos( Camera.HR ) * PLAYER_MOVE_SPEED;
MoveVector.x += sin( Camera.HR ) * PLAYER_MOVE_SPEED;
}
if( CheckHitKey( KEY_INPUT_S ) == 1 )
{
MoveVector.z -= cos( Camera.HR ) * PLAYER_MOVE_SPEED;
MoveVector.x -= sin( Camera.HR ) * PLAYER_MOVE_SPEED;
}
if( CheckHitKey( KEY_INPUT_D ) == 1 )
{
MoveVector.z += cos( Camera.HR + PHI_F / 2 ) * PLAYER_MOVE_SPEED;
MoveVector.x += sin( Camera.HR + PHI_F / 2 ) * PLAYER_MOVE_SPEED;
}
if( CheckHitKey( KEY_INPUT_A ) == 1 )
{
MoveVector.z -= cos( Camera.HR + PHI_F / 2 ) * PLAYER_MOVE_SPEED;
MoveVector.x -= sin( Camera.HR + PHI_F / 2 ) * PLAYER_MOVE_SPEED;
}
MoveVector.y -= 40.0f; //重力
Player_Process( MoveVector );
}
void Player_Process( VECTOR MoveVector )
{
int i, j, k; //カウンタ変数
int MoveFlag; //移動フラグ
int HitFlag; //当たり判定フラグ
MV1_COLL_RESULT_POLY_DIM HitDim; //当たり判定関数結果構造体
int WallNum; //壁カウント
int FloorNum; //床カウント
MV1_COLL_RESULT_POLY* Wall[ PLAYER_MAX_HITCOLL ]; //HitDimを貯めておくためのポインタ配列
MV1_COLL_RESULT_POLY* Floor[ PLAYER_MAX_HITCOLL ]; //上と同じ
MV1_COLL_RESULT_POLY* Poly; //作業用ポインタ
HITRESULT_LINE LineRes; //当たり判定関数結果構造体
VECTOR OldPos; //更新前の位置
VECTOR NowPos; //更新後の位置
OldPos = Player.Position;
NowPos = VAdd( Player.Position, MoveVector );
HitDim = MV1CollCheck_Sphere( MapHandle, -1, Player.Position, PLAYER_ENUM_DEFAULT_SIZE + VSize( MoveVector ) );
if( fabs( MoveVector.x ) > 0.01f || fabs( MoveVector.z ) > 0.01f )
{
MoveFlag = 1;
}
else
{
MoveFlag = 0;
}
//==当たり判定本体部分==//
WallNum = 0;
FloorNum = 0;
for( i = 0; i < HitDim.HitNum ; i ++ )
{
if( HitDim.Dim[ i ].Normal.y < 0.000001f && HitDim.Dim[ i ].Normal.y > -0.000001f )
{
if( HitDim.Dim[ i ].Position[ 0 ].y > Player.Position.y + 1.0f ||
HitDim.Dim[ i ].Position[ 1 ].y > Player.Position.y + 1.0f ||
HitDim.Dim[ i ].Position[ 2 ].y > Player.Position.y + 1.0f )
{
if( WallNum < PLAYER_MAX_HITCOLL )
{
Wall[ WallNum ] = &HitDim.Dim[ i ];
WallNum ++;
}
}
}
else
{
if( FloorNum < PLAYER_MAX_HITCOLL )
{
Floor[ FloorNum ] = &HitDim.Dim[ i ];
FloorNum ++;
}
}
}
if( WallNum != 0 )
{
HitFlag = 0;
if( MoveFlag == 1 )
{
for( i = 0 ; i < WallNum ; i ++ )
{
Poly = Wall[ i ];
if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, PLAYER_HIT_HEIGHT, 0.0f ) ), PLAYER_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == FALSE ) continue;
HitFlag = 1;
//壁に当たったとき
VECTOR SlideVector;
SlideVector = VCross( MoveVector, Poly->Normal );
SlideVector = VCross( Poly->Normal, SlideVector );
for( j = 0 ; j < WallNum ; j ++ )
{
Poly = Wall[ j ];
if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, PLAYER_HIT_HEIGHT, 0.0f ) ), PLAYER_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == TRUE ) break ;
}
if( j == WallNum )
{
HitFlag = 0;
break;
}
}
}
else
{
for( i = 0 ; i < WallNum ; i ++ )
{
Poly = Wall[ i ];
if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, PLAYER_HIT_HEIGHT, 0.0f ) ), PLAYER_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == TRUE )
{
HitFlag = 1;
break;
}
}
}
if( HitFlag == 1 )
{
for( k = 0 ; k < PLAYER_HIT_TRYNUM ; k ++ )
{
for( i = 0 ; i < WallNum ; i ++ )
{
Poly = Wall[ i ];
if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, PLAYER_HIT_HEIGHT, 0.0f ) ), PLAYER_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == FALSE ) continue ;
NowPos = VAdd( NowPos, VScale( Poly->Normal, PLAYER_HIT_SLIDE_LENGTH ) );
for( j = 0 ; j < WallNum ; j ++ )
{
if( HitCheck_Capsule_Triangle( NowPos, VAdd( NowPos, VGet( 0.0f, PLAYER_HIT_HEIGHT, 0.0f ) ), PLAYER_HIT_WIDTH, Poly->Position[ 0 ], Poly->Position[ 1 ], Poly->Position[ 2 ] ) == TRUE ) break ;
}
if( j == WallNum ) break;
}
if( i != WallNum ) break;
}
}
}
if( FloorNum != 0 )
{
float MaxY;
HitFlag = 0;
MaxY = 0.0f;
for( i = 0 ; i < FloorNum ; i ++ )
{
// i番目の床ポリゴンのアドレスを床ポリゴンポインタ配列から取得
Poly = Floor[ i ] ;
// ジャンプ中かどうかで処理を分岐
if( Player.State == 2 )
{
// ジャンプ中の場合は頭の先から足先より少し低い位置の間で当たっているかを判定
LineRes = HitCheck_Line_Triangle( VAdd( NowPos, VGet( 0.0f, PLAYER_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, PLAYER_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;
// もしジャンプ中だった場合は着地状態にする
if( Player.State == 2 )
{
// 移動していたかどうかで着地後の状態と再生するアニメーションを分岐する
if( MoveFlag )
{
// 移動している場合は走り状態に
Player.State = 1 ;
}
else
{
// 移動していない場合は立ち止り状態に
Player.State = 0 ;
}
}
}
else
{
// 床コリジョンに当たっていなくて且つジャンプ状態ではなかった場合は
if( Player.State != 2 )
{
// ジャンプ中にする
Player.State = 2;
}
}
}
// 新しい座標を保存する
Player.Position = NowPos ;
// プレイヤーのモデルの座標を更新する
//MV1SetPosition( Player.ModelHandle, pl.Position ) ;
// 検出したプレイヤーの周囲のポリゴン情報を開放する
MV1CollResultPolyDimTerminate( HitDim ) ;
}