animator.Play("DispOn"); と文字列指定するのが嫌。
でも public const DispOn = "DispOn"; と手書きで書くのも面倒(だし間違えるかも)。とにかく楽したい。
そう思い Editor 機能で自動的に const ファイルが作れないか、模索した時の話。
簡単そうで実は結構面倒でした…。
✖ RuntimeAnimatorController.animationClip
Animator anim = GetComponent<Animator>(); RuntimeAnimatorController ac = anim.runtimeAnimatorController; Debug.Log(ac.animationClips[0].name);
これは罠。一見取れそうに思え…たものの、DispOn という AnimationClip の名前が DisplayOn となっていた。
なぜだろう…記憶を思い起こす。
そういえば最初 DisplayOn という名前で Animation を作成、その後 DispOn にしたかも…?
この最初の名前は、後から Animation ファイル名を変えてもそのままになっており、それを取得しているようです。
そもそも Animator でつけた名前が欲しいので、Animation ファイルの名前ではだめですね。
〇UnityEditor.Animations.AnimatorControllerにキャスト
Animator anim = GetComponent<Animator>(); RuntimeAnimatorController ac = anim.runtimeAnimatorController; UnityEditor.Animations.AnimatorController acc = ac as UnityEditor.Animations.AnimatorController; for (int layer = 0; layer < acc.layers.Length; layer++) { for (int s = 0; s < acc.layers[layer].stateMachine.states.Length; s++) { UnityEditor.Animations.ChildAnimatorState state = acc.layers[layer].stateMachine.states[s]; Debug.Log(state.state.name); } }
鬼のようなキャストや配列に目がくらむものの、欲しい情報には届いた。
Parameter、その他諸々もアクセスできそう。
〇Animator のファイルを直接覗いてみる
これは最終手段と思っていますが、テキスト抽出能力の高い人なら最初から選択するかもしれない道筋。
Disp.controller(Animator ファイル)はテキスト形式なので…。
--- !u!1102 &7658924678026381332 AnimatorState: serializedVersion: 5 m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: DispOn m_Speed: 1 m_CycleOffset: 0 m_Transitions: [] m_StateMachineBehaviours: []
AnimatorState: の m_Name を抽出すれば取れなくはなさそう? 他にもずらりとメンバが並ぶ。
Assets/ 下にある .controller ファイルを探し、AnimatorController: という文字列があれば Animator と判断していいのではないでしょうか。
ただ、ファイルフォーマットが常にこのままとは限らないのがリスク。
コードで全ての Animator を取得するには?
シーンに存在しないものも含め、すべての Animator を取得するには以下のコード。
Animator[] animators = (Animator[])UnityEngine.Resources.FindObjectsOfTypeAll(typeof(Animator));
…で、取れるかと思ったら取れてませんでした(2020/10/2)。代わりの方法。
string[] files = Directory.GetFiles(Application.dataPath, "*.controller", SearchOption.AllDirectories); var animators = new List<RuntimeAnimatorController>(); foreach (string file in files) { // 雑なパス変換 string assetpath = FileUtil.GetProjectRelativePath(file).Replace("\\", "/"); "/"); RuntimeAnimatorController anim = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(assetpath); if (anim != null) { animators.Add(anim); } }
Animator の拡張子 .controller のファイル名を Assets/ 下から全て取得、Animator にキャストロードしてリスト化します。拡張子 .controller で Animator ではないファイルの場合、キャストロードが null になるのでリストには積まれません。
アニメーション名を自動で const 化するツール
ついでに公開。使えるかどうかは定かではない。
using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using UnityEditor; using UnityEngine; public class CreateAnimationConst : EditorWindow { /// <summary> /// コマンド名 /// </summary> const string ITEM_NAME = "Tools/Create Animation Const"; const string WINDOW_TITLE = "Create Animation Const"; const string BUTTON_SAVECREATE = "Save & Create"; const string BUTTON_CLEAR = "Clear"; const string PREFS_CS_PATH = ".cspath"; const string PREFS_PATTERN = ".pattern"; const string PREFS_REPLACEMENT = ".replacement"; [System.Serializable] public class AnimationName { public string RealName; public string Name; } [System.Serializable] public class DataParameter { /// <summary></summary> public string GroupName; /// <summary></summary> public List<AnimationName> Names; /// <summary></summary> public DataParameter(string groupName) { GroupName = groupName; Names = new List<AnimationName>(); } } Dictionary<string, DataParameter> animatorList; Vector2 curretScroll = Vector2.zero; string csPath = null; string pattern = null; string replacement = null; /// <summary> /// ウィンドウを開きます /// </summary> [MenuItem(ITEM_NAME)] static void create() { var window = GetWindow<CreateAnimationConst>(true, WINDOW_TITLE); window.Init(); } /// <summary> /// ウィンドウオープンの可否を取得します /// </summary> [MenuItem(ITEM_NAME, true)] static bool CanCreate() { bool enable = !EditorApplication.isPlaying && !Application.isPlaying && !EditorApplication.isCompiling; if (enable == false) { Debug.Log ($"{WINDOW_TITLE}: can't create. wait seconds."); } return enable; } /// <summary> /// 初期化 /// </summary> void Init() { loadPrefs(); animatorList = new Dictionary<string, DataParameter>(); addResourcesAnimator(); } void saveCode(string cspath) { StringBuilder sb = new StringBuilder(); foreach (var pair in animatorList) { DataParameter param = pair.Value; string name = getAnimationName(param.GroupName); sb.AppendLine( "///<summary>"); sb.AppendLine($"/// {name}"); sb.AppendLine( "///</summary>"); sb.AppendLine($"public class {name}"); sb.AppendLine( "{"); int length = 0; foreach (var namepair in param.Names) { if (length < namepair.Name.Length) { length = namepair.Name.Length; } } foreach (var namepair in param.Names) { sb.AppendLine($" public const string {namepair.Name.PadRight(length)} = \"{namepair.RealName}\";"); } sb.AppendLine( "}"); sb.AppendLine( ""); } File.WriteAllText(cspath, sb.ToString(), Encoding.UTF8); AssetDatabase.ImportAsset(cspath); } /// <summary> /// GUI を表示する時に呼び出されます /// </summary> void OnGUI() { if (animatorList == null) { Close(); return; } GUILayout.Space(20); csPath = EditorGUILayout.TextField("source code path", csPath); pattern = EditorGUILayout.TextField("regex pattern", pattern); replacement = EditorGUILayout.TextField("replacement", replacement); GUILayout.Space(20); // Button EditorGUILayout.BeginHorizontal(); if (GUILayout.Button(BUTTON_SAVECREATE)) { savePrefs(); saveCode(csPath); } if (GUILayout.Button(BUTTON_CLEAR)) { animatorList = new Dictionary<string, DataParameter>(); addResourcesAnimator(); } EditorGUILayout.EndHorizontal(); GUILayout.Space(20); // AnimatorLabels curretScroll = EditorGUILayout.BeginScrollView(curretScroll); EditorGUILayout.BeginVertical("box"); foreach (var pair in animatorList) { DataParameter param = pair.Value; string name = getAnimationName(param.GroupName); GUILayout.Label($"{name}", EditorStyles.boldLabel); for (int i = 0; i < param.Names.Count; i++) { param.Names[i].Name = EditorGUILayout.TextField(" ", param.Names[i].Name); } GUILayout.Space(20); } EditorGUILayout.EndVertical(); EditorGUILayout.EndScrollView(); } void addResourcesAnimator() { string[] files = Directory.GetFiles(Application.dataPath, "*.controller", SearchOption.AllDirectories); var animators = new List<RuntimeAnimatorController>(); foreach (string file in files) { // 雑なパス変換 string assetpath = FileUtil.GetProjectRelativePath(file).Replace("\\", "/"); RuntimeAnimatorController anim = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(assetpath); if (anim != null) { animators.Add(anim); } } foreach (RuntimeAnimatorController ac in animators) { if (ac == null || string.IsNullOrEmpty(ac.name) == true) { continue; } if (animatorList.ContainsKey(ac.name) == true) { continue; } animatorList.Add(ac.name, new DataParameter(ac.name)); // Animator に登録された名前を取得 UnityEditor.Animations.AnimatorController acc = ac as UnityEditor.Animations.AnimatorController; for (int layer = 0; layer < acc.layers.Length; layer++) { for (int s = 0; s < acc.layers[layer].stateMachine.states.Length; s++) { UnityEditor.Animations.ChildAnimatorState state = acc.layers[layer].stateMachine.states[s]; AnimationName name = new AnimationName(); name.RealName = state.state.name; if (name.RealName != ac.name) { // animator:Anim の時、AnimIn -> In name.Name = state.state.motion.name.Replace(ac.name, ""); } else { name.Name = getAnimationName(state.state.motion.name); } // FadeIn -> FADE_IN name.Name = Regex.Replace(name.Name, "(?<tag>[a-z0-9]+)(?<tag2>[A-Z]{1}[a-z]+)", "${tag}_${tag2}"); name.Name = name.Name.ToUpper(); animatorList[ac.name].Names.Add(name); } } } } /// <summary> /// アニメーション名取得 /// </summary> string getAnimationName(string name) { name = Regex.Replace(name, pattern, replacement); return name; } /// <summary> /// save prefs /// </summary> void savePrefs() { EditorPrefs.SetString(nameof(CreateAnimationConst) + PREFS_CS_PATH, csPath); EditorPrefs.SetString(nameof(CreateAnimationConst) + PREFS_PATTERN, pattern); EditorPrefs.SetString(nameof(CreateAnimationConst) + PREFS_REPLACEMENT, replacement); } /// <summary> /// load prefs /// </summary> void loadPrefs() { csPath = EditorPrefs.GetString(nameof(CreateAnimationConst) + PREFS_CS_PATH); pattern = EditorPrefs.GetString(nameof(CreateAnimationConst) + PREFS_PATTERN); replacement = EditorPrefs.GetString(nameof(CreateAnimationConst) + PREFS_REPLACEMENT); if (string.IsNullOrEmpty(csPath) == true) { csPath = "Assets/AnimationConst.cs"; } } }