てんちょーの技術日誌

自分がつまづいたこととかメモ

【Unity】SetHumanPoseを使うとなぜか吹っ飛んでしまう時の対策

概要

HumanPoseHandlerを使っていたら、なぜか遥か彼方へ飛んでいってしまう…
みたいなことに遭遇していました

f:id:shop_0761:20210331145116g:plain

f:id:shop_0761:20210414094405g:plain

f:id:shop_0761:20210414094424g:plain

今回ようやく原因と一部対処方法が分かったので書いておきます

検証環境

Unity2019.4.22f1

発生条件

把握してる範囲だと2パターンあります

Aパターン

  1. HumanPoseHandler生成時の第2引数 root に不正な Pos/Rot/Scale 値がある
  2. SetHumanPose をする

Bパターン

  1. HumanPoseHandlerを生成する
  2. Animator が Attach されている Transform の Pos/Rot/Scale を変える
    (Editor操作を含む)
  3. SetHumanPose をする

対処方法

Bは対処方法がないので触らない…ということになってしまいます…

Aはキャラが原点にいないせいでHumanPoseHandler生成時に発生します
こっちのほうは対処法を見つけたのでコードを載せます

    private HumanPoseHandler CreateHumanPoseHandler(Animator animator)
    {
        if (animator == null) { return null; }
        var position = animator.transform.position;
        var rotation = animator.transform.rotation;
        var scale = animator.transform.localScale;

        animator.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
        animator.transform.localScale = Vector3.one;
        var humanPoseHandler = new HumanPoseHandler(animator.avatar, animator.transform);

        var hipBone = animator.GetBoneTransform(HumanBodyBones.Hips);
        hipBone.rotation = rotation;
        hipBone.position = position + hipBone.up * hipBone.position.y * scale.y;
        hipBone.localScale = scale;
        return humanPoseHandler;
    }

日本語にすると

  1. animatorのあるTransformをキャッシュ
  2. 全部初期値
    (Position : Vector.zero / Rotation: Quaternion.identity / Scale: Vector.one) に戻す
  3. HumanPoseHandlerの生成
  4. HipBoneにキャッシュ分を反映する

4つ目は恐らく HumanPose.bodyPosition/ HumanPose.bodyRotation でもよいですが
Scaleを反映させる術がなさそう?だったのでhipboneにしています

position + hipBone.up * hipBone.position.y * scale.y

この部分はUnityによくある体が半分くらい床に埋まってしまうやつの対策です
hipBoneのYだけ考慮したかったので up と掛け算しつつ、
元のscaleも計算に入れてつじつま合わせをしています

推測

おそらく animator.transform を基準にSetHumanPoseの計算をしており、
これが初期値でないと毎フレームoffsetとして加算されてしまうようです

他に試したこと

Hipの親をHumanPoseHandlerの第2引数にする

ちゃんとroot boneが用意されているモデルを使って試しました
吹っ飛ばなくなる…がMuscleが反映されませんでした
ということはanimator.transformを渡すのが必須のようですね…

HumanPose.bodyPosition/bodyRotation で反映する

途中までこっちで確認していましたが、やや計算誤差がありそうです

まとめ

調べてもあまり同じようなことで悩んでる人がいなさそう…?
だったので記事にしておきました

とりあえずanimator.transformを操作しなければ
一旦これで対策できそうなのでなんとかなってよかったです

なにかあれば Twitter までご連絡ください~