スポンサーサイト

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

アクションゲームの斜め坂の実装方法

現在、斜め坂の当たり判定周りを実装しています。
まず、根幹の処理をいくつか変えたので、それの説明。

-----------------------------
【1】めり込みの際の、押し込み廃止
【2】弾性力追加

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


【1】めり込みの際の、押し込み廃止
主人公が右へ移動して、移動可能オブジェクトAと衝突したとします。

この時、今までは
こんな面倒な手順を取っていたのですが、
===================================
手順1:めり込んでいた分だけAを右に動かす
手順2:更に右に物体があった場合はそれも動かす
手順3:更に更に右側に移動不可の壁などがあった場合、
     手順1~手順2の移動をキャンセル

===================================
【参考画像】
Sample_20140905_1.jpg


上記手順を止めて、
めり込む場合は必ず、
めり込まない位置に戻す
という風にしました。

【参考画像】
Sample_20140905_2.jpg

これだと、押せるオブジェクトがあっても押せないじゃないか、という事になってしまいますが、
次の弾性力と合わせて説明します。


【2】弾性力追加
全ての物体に"跳ね返る度合い"の概念を追加しました。
オブジェクトが衝突した場合、この概念を用いて移動方向を決めます。

※下記ページを参考にしました。
【参考URL】EMANの物理学・力学・2物体の衝突

オブジェクトA、Bが衝突した場合、下記の様に計算します。
int t_SpeedA = t_ObjectA->m_DetailSpeedX;
int t_SpeedB = t_ObjectB->m_DetailSpeedX;
double t_ElastA = ( double )t_ObjectA->m_ElasticForce / 1000;
double t_ElastB = ( double )t_ObjectB->m_ElasticForce / 1000;
double t_RateA = t_ObjectA->m_Mass / t_ObjectB->m_Mass;
double t_RateB = t_ObjectB->m_Mass / t_ObjectA->m_Mass;

t_ObjectA->m_DetailSpeedX = t_SpeedA + ( int )( ( double )( -t_SpeedA + t_SpeedB )*( 1 + t_ElastA )/( t_RateA + 1 ) );
t_ObjectB->m_DetailSpeedX = t_SpeedB + ( int )( ( double )( -t_SpeedB + t_SpeedA )*( 1 + t_ElastB )/( t_RateB + 1 ) );



分かりにくいので、具体例。

■例1
主人公Aが移動速度2で、壁Bに衝突。
主人公Aの弾性係数は0とする。(跳ね返る弾力性が無い)


※壁Bは不動なので、質量を9999999などとして考えます。
(主人公の質量を10等にした場合、壁は質量が大きすぎて微動だにしない、という動作になる)

t_ObjectA->m_DetailSpeedX = 2 + ( -2 + 0 )*( 1 + 0 ) / ( 0 + 1 );
t_ObjectA->m_DetailSpeedX = 2 - 2;
t_ObjectA->m_DetailSpeedX = 0;
⇒主人公の移動速度は0となる

t_ObjectB->m_DetailSpeedX = 0 + ( -0 + 2 )*( 1 + 0 ) / ( 9999999 + 1 );
t_ObjectB->m_DetailSpeedX = 0 + 0
t_ObjectB->m_DetailSpeedX = 0
⇒壁の移動速度は0のまま変わらない


■例2
主人公Aが移動速度2で、壁Bに衝突。
主人公Aの弾性係数は1とする。(最大限跳ね返る)


t_ObjectA->m_DetailSpeedX = 2 + ( -2 + 0 )*( 1 + 1 ) / ( 0 + 1 );
t_ObjectA->m_DetailSpeedX = 2 - 4;
t_ObjectA->m_DetailSpeedX = -2;
⇒主人公の移動速度は-2となる
(壁にぶつかった速度と同じ速さで、反対方向に跳ね返る)

t_ObjectB->m_DetailSpeedX = 0 + ( -0 + 2 )*( 1 + 0 ) / ( 9999999 + 1 );
t_ObjectB->m_DetailSpeedX = 0 + 0
t_ObjectB->m_DetailSpeedX = 0
⇒壁の移動速度は0のまま変わらない


こんな感じですね!
で、めり込み解消の方にしか座標を修正しないのなら、オブジェクトを押せないのでは?
という点についてですが、


■1フレーム目
手順1:主人公が移動速度1で、箱Aに衝突する
手順2:弾性力の計算が行われ、箱Aの移動速度が1になる

■2フレーム目
手順3:箱Aが移動速度1で、箱Bに衝突する
手順4:弾性力の計算が行われ、箱Bの移動速度が1になる

■3フレーム目
手順5:箱Bが移動速度1で、壁に衝突する
手順6:弾性力の計算が行われ、箱Bの移動速度が0に

⇒主人公が箱を押している様な動作になる!



こんな感じで、「複数のオブジェクトを押している」動作になります。
1フレームで全部計算するのではなく、複数のフレームに分けて処理が行われます。

つまり、最終的な結果が出るまでに表示遅延が発生するのですが、
1秒間に64回画面を更新する場合に、4回で結果が出た場合、
1秒の16分の1、つまり0.0625秒程度の誤差になります。

肉眼で「あっ衝突後の位置が1回で更新されずに、1、2フレーム遅延して更新が完了している!」とか
分かる訳ないので、これで問題ない、という寸法です。



■斜め坂の実装方法
では、次に斜め坂の実装について。

斜め坂は、一次関数の直線と考えて、
オブジェクトの底との交点を求めて算出します。

※下記の2つルールを守って直線情報を定義します
ルール1:必ず右方向の直線になるように座標を指定
(始点のX座標を、終点のX座標より小さくする。
例えば、右上がりの直線情報は、
始点(200, 0 ) - 終点(0, 200)ではなく、始点(0,200 ) - 終点(200, 0)とする。
※左向きと右向きの直線が混在すると面倒だから、というだけの理由。)


ルール2:垂直な直線は存在しないものとする
(直線の傾きを出す所で、0除算が発生する為。
 垂直な直線を作りたい場合は、直線ではなく横幅1ドットの矩形として対応する)



例えば、オブジェクトAが右上がりの斜め坂に衝突する場合は、
⇒A ◢
----------------------------------------------
【1】まず、斜め坂を矩形(四角形)として衝突判定を行う
【2】衝突している場合、下記を求める
  ①斜め坂の始点(X座標)
  ②斜め坂の始点(Y座標)
  ③斜め坂の終点(X座標)
  ④斜め坂の終点(Y座標)
  ⑤斜め坂の傾き
  ⑥オブジェクトAの底辺
  ⑦オブジェクトAの右辺
  ⑧オブジェクトAの高さ(縦幅)

【3】上記から、オブジェクトAの右辺があるX座標での、斜め坂のY座標を求める
   傾き: (double)(④-②) / (③-①)
   斜め坂のY座標:②+(⑤×(⑦-①))

【4】「手順3で出したY座標 < ⑥オブジェクトAの底辺」であれば、
   "斜め坂にめり込んでいる"と判定
【5】めり込んでいる場合、「手順3のY座標 - ⑧オブジェクトAの高さ」を、
   オブジェクトAの新しいY座標とする

----------------------------------------------
こんな感じになります。

具体的に図を用いて計算してみます。


【2】各必要な値を求める
右方向へ進む緑色の長方形と、青色の坂道が衝突するとします。
この場合、必要な値は下記の様になります。

b_1010_1.png


【3】オブジェクトAの右辺がある位置での、
坂道のY座標を求める


右辺のある位置での坂道のY座標は、
始点のY座標 + 傾き×始点から右辺までの距離で出せます。
今回は下記の様になるので、
-------------------------------------
始点のY座標 … 2
傾き … -1
始点からオブジェクトAの右辺までの距離 … 3-2=1
-------------------------------------
2 + (-1×1) = 1となります。
※上記の図を見ても、オブジェクトAの右辺がある位置では、(X座標が3の所では)
 坂道のY座標は1である事が分かると思います。


【4】めり込んでいるかを判定
オブジェクトAの右辺の位置では、坂道のY座標は1でした。
つまり、オブジェクトAの底辺が1より大きければめり込んでいると判定します。

※図を見ても分かる様に、今回の例では2>1 (底辺>坂道のY座標)なので、
 めり込んでいると判定。


<追記予定!>
スポンサーサイト


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