ぼっちプログラマのメモ

UE4とかUE5とかについて書いたり書かなかったり。

【UE4】Calculate Directionノードを使って移動方向にキャラクタの体を傾ける方法について

はじめに

「お!見たことないノード!ググっても情報少ないし新しいノードか!?」と意気揚々とツイートしたら…

フルボッコにされました(´;ω;`)ブワッ

f:id:pafuhana1213:20210126185845p:plain

ということで、泣きながら試しに使ってみた所いい感じだったのでサクッと記事にしてみました

公式ドキュメント:
Calculate Direction | Unreal Engine Documentation



Calculate Directionノードの使い方

とっても簡単です!これで「移動方向のベクトルとキャラクタ前方のベクトルの角度」を取得できます。

f:id:pafuhana1213:20210126190531p:plain

なお、AnimationBPのEventGraphでしか使うことができません。
AnimationBPのEventGraph、またはSkeletalMeshComponent -> Get Anim Instance経由で使えます。

f:id:pafuhana1213:20210126232013p:plain

これだけだと少しイメージしづらいので…2つのベクトルをArrowComponentで可視化してみました。赤が移動方向(=Velocity)、青がキャラクタ(=Actor)の向きです。

f:id:pafuhana1213:20210126190315p:plain

キャラクタが左を向いている状態で右方向に移動し始めると…以下のようになります。

f:id:pafuhana1213:20210126191417g:plain

そして、Calculate Directionノードは下図のこの角度を計算してくれます!

f:id:pafuhana1213:20210126191554p:plain

べんり!で、これで終わっては少し寂しいのです。せっかくなので取得した角度を使って何かしてみます。

Calculate Directionノードの結果を使ってキャラクタの体を傾けてみる

こちらの しょーごさんの ツイートにもある通り、BlendSpaceと併用することで移動方向に応じたアニメーションを再生しより自然な動きを表現することができます。


docs.unrealengine.com

というか、まさにCalculate DirectionノードとBlendSpaceの併用に関するチュートリアルがありますね!ガハハ!(ここまで書いた後に気づいた)
docs.unrealengine.com

とは言え、BlendSpace用にAnimationSequenceを複数個用意するのは大変です。なので、AnimGraphで使えるTransfom(Modify)Boneノードを使って簡単に傾けてみます。

と言っても簡単で、先程取得した角度を使って腰辺りの骨(spine_03ボーン)を回すだけです。なお、下図の赤枠にある通り、RotationModeはAddtoExistingにする必要があります。他の設定の場合だと、角度制御が効かなかったりアニメーションによる骨の回転量を無視して上書きするようになってしまいます。

f:id:pafuhana1213:20210126193031p:plain

そして、この結果が下図です!…骨の追加する回転量が大きすぎるため、全く自然な動きではありません。

f:id:pafuhana1213:20210126193533g:plain

これを解決するには色んな方法が考えられますが、恐らく最も手っ取り早いには追加する回転量に制限をかけちゃうことです。
このように -30 ~ 30 の範囲に抑えると…

f:id:pafuhana1213:20210126193901j:plain

このようにいい感じになります!ちょっとしたことですが、デフォルトより自然な動きになりました!

f:id:pafuhana1213:20210126194330g:plain

より自然な動きにしたい場合はBlendSpaceを用いるか、Control Rigを用いてより複雑な補正を行うことになるかと思います。後者に関してはいつか試してみたいなぁと思ったりしてます。

さいごに

Calculate Directionノードの使い方と簡易的な活用方法について説明してみました。移動方向に体を少し傾けるというちょっとしたことではありますが、アニメーションのクオリティはこういう事の積み重ねで上げていくものかと思います(逆に言うと、この辺りが疎かになっていると違和感が出てきてしまいます…)。

今回の方法でしたらサクッと導入できるかと思いますので、是非一度試してみてください!

おまけ

ちなみに、CalculateDirectionの内部実装はこんな感じ。…ふつうですね!

float UAnimInstance::CalculateDirection(const FVector& Velocity, const FRotator& BaseRotation) const
{
	if (!Velocity.IsNearlyZero())
	{
		FMatrix RotMatrix = FRotationMatrix(BaseRotation);
		FVector ForwardVector = RotMatrix.GetScaledAxis(EAxis::X);
		FVector RightVector = RotMatrix.GetScaledAxis(EAxis::Y);
		FVector NormalizedVel = Velocity.GetSafeNormal2D();

		// get a cos(alpha) of forward vector vs velocity
		float ForwardCosAngle = FVector::DotProduct(ForwardVector, NormalizedVel);
		// now get the alpha and convert to degree
		float ForwardDeltaDegree = FMath::RadiansToDegrees(FMath::Acos(ForwardCosAngle));

		// depending on where right vector is, flip it
		float RightCosAngle = FVector::DotProduct(RightVector, NormalizedVel);
		if (RightCosAngle < 0)
		{
			ForwardDeltaDegree *= -1;
		}

		return ForwardDeltaDegree;
	}

	return 0.f;
}
<||