元は 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 を動的生成することが出来ました。
ということで、メモでした。