なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

Unity で Animation を動的生成したい

元は VRChat 関連だけど、アバターの GameObject のツリー構造に依存せず、
自由にギミックを仕込む為の仕組みを作りたかった。
ただ、そのまま anim を配布しただけだと対応できないので、動的に生成してみました。

Animation を動的に生成するには、以下のファイルをエディター拡張から作成できれば良さそうです。

  • Animator Controller
  • Animation Clip

ということで、まずは Animation Clip を作成してみます。

Animation Clip の作成は簡単で、以下のようにやっていくことで作成できます。

var animation = new AnimationClip();

// Offset/GameObject を無効→有効にする
AnimatorUtility.SetEditorCurve(
    animation,
    EditorCurveBinding.FloatCurve("Offset/GameObject", typeof(GameObject), "m_IsActive"),
    AnimationCurve.Linear(0, 0, 1 / 60f, 1)
);

AssetDatabase.CreateAsset(animation, "path/to/anim.anim");

m_Active の部分とかは実際に作成してみて、 anim ファイルを覗いてみると書いてあります。 Offset/GameObject の部分は、 Animator をセットする GameObject からの、
相対パスを設定してあげればよいです。
ちなみに相対パスは以下のコードで求められます。

private static string GetPathBetweenGameObjects(GameObject root, GameObject child)
{
    var paths = new List<string>();
    var current = child.transform;

    while (current != null && current != root.transform)
    {
        paths.Add(current.name);
        current = current.parent;
    }

    paths.Reverse();
    return string.Join("/", paths);
}

Animation Clip を生成できたら、次は Animator Controller を作成します。
こちらは、コードで作るのが面倒だったので、予め作ってあるものをいじるようにしました。

まずは、テンプレートととなる Animator Controller をコピーし、それを読み込みます。

AssetDatabase.CopyAsset("/path/to/template.controller", "/path/to/dest.controller");
var controller = AssetDatabase.LoadAssetAtPath<AnimatorController>("/path/to/dest.controller");

次に、元となる State Machine を読み込み、 Animation Clip を設定していきます。

foreach (var state in stateMachine.states)
{
    // your code here...
    state.state.motion = animation;
}

次に、パラメータを追加します。
これはどうやら Remove してから Add しないと反応しないらしい?

foreach (var value in controller.parameters.Select((w, i) => (Index: i, Parameter: w)))
{
    controller.RemoveParameter(value.Index);

    // Key 部分はお好きに
    controller.AddParameter("Key", value.Parameter.type);
}

最後に条件を設定してあげます。

// Any -> Others
foreach (var transition in stateMachine.anyStateTransitions)
{
    if (transition.coditions.Length == 0)
        continue;

    // お好きに...
}

とまあ、こんな感じで、ある程度コードから animation を動的生成することが出来ました。
ということで、メモでした。