2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Unity】VRIKとHumanPoseHandler.SetHumanPoseを併用するとプルプルする問題の備忘録

Last updated at Posted at 2023-05-22

はじめに

現在開発のアバターを使ったアプリでは、VRMモデルを下記の機能を併用して制御をしています。

  • Animator(全身のポーズに使用)
  • VRIK(腕の動きに使用)
  • HumanPoseHandler (指の制御に使用)

その際、下記のようにモデルがプルプルする現象が発生してしまました。

guiter.gif

結論としてはAnimatorUpdateModeNormalにすることで解決したのですが、根本的な原因は分からずじまいでした。色々と調べたこともあるので、この時の状況についてまとめておきたいと思います。

各実行タイミングについて

下記ページの図を見ながら整理しました。

必要なライフサイクルを抜粋すると、下記の流れを繰り返します。

  1. Physics
    1. FixedUpdate()
    2. Animation実行(AnimatorUpdateMode.AnimatePhysicsの場合)
  2. Game logic
    1. Update()
    2. Animation実行(AnimatorUpdateMode.Normalの場合)
    3. LateUpdate()
  3. Scene rendering(実際に画面に描画される)

PhysicsはGame logicと別のタイミングで実行されていて、どちらかが連続で呼ばれることもあります。

Animator

AnimatorUpdateModeの値によって実行タイミングが変わります。

  • AnimatorUpdateMode.Normal : Update()LateUpdated()の間で実行される
  • AnimatorUpdateMode.AnimatePhysics : FixedUpdate()の直後に実行される

Animatorを使うと基本的にすべての値が上書きされてしまうようで、スクリプトを併用する場合はAnimatorの実行後にタイミング(LateUpdate()など)で実行する必要があります。

AvatarMaskを使った方法についても検討しましたが、Maskした部分以外の値も上書きされてしまうようです。

なお、VRMのHumanoidはUnityのHumanoidAvatarを元にしていますが、Avatarオブジェクトは持っていないため、対応が失敗している可能性はあるかもしれません。

VRIK

VRIKはLateUpdate()内から呼び出されるUpdateSolver()内で、ボーンのtransformの値を直接書き換えることで姿勢の制御をしています。書き換え前の値は内部でキャッシュしておらず、同じフレーム内で取得しているようにみえました。

注目したいこととして、UpdateSolver()LateUpdate()内で呼ばれる時と呼ばれないときがありました。

(抜粋)SolverManager.cs
void FixedUpdate() {
	updateFrame = true;
}

void LateUpdate() {
	// Check if either animatePhysics is false or FixedUpdate has been called
	if (!animatePhysics) updateFrame = true;
	if (!updateFrame) return;
	updateFrame = false;
	
	UpdateSolver();
}

内容としてはこのように制御しているようです。

  • AnimatorUpdateMode.Normalの場合、毎回のLateUpdate()で実行
  • AnimatorUpdateMode.AnimatePhysicsの場合、FixedUpdate()が呼ばれた後のLateUpdate()のみで実行

HumanPoseHandler

下記のコードを一部改変して使用しています。Animationとの兼ね合いで、Update()ではなくLateUpdate()にて実行しています。

このコードでも、書き換え前の値は内部でキャッシュしておらず、同じフレーム内で取得しています。

.cs
var handler = new HumanPoseHandler(_HumanoidAnim.avatar, _HumanoidAnim.transform);
handler.GetHumanPose(ref targetHumanPose);
// 姿勢の変更
handler.SetHumanPose(ref targetHumanPose);

発生していたこと

AnimatorUpdateMode.AnimatePhysicsの時にVRIK内のUpdateSolver()が呼ばれないタイミングがあり、HumanPoseHandler.SetHumanPose()が連続で呼ばれる状況でプルプルが発生していました。

AnimatorUpdateMode.Normalではそれぞれが毎フレーム呼ばれるようになり、プルプルしなくなりました。同じLateUpdate()内での実行なので呼び出し順は不定なのですが、Script Execution Orderで指定してみても特に挙動は変わりませんでした。

分からなかったこと

  • プルプルする原因が分からない
  • VRIKもHumanPoseHandlerもそれぞれ同じフレーム内で直前の値を取得しているはずなのに、呼び出し頻度によって挙動が変わる理由が分からない

最後に

何か原因が思い当たることがある方は是非コメントなどで教えてもらえると嬉しいです!

Twitter: @nkjzm

2023/05/22 追記

便利デリゲートを教えていただきました!実行順を制御できて呼び出し頻度も連動させられるので、こちらの方がよさそうですね。ありがとうございます!

関連

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?