スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

アクションゲームの物理演算の実装方法! -2014/07/18版-

物理演算の修正を行っていたのですが、
色々と不具合修正などで時間がかかってしまいましたorz

とりあえず、現時点での実装方法を記載します!

------------------------
【1】"力の方向"を設定
【2】衝突判定(Y軸方向)
【3】摩擦処理(X軸移動減衰)
【4】衝突判定(X軸方向)

------------------------
現状はこんな感じです。
とりあえず詳細に記載してみます。
※イメージを掴むための簡易的なソースコードを記載してます
 簡易的なので、実際のコードとは異なります


今まで、右キーを押す⇒右へ移動速度2で進むという感じだったのですが、
これを止めて、右へ2"進みたがっている"(力がかかっている)という風にしました。

■旧方式
if( GetInput( KEY_INPUT_RIGHT ) > 0 ){
t_SpeedX = 2;
}

■新方式
if( GetInput( KEY_INPUT_RIGHT ) > 0 ){
t_PowerX = 2;
}


そして、Y軸方向の衝突判定の際に、
状態に応じてPowerの値を減衰し、Speedに加算
します。

if( 普通の床と接触した ){
t_Power *= 1.0;
}else if( 氷の床と接触した ){
t_Power *= 0.2;
}else if( 何も接触しなかった(空中) ){
t_Power *= 0.4;
}
//加算する
t_Speed += t_Power;


例えば、右キーを押して一律で2ドット右へ進みたがるとします。
※数値は1000倍に細分化されているものとします。
 つまり「速度1000」で、1ドット進む。

この場合、下記の様な感じで、加速度合いが変わります。
■通常の床
2000⇒4000⇒6000⇒8000
■氷の床
400⇒800⇒1200⇒1600
■空中
800⇒1600⇒2400⇒3200

なので、「氷の床だと滑って、初速が出せない」といった動作にする事が出来ます。

で、上記のままだと、「ずっと右へ移動し続ける状態」になるので、
摩擦による減速処理を入れます。

■摩擦処理の例
Bの上をAが滑っている場合、下記の様な感じになります。
⇒[A]
[  B  ]
t_SpeedA += ( t_SpeedB - t_SpeedA )*0.01;
※0.01というのは摩擦の度合いです。
 オブジェクト毎に値を持たせておけば、オブジェクト毎に摩擦による減速を変えられます。
(Bの摩擦を0にした場合、t_SpeedA += 0;となり、減速せずに進み続けます)



あと新しく取り入れたのが、「影響度」という概念。

今まで、下記の様に列挙体の識別番号を作っておき、
その識別番号で、めり込んだ際の処理を変えていたのですが、
これを0~1000までの影響度の変数を持たせるように変更しました。

■今までの衝突判定
enum ObjectID {
OBJ_PLAYER,
OBJ_WALL,
OBJ_ENEMY
};


int Check( Object& t_ObjectA, Object& t_ObectB )
{
if( t_ObjectB->m_ID == OBJ_WALL ){
//壁は動かないので、オブジェクトAが壁の左側に来るように修正
t_ObjectA->m_PosX = t_ObjectB->m_PosX - t_ObjectA->Width;
return( -1 );
}else if( t_ObjectB->m_ID == OBJ_BOX ){
//箱(オブジェクトB)は動くので、めり込みが解消する様に箱の座標を修正
t_ObjectB->m_PosX = t_ObjectA->m_PosX + t_ObjectA->m_Width;

//オブジェクトAに押し出されたオブジェクトBが、
//他のオブジェクトと重ならないか確認

if( CheckAll( t_ObjectB ) == -1 ){
  //重なっていれば、オブジェクトBの位置が修正されているので、
     //その修正された位置を元に、めり込んだオブジェクトAの座標も修正

t_ObjectA->m_PosX = t_ObjectB->m_PosX - t_ObjectA->m_Width;
return( -1 );
}
return( 0 );
}
}



■現在の衝突判定
※下記の様にAが右方向へ進み、Bへぶつかる場合
  三[A][B]

//めり込んだ横幅を計算
int t_Dif = t_ObjectA_Right - t_ObjectB_Left + 1;
//影響度により、どのくらいめり込むかの修正
int t_ModDif = ( int )( t_Dif * ( ( double )t_ObjectB->m_Rate / 1000 ) );

//座標修正
SetObjectPosX( t_ObjectB, t_ObjectB_Left + t_ModDif );
SetObjectPosX( t_ObjectA, t_ObjectB_Left - t_ObjectA->m_Width );

//オブジェクトBが移動しなかった場合は、
//他オブジェクトとの衝突判定は不要

if( t_ModDif == 0 ){
return( 0 );
}

//移動したオブジェクトBが、他のオブジェクトと重なるか確認

if( CheckAll( t_ObjectB ) == -1 ){
//重なっていれば、関数内部でオブジェクトBの座標が修正されているので、
//それを元にオブジェクトAの座標も修正

SetObjectPosX( t_ObjectA, t_ObjectB->m_Left - t_ObjectA->m_Width );
}


こんな感じですね!
旧仕様では、オブジェクトの種類を増やす度に、
それ専用のコードを追記して行く必要がありましたが、

現在は、0~1000の影響度を設定すれば、
後は勝手にめり込み解消処理を行ってくれます。

もう少し物理演算の部分を修正したいのですが、どうしよう。
せめて"坂の転がり"あたりは実装したいと思っているのです。
(坂道にボールが落ちた場合、斜面に沿って転がっていく処理)

現在、斜面が45゜だろうが、めり込まない様に押し出して終わりですからね。
※こんな感じ。↓
    /
 ■/
  /

左斜め下に、加速して転がって行く事は無い。

時間かかる様であれば、現行の
衝突/めり込み解消/摩擦による速度減衰/重力落下のみでゲームを作って行こうかと思います。

また、よりよい実装方法を思いついたら、別途解説記事の様なものを作ってみます!
(物理演算の実装は難しいですね…)
スポンサーサイト
コメント
コメントの投稿
管理者にだけ表示を許可する



上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。