んななのゲーム開発備忘録

ゲーム制作の進捗と開発メモなど掲載しています。

Unityで斜面に沿ったベクトルはVector3.ProjectOnPlaneで作ろう+その解説

f:id:nyanya-nnana:20171219223326p:plain:w500

キャラクターに力を加えたい時、斜面に沿って加えたいと思ったことはありませんか? 本記事ではUnityで斜面など平面に沿ったベクトルを簡単に作ることができるVector3.ProjectOnPlaneと、平面に沿ったベクトルの作り方について解説します。 手っ取り早く平面に沿ったベクトルを実装したい方はコチラの手順を見ればおっけーです。

  • 沿わせたい平面の法線ベクトル(normalVector)を用意(法線ベクトルは平面に対して垂直なベクトル)
  • 沿わせたいベクトル(inputVector)を用意
  • 目的のベクトル(onPlane)をVector3.ProjectOnPlaneを用いて次のように計算
onPlane = Vector3.ProjectOnPlane(inputVector, normalVector);

目次

平面に沿ったベクトルの実装

平面に沿ったベクトルの実装方法は色々あるかと思いますが、次の3つを上げておこうと思います。

どのようにして作られるか知りたい方はコチラを参照してください。

このベクトルを使用したデモはコチラをご覧ください。

Vector3.ProjectOnPlane(Vector3 vector, Vector3 planeNormal)を使う方法

実はしたいことそのもののメソッド(Vector3.ProjectOnPlane(Vector3 vector, Vector3 planeNormal))が用意されています。

1つ目の引数のvectorは平面に投影したいベクトルで、2つ目の引数のplaneNormalは平面の法線ベクトルです。メソッドを実行するとVector3型の平面に沿ったベクトルが返ってきます。

次のように使用します。

Vector3 normalVector = Vector3.zero;

private void OnCollisionStay(Collision collision)
{
    // 衝突した面の、接触した点における法線を取得
    normalVector = collision.contacts[0].normal;
}

private void FixedUpdate()
{
    // 平面に投影したいベクトルを作成
    Vector3 inputVector = Vector3.zero;
    inputVector.x = Input.GetAxis("Horizontal");
    inputVector.z = Input.GetAxis("Vertical");

    // 平面に沿ったベクトルを計算
    Vector3 onPlane = Vector3.ProjectOnPlane(inputVector, normalVector);
}

Vector3.Project(Vector3 vector, Vector3 onNormal)を使う方法

こちらはVector3.Project(Vector3 vector, Vector3 onNormal)というメソッドを用いる方法です。

平面に投影したいベクトルの法線方向の成分のベクトルを次のように計算して

Vector3 onNormal = Vector3.Project(inputVector, normalVector);

得られた法線方向成分のベクトルを元のベクトルから引くと所望のベクトルが得られます。 Vector3.ProjectOnPlaneと共通する部分は省略しています。

Vector3 onPlane = inputVector - onNormal;

Vector3.Dot(Vector3 lhs, Vector3 rhs)を使う方法

最後はベクトルの内積を求めるメソッドVector3.Dot(Vector3 lhs, Vector3 rhs)を用いる方法です。 最初に平面に沿わせたいベクトルの法線方向の成分の大きさを内積を使って計算します。法線方向のベクトルの大きさが1でない場合はnormalizedされたベクトルを使用してください。

// normalVectorの大きさが1でない場合はnormalVectorの代わりにnormalVector.normalizedを使用
float inputMagnitudeOnNormal = Vector3.Dot(inputVector, normalVector);

次に法線方向の成分のベクトルを計算します。

Vector3 onNormal = inputVector - inputManigtudeOnNormal*normalVector;

このベクトルを平面に沿わせたいベクトルから引けば所望のベクトルが得られます。

Vector3 onPlane = inputVector - onNormal;

デモ

斜面を考慮しないで一定方向に力を加えた場合、次の動画のようになります。

斜面でひっかかって登れません。


Unityで斜面に沿ったベクトルはVector3.ProjectOnPlaneで作る(その1)

こちらはVector3.ProjectOnPlaneを使って斜面に沿ったベクトルを計算したものです。 斜面でひっかからずにスムーズに登ることができています。


Unityで斜面に沿ったベクトルはVector3.ProjectOnPlaneで作る(その2)

平面に沿ったベクトルの作り方

平面に沿ったベクトルの作り方は次の通りです。

"ベクトルは、他のベクトルの足し算で表現できる"という性質を用いて平面に沿ったベクトルを作成します。 この性質を用いると平面に沿わせたいベクトルinputVectorは、平面に沿った方向のベクトルonPlaneと、その法線方向のベクトルonNormalという2つのベクトルの足し算として次のように表すことができます。

inputVector = onPlane + onNormal

図に表すと次の通りです。

f:id:nyanya-nnana:20171219124719p:plain:w400

この式を変形することで次のように平面に沿ったベクトルが計算できます。

onPlane = inputVector - onNormal

つまり平面の法線方向のベクトルさえ得られれば、これに沿うベクトルが得られるということです。

問題はどのようにしてそれを得るか?です。 これにはベクトルの内積を用います。

ベクトルの内積を使って求めたい方向の成分を計算

ベクトルの内積はベクトル\boldsymbol{r}\boldsymbol{x}と、これらのベクトルがなす角を\thetaとして、次のように表せます。

\boldsymbol{r}\cdot\boldsymbol{x} = |\boldsymbol{r}||\boldsymbol{x}|\cos{\theta}

内積の演算は\cdotで表され、|\boldsymbol{r}|はベクトル\boldsymbol{r}の大きさを表しています。 \cos{\theta}余弦関数です。 UnityのC#的に表すと次のようになります。

Vector3.Dot(vectorR, vectorX) = vectorR.magnitude * vectorX.magnitude * Mathf.Cos(angleTheta)

もう少し詳しく知りたい方はコチラの記事などを参考にしてみるとよいかもしれません。

f:id:nyanya-nnana:20171219131557p:plain:w300

余弦関数は上の図のように円上に点をとって図形を考えると、円の半径をrx軸方向の成分をxとすると次のように表せます。

\cos{\theta} = x/r

これを変形すると

x = r\cos{\theta}

つまり、xを求めたい方向の成分と考えると、元のベクトルの長さと余弦関数を用いると求めたい方向の成分が求められることがわかります。

これを内積と比較してみると似たような形をしていることがわかります。

ベクトル\boldsymbol{r}を元のベクトル、ベクトル\boldsymbol{x}を求めたい方向のベクトルと考えるとさきほどの式にベクトルxの大きさをかけたものがベクトルの内積になっています。

そこで、ベクトル\boldsymbol{x}の大きさを1としてみます。

そうすると、ベクトルの内積を用いて求めたい方向の成分inputMagnitudeOnNormalを計算できるようになります。

inputMagnitudeOnNormal=|\boldsymbol{r}|\cos{\theta}

UnityのC#的に表すと次のようになります。

magnitudeOnNormal = vectorR.magnitude * Mathf.Cos(angleTheta)

法線方向のベクトルを作成

先の計算で得られた法線方向成分の大きさを、大きさ1の法線ベクトルにかけると法線方向成分のベクトルonNormalが得られます。

onNormal = inputMagnitudeOnNormal * normalVector.normalized

元のベクトルから法線方向のベクトルを引いて平面に沿ったベクトルを計算

元のベクトルinputVectorは法線方向のベクトルonNormalと平面に沿ったベクトルonPlaneを足し算で表されていたことを思い出すと、求めるベクトルは次のように計算することができます。

onPlane = inputVector - onNormal

記事の内容は以上となります(。・x・)ゞ