スポンサーサイト

上記の広告は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
if( //キーが押されていれば){
m_Speed += 4;
}


以前は上記の様にしていました。
摩擦の概念が無いので、例えば、
右キーを押せば通常の地面であろうと、氷の床であろうと即座に移動開始します。
(氷の床上だと、(滑るので)しばらくは移動開始しないのが適切。)


■具体例 その2
if( //通常の地面であれば ){
m_Speed *= 0.8;
}


自分のプログラムでは、右へ4進むとした場合、
何もしなければそのまま右へ4進み続けます。

なので、地面とのあたり判定があれば、
上記の様に4⇒3.2⇒2.56…という風に摩擦らしき力が働き、止まる様な
実装にしていたのですが、これだと
空中で左右キーを押した際に、物凄い勢いで移動開始します。

※地面に当たっている場合は、移動速度が0.8倍になるのですが、
 空中では0.8倍されないので、4⇒8⇒12と、移動速度がどんどん上がる状態になる。


空中の場合(衝突判定をして何も接触していない場合)は、
移動速度を0.4倍にするとかいう実装で多少見た目はマシになりましたが、
汚い実装方法だなと思い、作り直しています。


あと、オブジェクト作成を隠ぺいする様に変更中です。
今までは、プログラム側で下記を全部行っていました。
-----------------------------
・画像情報作成
・矩形情報作成
・移動クラス情報作成

-----------------------------

いろいろとかなり省略してますが大体こんな感じ。

t_DrawerInfo.m_PosX = 100;
t_DrawerInfo.m_PosY = 50;
t_DrawerInfo.m_Rota = 0;
t_DrawerInfo.m_Trans = 255;
t_DrawerInfo.m_Extend = 1000;
t_DrawerInfo.m_DrawFlag = DRAW_NOW;

int t_DrawerHandle = CreateDrawer( t_DrawerInfo );
if( t_DrawerHandle == -1 ){
return( -1 );
}

t_CollisionInfo.m_PosX = 0;
t_CollisionInfo.m_PosY = 0;
t_CollisionInfo.m_Width = 64;
t_CollisionInfo.m_Height = 64;

int t_CollisionHandle = CreateCollision( t_CollisionInfo );
if( t_CollisionHandle == -1 ){
return( -1 );
}

switch( t_MoverType ){
case MOVER_PLAYER:
t_pMover.reset( new CMoverPlayer() );
break;
default:
break;
}

if( t_pMover == NULL ){
return( -1 );
}
std::vector<int> t_Vec;
t_Vec.push_back( t_DrawerHandle );
t_Vec.push_back( t_CollisionHandle );

if( t_pMover.Init( t_Vec ) == -1 ){
return( -1 );
}

int t_MoverHandle = AddMover( t_pMover );
return( t_MoverHandle );


上記の様なコードを記載して、やっと主人公が誕生します。
長過ぎ。

敵とかいろんなキャラが登場するのに、
上記の様なものをいちいち書いてられるか!という訳で、
データ作成クラスを作り、そこに内部処理は全部任せます。

こんな感じで作成出来る様にする予定です!

//x = 100, y = 50に主人公1を作成
t_ObjectManager.Create( OBJECT_PLAYER1, 100, 50 );


このオブジェクト作成と、物理演算周りの修正が完了すれば、
後はステージ量産とか出来る
と思うので、頑張ります…!
7月中旬 完成目標…!

【追記】
そういえば、アクションゲームのカメラワークの実装方法の解説がまだだったような気がする。
これも後日記事として纏めようと思います。


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