ぼっちプログラマのメモ

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

Anim Nodeに不正な値が設定された際に、ノードを Warning / Error 表示にしたり AnimBPをコンパイルエラーにする方法について

ログにErrorやWarning出しても気づかない可能性があるので、NodeをError, Warning状態にしたりエラーを発生させようというお話です。なお、AnimNode, AnimGraphNode という単語が出てきますが本記事ではそれについて解説しません。以下の記事が参考になるかと思います
UE4 アニメ―ションノードの作成(ボーンのトランスフォーム)を試してみる - Qiita

はじめに

自作の疑似物理プラグインKawaii Physics」にて、Anim Nodeをとある設定にするとクラッシュするという報告を受けました。

f:id:pafuhana1213:20200516165718p:plain
Rootと同じボーンをExcludeBoneに指定するとクラッシュするバグを修正 by kyi · Pull Request #12 · pafuhana1213/KawaiiPhysics · GitHub


クラッシュを回避すること自体は簡単だったのですが、事実上ノードが機能しなくなるので「不正な値がノードに設定されているよ!修正してね!」ということを作業者に伝えたい所です。また、ログに出力する対応が実装楽ですが…ログを確認してくれないことは稀によくあるので効果としては薄くなることが多いです。
[UE4] UE_LOGについてあれこれ|株式会社ヒストリア


ということで、Anm Nodeに不正な値が設定された際に「問題が発生していることがひと目で分かる」 & 「修正を促す」方法を探して見つけたのが、ValidateAnimNodePostCompile 関数です。下図のように、ERROR! や WARNING! 表示にできる上にAnimBPのコンパイルエラーも起こすことができます!
f:id:pafuhana1213:20200516170926p:plain

コンパイルエラーを起こせるのがとても素敵です。エラーを修正しないとAnimBPを動作させることができないので作業者に修正を強いることができます。また、パッケージ作成時にも同様にError / Warning出力 & パッケージ作成が発生するため、不正な値が入っている状態でパッケージが作られてしまうことを回避できます!

それではValidateAnimNodePostCompile 関数をどのようにして使うのかについて説明します。といっても超簡単です。

ValidateAnimNodePostCompile 関数の使い方

この関数は、UAnimGraphNode_Base で宣言されている仮想関数で、UAnimGraphNode_Base を継承する UAnimGraphNode_SkeletalControlBase では以下のように定義されています。

void UAnimGraphNode_SkeletalControlBase::ValidateAnimNodePostCompile(FCompilerResultsLog& MessageLog, UAnimBlueprintGeneratedClass* CompiledClass, int32 CompiledNodeIndex)
{
	if (UAnimationSettings::Get()->bEnablePerformanceLog)
	{
		const FAnimNode_SkeletalControlBase* Node = GetNode();
		if (Node && Node->LODThreshold < 0)
		{
			MessageLog.Warning(TEXT("@@ contains no LOD Threshold."), this);
		}
	}
}

上記のコードにある MessageLog.Warning関数 や MessageLog.Error関数 を呼び出すと Anim NodeをERROR! や WARNING! 表示にしたり AnimBPのコンパイルエラーを発生させられます。また、関数の第1引数にログやコンパイル結果に出力する文章を設定することができます。

ちなみに、KawaiiPhysicsの例で見せた Warning / Error に関してはこのようにコードを組んでいます。行っていることはとてもシンプルで、AnimNode側のパラメータを取得して判定してるだけです。なお、この関数が呼ばれる時点では RootBoneは初期化されていない ( FBoneReference内のパラメータが未設定で判定に使えない )ので、引数のCompiledClassから取得したTargetSkeletonを使って初期化処理を行っています。

void UAnimGraphNode_KawaiiPhysics::ValidateAnimNodePostCompile(FCompilerResultsLog& MessageLog, UAnimBlueprintGeneratedClass* CompiledClass, int32 CompiledNodeIndex)
{
	UAnimGraphNode_SkeletalControlBase::ValidateAnimNodePostCompile(MessageLog, CompiledClass, CompiledNodeIndex);

	Node.RootBone.Initialize(CompiledClass->TargetSkeleton);
	if (Node.RootBone.BoneIndex >= 0)
	{
		if (Node.ExcludeBones.Contains(Node.RootBone))
		{
			MessageLog.Error(TEXT("@@ ExcludeBones should NOT has RootBone."), this);
		}
	}
	else
	{
		MessageLog.Warning(TEXT("@@ RootBone is empty."), this);
	}
}

ちょっとしたことですがログに出力するだけよりも効果大かと思いますので、自作AnimNodeを作ってる方はぜひぜひ使ってみてください!

おしまい