|
質点とラインの設定が終わったら、いよいよ剛体を動かしてみます。
この剛体は自動車ですので、前進させるにはアクセルが必要です。
実際の自動車でもそうですが、アクセルを踏むとタイヤが回って前進します。
簡易剛体運動では、質点をタイヤに見立てて移動させます。
前進する時に動かす質点は、底面にあるP0~P3です。
四輪駆動車ならばP0~P3すべてを動かし、前輪駆動車ならばP0とP1、後輪駆動車ならばP2とP3を動かします。
前進させる時の質点の方向は、P0-P2ベクトルの方向です。
もちろん、このベクトルのマイナス方向へ移動させれば後退します。
同様にステアリングを切った時に動かす質点は、底面にあるP0とP1になります。
本来は自動車が停止中にステアリングを切っても動くことはないので、
停止しているかどうかを調べてから、ステアリング用の質点は動かさなければなりません。
何も考えずにこれらの質点を動かすと、停止しているのにその場でぐるぐる回る自動車になってしまいます
(ゲームの仕様でそのような動きをするマシンならそれでも構いません)。
ステアリングを切った時の質点の移動方向は、P0-P1ベクトルの方向です。
このベクトルのプラス方向に動かすかマイナス方向に動かすかは、
ステアリングを右に切ったか左に切ったかで変わります。
それではまず、アクセルを踏んだだけの状態を四輪駆動車で考えてみます。
アクセルを踏んで前進させた直後の剛体の状態は図3のようにP0~P3だけが移動した状態になっています。
P0'~P3'は、移動後のP0~P3です。
前進をさせた時点では、P4~P7はまだ移動させません。

(図3)
タイヤの移動が終わったら、各ラインに繋がれた質点の位置を調整します。
調整は、ラインに繋がれた2つの質点の位置を使って、数値微分と数値積分を行います。
微分の係数や積分の回数は、プログラマのさじ加減です。
微分係数と積分回数が小さすぎたりすると、剛体が安定しなかったりします。
面白いので、このあたりの数値はいろいろと調整してみると良いかもしれません。
まずはラインに繋がれた質点1と質点2の差分を計算(コード①)し、その距離(コード②)と質点1-質点2の単位ベクトル(コード③)を求めます。
次に、現在の質点1の位置から先ほどの単位ベクトルの方向へ、数値微分を行った値だけ移動します(コード④)。
同様に質点2も移動します(コード⑤)。質点2の場合、固定距離と差分距離の減算する方向に注意してください。
この計算は、質点同士を繋ぐ全てのラインに対して、たとえそのフレームに質点の移動が行われなかったとしても毎フレーム行います。
具体的には以下のコードのような計算を行って、徐々に本来の固定距離に質点同士を近づけて行きます。
(サンプルコードのalpLine、iIntegralはあらかじめ初期化されているものとします)
class CRigidBodyLine{
private:
D3DXVECTOR3 m_vec3Point1;
D3DXVECTOR3 m_vec3Point2;
float m_fFixedDistance;
float m_fDifferential;
public:
CRigidBodyLine(void) {}
~CRigidBodyLine(void) {}
void Initialize(D3DXVECTOR3* p_lpvec3Point1, D3DXVECTOR3* p_lpvec3Point2,
float p_fDifferential);
void AdjustDistance(void);
};
void CRigidBodyLine::Initialize(D3DXVECTOR3* p_lpvec3Point1, D3DXVECTOR3* p_lpvec3Point2,
float p_fDifferential)
{
m_vec3Point1 = *p_lpvec3Point1;
m_vec3Point2 = *p_lpvec3Point2;
m_fFixedDistance = D3DXVec3Length(&(m_vec3Point1 - m_vec3Point2));
m_fDifferential = p_fDifferential;
}
void CRigidBodyLine::AdjustDistance(void)
{
D3DXVECTOR3 vec3Diff;
float fDistance;
D3DXVECTOR3 vec3DiffNormal;
vec3Diff = m_vec3Point1 - m_vec3Point2; ……… ①
fDistance = D3DXVec3Length(&vec3Diff); ……… ②
D3DXVec3Normalize(&vec3DiffNormal, &vec3Diff); ……… ③
m_vec3Point1 = m_vec3Point1
+ (vec3DiffNormal * (m_fFixedDistance - fDistance) * m_fDifferential); ……… ④
m_vec3Point2 = m_vec3Point2
+ (vec3DiffNormal * (fDistance - m_fFixedDistance) * m_fDifferential); ………
}
CRigidBodyLine* alpLine[28];
int iIntegral;
for(int i = 0; i < iIntegral; ++i){
for(int j = 0; j <= 27; ++j){
alpLine[j]->AdjustDistance();
}
}
全ての質点の調整が終わると、P4~P7の質点も自動的にちょうど良い位置に移動しています。
一応、以上の処理だけでも剛体の移動は可能なのですが、もう少し動きにリアルさを出すために、
重力加速度やフロントとリアにかかるダウンフォース(ダウンフォースは速度によって変化させると効果的です)を、
質点の移動に加えたりしてみると良いと思います。
これらの力を加える場合は、P0~P3の質点に対してP0-P4ベクトル方向へ加えます。
ただし、これらの力を単純に加えているだけではどんどん剛体が下へ行くだけになってしまいますので、
ナビゲーションメッシュと組み合わせて、質点が乗っているセルよりは下に行かないように、
Y座標を調整しなければならないのは必須です。
また、アクセルを離したとしても、質点の移動が突然ピタリと止まるわけではなく、実際はある程度惰性で動き続けます。
質点の移動を行う際には、前回の質点の調整によって生まれた力を惰性として加えてみると良いと思います。
惰性は、質点の調整が終わった時点で各質点に対して、次のように計算しておきます。
(質点の調整後の位置 - 質点の調整前の位置) * 惰性係数
惰性係数は大きければ惰性が大きく、小さければあまり惰性がかからないようになります。
この計算で出てきたベクトルを、次の質点の移動の際に加えてやります。
簡易剛体運動を行う一連の流れを仮想言語で書くと次のようになります。
この流れには、ナビゲーションメッシュを利用した調整も含まれています。
Point[8]
PrevPoint[8]
Inertial[8]
Line[28]
loop{
ゲームのいろいろな処理
for(i = 0; i <= 7; ++i){
Point[i] += Inertial[i]
}
for(i = 0; i <= 3; ++i){
Point[i] += 1フレームにかかる重力加速度
}
for(i = 0; i <= 1; ++i){
Point[i] += 現在の速度に対するフロントのダウンフォース
}
for(i = 2; i <= 3; ++i){
Point[i] += 現在の速度に対するリアのダウンフォース
}
for(i = 0; i <= 1; ++i){
Point[i] += normal(PrevPoint[0] - PrevPoint[1]) * ステアリングの値
}
for(i = 0; i <= 3; ++i){
Point[i] += normal(PrevPoint[0] - PrevPoint[2]) * アクセルの値
}
for(i = 0; i < 数値積分回数; ++i){
for(j = 0; j <= 27; ++j){
Line[j]の数値微分を行う
}
}
for(i = 0; i <= 3; ++i){
Point[i] = PrevPoint[i]からPoint[i]の移動をナビゲーションメッシュで解決した位置
}
for(i = 0; i <= 3; ++i){
Inertial[i] = (Point[i] - PrevPoint[i]) * 惰性係数
PrevPoint[i] = Point[i]
}
レンダリングとかゲームのいろいろな処理
if(ゲーム終了?){
break
}
}
ゲーム終わり
|