4
11

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 5 years have passed since last update.

【Unity(C#)】Animationとパラメータを連動させる方法

Last updated at Posted at 2019-05-26

##デモ
タイトルの内容について書きますが、百聞は一見に如かずということでご覧ください。

Gif_Squat.gif

膝の角度に合わせて数値及び、青い角度計が変化しています。

これは膝の角度を読み取っているわけではないです。

アニメーション(スクワット)の再生時間に応じてパラメータを変化させています。


2019/06/25 追記
アニメーションはこちらから拾ってきました→Mixamo

##GetCurrentAnimatorStateInfo
Animatorを作ると複数のステートを作成できます。

ステートはアニメーションを遷移させるときに便利です。

今回は再生中のアニメーションを調べる必要があるので、
GetCurrentAnimatorStateInfo(0)を使って調べます。


   [SerializeField]
   Animator squatAnimeController;

   void Update()
    {
       AnimatorStateInfo animeStateInfo = squatAnimeController.GetCurrentAnimatorStateInfo(0);
    }

GetCurrentAnimatorStateInfo(0)を使うことで、再生中のアニメーションを取得できます。
引数はアニメーションのLayerですが、複雑なアニメーションを作成してLayerを使用した場合を除いて、
基本的にデフォルトの0で大丈夫です。

AnimatorStateInfo.normalizedTime

normalizedTimeを使用すればアニメーションの再生時間を0~1の値に正規化することができます。

正規化された時間を利用して他のパラメータの値を操作すれば
デモのようにアニメーションに合わせて数値を変化させることができます。

##Mathf.Lerp
先程の正規化された時間とMathf.Lerpを利用して数値を連動して変化させます。
Mathf.Lerpの引数は3つ用意されており、Lerp(float a, float b, float t)となっています。

それぞれの引数は
a:開始値
b:終了値
t:補完値(0~1)
となっています。aの値からbの値に向けてtで補完します。
と言われてもいまいちわからないと思います。(私は使ってみるまでさっぱりでした)

なので一例を示します。

まずはコードです。


   [SerializeField]
   Animator squatAnimeController;

   [SerializeField]
   Text degreesText;

   void Update()
    {
       AnimatorStateInfo animeStateInfo = squatAnimeController.GetCurrentAnimatorStateInfo(0);

       float degrees = Mathf.Lerp(180, 0, animeStateInfo.normalizedTime); 
       degreesText.text = ((int)degrees).ToString() + "°";
    }

このように、アニメーションの再生時間(正規化されているので0~1)に合わせて
角度が180~0°で変化しているのがわかるかと思います。(GIFが汚いのはご容赦ください)
Gif_Squat_Explanation.gif

これが先程のaの値からbの値に向けてtで補完するという意味です。

a:開始値(180)
b:終了値(0)
t:補完値(正規化したアニメーションの再生時間0~1)

##Mathf.Repeat
先程のGIF画像ではステートを二つ用意して、同じアニメーションを交互に遷移させていました。
その理由としてはアニメーションのループ設定がうまくいかなかったからです。

ループ設定してしまうと、アニメーションの正規化がうまくできないようです。
0~1収まるはずの正規化した値が1を超えて無限に増えていきます。(ループに終了時間という概念が存在しないから?)

もし、なんらかの事情でアニメーションのループ設定を外せない場合は、
Mathf.Repeatで正規化した値が1を超えないようにします。

Mathf.Repeat(float t, float length)
指定値(length)を超えると0に戻り、再度 指定値まで循環します。

   [SerializeField]
   Animator squatAnimeController;

   [SerializeField]
   Text degreesText;

   void Update()
    {
       AnimatorStateInfo animeStateInfo = squatAnimeController.GetCurrentAnimatorStateInfo(0);

       float degrees = Mathf.Lerp(180, 0, Mathf.Repeat(animeStateInfo.normalizedTime, 1)); 
       degreesText.text = ((int)degrees).ToString() + "°";
    }

これでループ設定を外せないとしても安心です。
Gif_Squat_Explanation_2.gif

##fillAmount

角度計のアニメーションはこちらのサイトを参考にしました。

アニメーションに応じて、FillAmountの値を変化させています。
ImageFillAmount.PNG

##最終的なコード

当方、マジックナンバーおじさんですが、ご容赦ください。

作成した画像(色付き)にアタッチ
using UnityEngine.UI;
using UnityEngine;

public class AngleVisualController : MonoBehaviour
{
    [SerializeField]
    Animator squatAnimeController;

    AnimatorStateInfo animeStateInfo;

    [SerializeField]
    Text degreesText;

    Image circle_Image;

    void Start()
    {
        circle_Image = this.gameObject.GetComponent<Image>();
    }

    void Update()
    {
        animeStateInfo = squatAnimeController.GetCurrentAnimatorStateInfo(0);

        float squatVertical = 25 / 60.0f;
        float degrees = 0;

        if (animeStateInfo.normalizedTime < squatVertical)
        {
            circle_Image.fillAmount = Mathf.Lerp(0.48f, 0.25f, animeStateInfo.normalizedTime * (60.0f / 25));

            degrees = Mathf.Lerp(170, 90, animeStateInfo.normalizedTime * (60.0f / 25));
            degreesText.text = ((int)degrees).ToString() + "°";
        }
        else
        {
            circle_Image.fillAmount = Mathf.Lerp(0.25f, 0.48f, (animeStateInfo.normalizedTime-(25.0f/60)) * (60.0f / 25));

            degrees = Mathf.Lerp(90, 170, (animeStateInfo.normalizedTime - (25.0f / 60)) * (60.0f / 25));
            degreesText.text = ((int)degrees).ToString() + "°";
        }
    }
}

なぜこんなわけのわからない計算をLerpの中に仕込ませているかというと、
・スクワットのアニメーションの折り返し地点(正規化した値でいうと0.5)がしゃがんだ状態ではない
・角度の値が、直立(170)→しゃがむ(90)→直立(170)を取る
・アニメーションの再生フレームを取得できない
からです。

なので、もっとシンプルに使うには
・しゃがむ、立ち上がる の二つのアニメーションを用意する
なのかな~と思います。そうすれば、変な計算しなくても正規化した値をそのまま使えるかと思います。

本当は、アニメーションの再生フレームを取得して
0~25フレームはこの処理をして、26~60フレームは別の処理を...
みたいなことができれば便利なのですが、見つかりませんでした。(ありましたら教えてください)

4
11
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
4
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?