3Dシューティングゲームでの自機のピッチアップ/ダウンとローリングの回転がうまく行かない点(片方のみの回転ならうまく行く)について悩んでいます。(具体的な内容は下で記述します)
開発言語はC++でDirectX11を使用しています。
理想としてはエースコンバットのような自機を自由にローリング、ピッチアップ/ダウン、ヨーイングさせて戦闘機を自由に飛ばしたいです。
現時点では自機は移動せず、原点でローリング、ピッチアップ/ダウンするのみでヨーイングは実装していません。
自機とカメラの回転にはクオータニオンを使用しており、
ローリングに使うクオータニオンの軸とする前方向ベクトル、
ピッチアップ/ダウンに使うクオータニオンの軸とする横方向ベクトル、
ヨーイングに使うクオータニオンの軸とする上方向ベクトル
を使用しています。
機体の回転について
機体の回転は翼の回転している角度を機体の回転角に足して、機体の回転角に応じて機体を回転させるようにしています。
機体のそれぞれの回転角が360度・-360度を越えた場合は0度に戻しています。
ピッチアップ/ダウンのみ、もしくはローリングのみならばちゃんとそれぞれのベクトルを軸として回転してくれますが、
両方の回転を行うと機体がおかしな回転をずっとするようになり、特にピッチアップ/ダウンかローリングの角度が約90度以上約270度以下・約-90度以下約-270度以上の状態で回転を行っていないほうの回転を少しでも行うとめちゃくちゃな回転をするようになり、ずっと回り続けてしまいます(キーの入力によって落ち着かせることは可能です)。
カメラはエースコンバットの三人称視点のように機体の後方から機体を映す様にしています。
機体の回転がうまく行かないのに対してカメラの座標回転、上方向ベクトルの回転はなぜかうまく行き、機体がどう回転しようが機体の後ろにいて機体を映してます。
以下はソースです
ローカル変数 機体の回転を行う場所
//横方向ベクトルを軸に回転するクオータニオンを作る
S_SceneData->S_PitchUpDown_Q = Quaternion::CreateFromAxisAngle(S_SceneData->S_PlaneSideVec, XMConvertToRadians(-S_SceneData->S_PitchUpDownNum));
//前方向ベクトルを軸に回転するクオータニオンを作る
S_SceneData->S_Roll_Q = Quaternion::CreateFromAxisAngle(S_SceneData->S_PlaneFrontVec, XMConvertToRadians(S_SceneData->S_RollNum));
//上方向ベクトルを軸に回転するクオータニオンを作る
S_SceneData->S_Yaw_Q = Quaternion::CreateFromAxisAngle(S_SceneData->S_PlaneUPVec, XMConvertToRadians(S_SceneData->S_YawNum));
//前方向ベクトルにピッチアップ/ダウンとヨーイングの回転を反映させる
S_SceneData->S_PlaneFrontVec = Vector3::Transform(Vector3(0.0f, 0.0f, -1.0f), Matrix::CreateFromQuaternion(S_SceneData->S_PitchUpDown_Q) * Matrix::CreateFromQuaternion(S_SceneData->S_Yaw_Q));
//上方向ベクトルにピッチアップ/ダウンとローリングの回転を反映させる
S_SceneData->S_PlaneUPVec = Vector3::Transform(Vector3(0.0f, 1.0f, 0.0f), Matrix::CreateFromQuaternion(S_SceneData->S_PitchUpDown_Q) * Matrix::CreateFromQuaternion(S_SceneData->S_Roll_Q));
//外積によりピッチアップ/ダウンとロール後の横方向ベクトルを求める
S_SceneData->S_PlaneSideVec = -S_SceneData->S_PlaneUPVec.Cross(S_SceneData->S_PlaneFrontVec);
//機体の行列に回転データを反映させる
plane_roll = Matrix::CreateFromQuaternion(S_SceneData->S_PitchUpDown_Q) * Matrix::CreateFromQuaternion(S_SceneData->S_Roll_Q);
Matrix PlaneMat = plane_roll * PlaneTrans;
//機体のカメラ座標(機体の移動と回転有り)に ( 機体のカメラ座標(機体の移動と回転なし)*機体の移動と回転 ) を代入する
S_SceneData->S_PlaneChmera = Vector3::Transform(S_SceneData->S_PlaneCameraTmp, (Matrix::CreateFromQuaternion(S_SceneData->S_PitchUpDown_Q) * Matrix::CreateFromQuaternion(S_SceneData->S_Roll_Q)));
//機体のカメラ座標(機体の移動と回転有り)に ( 機体のカメラ座標(機体の移動と回転なし)*機体の移動と回転 ) を代入する
S_SceneData->S_PlaneChmera = Vector3::Transform(S_SceneData->S_PlaneChmera, PlaneTrans);
//機体の回転状況に応じてカメラの上方向ベクトルを回転させる
S_SceneData->S_CameraUpVec = Vector3::Transform(Vector3(0.0f, 1.0f, 0.0f), (Matrix::CreateFromQuaternion(S_SceneData->S_PitchUpDown_Q) * Matrix::CreateFromQuaternion(S_SceneData->S_Roll_Q)));
//ビュー行列の生成 //カメラの座標 //機体の座標 //カメラの上方向ベクトル
view = Matrix::CreateLookAt(S_SceneData->S_PlaneChmera, S_SceneData->S_PlanePos, S_SceneData->S_CameraUpVec);
//下キーを押し続けている場合の処理
if (S_SceneData->S_Key->Press(VK_DOWN))
{
//エレベーターの回転制限角まで回転するようにする
if (S_SceneData->S_RightTaleWingRot_X > -20.0f)
{
S_SceneData->S_RightTaleWingRot_X -= 0.25f;
}
if (S_SceneData->S_LeftTaleWingRot_X > -20.0f)
{
S_SceneData->S_LeftTaleWingRot_X -= 0.25f;
}
}
//上キーを押し続けている場合の処理
else if (S_SceneData->S_Key->Press(VK_UP))
{
//エレベーターの回転制限角まで回転するようにする
if (S_SceneData->S_RightTaleWingRot_X < 20.0f)
{
S_SceneData->S_RightTaleWingRot_X += 0.25f;
}
if (S_SceneData->S_LeftTaleWingRot_X < 20.0f)
{
S_SceneData->S_LeftTaleWingRot_X += 0.25f;
}
}
//何も押していない場合の処理
else
{
//エレベーターの回転角が0になるまでエレベーターの回転角を増やす/減らす
if (S_SceneData->S_RightTaleWingRot_X > 0.0f)
{
S_SceneData->S_RightTaleWingRot_X -= 0.25f;
}
if (S_SceneData->S_RightTaleWingRot_X < 0.0f)
{
S_SceneData->S_RightTaleWingRot_X += 0.25f;
}
if (S_SceneData->S_LeftTaleWingRot_X > 0.0f)
{
S_SceneData->S_LeftTaleWingRot_X -= 0.25f;
}
if (S_SceneData->S_LeftTaleWingRot_X < 0.0f)
{
S_SceneData->S_LeftTaleWingRot_X += 0.25f;
}
}
//右ぎーを押し続けている場合の処理
if (S_SceneData->S_Key->Press(VK_RIGHT))
{
//エルロンの回転角の制限値までエルロンが回転できるようにする
if (S_SceneData->S_RightAileronRot_X < 20.0f)
{
S_SceneData->S_RightAileronRot_X += 0.25f;
}
}
//左ぎーを押し続けている場合の処理
else if (S_SceneData->S_Key->Press(VK_LEFT))
{
//エルロンの回転角の制限値までエルロンが回転できるようにする
if (S_SceneData->S_RightAileronRot_X > -20.0f)
{
S_SceneData->S_RightAileronRot_X -= 0.25f;
}
}
//何も押していない場合の処理
else
{
//エルロンの回転角が0になるまでエルロンの回転角を増やす/減らす
if (S_SceneData->S_RightAileronRot_X < 0.0f)
{
S_SceneData->S_RightAileronRot_X += 0.25f;
}
if (S_SceneData->S_RightAileronRot_X > 0.0f)
{
S_SceneData->S_RightAileronRot_X -= 0.25f;
}
}
//エレベーターの回転角をピッチアップ/ダウンする数値に反映させる
S_SceneData->S_PitchUpDownNum += S_SceneData->S_RightTaleWingRot_X;
//エルロンの回転角をロールする数値に反映させる
S_SceneData->S_RollNum += S_SceneData->S_RightAileronRot_X;
//360度以上回転したら0度に戻す
if (XMConvertToRadians(S_SceneData->S_PitchUpDownNum) > 360.0f || XMConvertToRadians(S_SceneData->S_PitchUpDownNum) < -360.0f)
{
S_SceneData->S_PitchUpDownNum = 0.0f;
}
//360度以上回転したら0度に戻す
if (XMConvertToRadians(S_SceneData->S_RollNum) > 360.0f || XMConvertToRadians(S_SceneData->S_RollNum) < -360.0f)
{
S_SceneData->S_RollNum = 0.0f;
}
//機体の胴体の描画
Plane_Draw(S_ChoosingNum, 0, PlaneMat, view);
//右水平尾翼の描画
//パーツの回転
Matrix RotX = Matrix::CreateRotationX(XMConvertToRadians(S_SceneData->S_RightTaleWingRot_X));
Matrix RotY = Matrix::CreateRotationY(XMConvertToRadians(S_SceneData->S_RightTaleWingRot_Y));
Matrix RotZ = Matrix::CreateRotationZ(S_SceneData->S_RightTaleWingRot_Z);
//パーツの機体に合わせた回転
Matrix RotShapeX = Matrix::CreateRotationX(S_SceneData->S_RightTaleWingRotShape_X);
Matrix RotShapeY = Matrix::CreateRotationY(S_SceneData->S_RightTaleWingRotShape_Y);
Matrix RotShapeZ = Matrix::CreateRotationZ(DirectX::XMConvertToRadians(180.0f));
//パーツの機体の合わせた移動
Matrix Trans = Matrix::CreateTranslation(266.72f, 24.70f, 616.97f);
world = RotX * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 1, world, view);
//左水平尾翼の描画
RotX = Matrix::CreateRotationX(XMConvertToRadians(S_SceneData->S_LeftTaleWingRot_X));
Trans = Matrix::CreateTranslation(-266.72f, 24.70f, 616.97f);
world = RotX * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 2, world, view);
//右フラップの表示
RotShapeX = Matrix::CreateRotationX(XMConvertToRadians(-0.84f));
RotShapeY = Matrix::CreateRotationY(XMConvertToRadians(7.31f));
RotShapeZ = Matrix::CreateRotationZ(XMConvertToRadians(0.11f));
Trans = Matrix::CreateTranslation(370.8f, 29.75f, 344.6f);
world = RotShapeX * RotShapeY * RotShapeZ * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 7, world, view);
//左フラップの表示
RotShapeX = Matrix::CreateRotationX(XMConvertToRadians(0.11f));
RotShapeY = Matrix::CreateRotationY(XMConvertToRadians(-7.31f));
RotShapeZ = Matrix::CreateRotationZ(XMConvertToRadians(-0.84f));
Trans = Matrix::CreateTranslation(-370.8f, 29.75f, 344.6f);
world = RotShapeX * RotShapeY * RotShapeZ * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 8, world, view);
//右エルロンの表示
RotShapeX = Matrix::CreateRotationX(XMConvertToRadians(-0.13f));
RotShapeY = Matrix::CreateRotationY(XMConvertToRadians(7.31f));
RotShapeZ = Matrix::CreateRotationZ(XMConvertToRadians(0.99f));
Trans = Matrix::CreateTranslation(577.8f, 25.87f, 307.5f);
world = RotShapeX * RotShapeY * RotShapeZ * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 9, world, view);
//左エルロンの表示
RotShapeX = Matrix::CreateRotationX(XMConvertToRadians(0.13f));
RotShapeY = Matrix::CreateRotationY(XMConvertToRadians(-7.31f));
RotShapeZ = Matrix::CreateRotationZ(XMConvertToRadians(-0.99f));
Trans = Matrix::CreateTranslation(-577.8f, 25.87f, 307.5f);
world = RotShapeX * RotShapeY * RotShapeZ * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 10, world, view);
//右前フラップの表示
RotShapeX = Matrix::CreateRotationX(XMConvertToRadians(0.84f));
RotShapeY = Matrix::CreateRotationY(XMConvertToRadians(45.22f));
RotShapeZ = Matrix::CreateRotationZ(XMConvertToRadians(-0.11f));
Trans = Matrix::CreateTranslation(-493.2f, 17.24f, 70.34f);
world = RotShapeX * RotShapeY * RotShapeZ * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 19, world, view);
//左前フラップの表示
RotShapeX = Matrix::CreateRotationX(XMConvertToRadians(0.11f));
RotShapeY = Matrix::CreateRotationY(XMConvertToRadians(-45.22f));
RotShapeZ = Matrix::CreateRotationZ(XMConvertToRadians(-0.84f));
Trans = Matrix::CreateTranslation(493.2f, 17.24f, 70.34f);
world = RotShapeY * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 20, world, view);
//右カナードの表示
RotShapeX = Matrix::CreateRotationX(XMConvertToRadians(0.84f));
RotShapeY = Matrix::CreateRotationY(XMConvertToRadians(-45.22f));
RotShapeZ = Matrix::CreateRotationZ(XMConvertToRadians(-0.11f));
Trans = Matrix::CreateTranslation(180.8f, 20.35f, -405.9f);
world = RotShapeY * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 5, world, view);
//左カナードの表示
RotShapeX = Matrix::CreateRotationX(XMConvertToRadians(0.11f));
RotShapeY = Matrix::CreateRotationY(XMConvertToRadians(45.22f));
RotShapeZ = Matrix::CreateRotationZ(XMConvertToRadians(-0.84f));
Trans = Matrix::CreateTranslation(-180.8f, 20.35f, -405.9f);
world = RotShapeY * Trans * PlaneMat;
Plane_Draw(S_ChoosingNum, 6, world, view);
よろしくお願いします。