Search Unity

Showcase Live Animation Clip Selector

Discussion in 'Animation' started by summerscorcher, Feb 14, 2023.

  1. summerscorcher

    summerscorcher

    Joined:
    Apr 8, 2019
    Posts:
    1
    Hi all,

    I figured I'd share this small editor script I made since (to my knowledge) no good results about this issue seem pop up on Google.

    As an animator gets a lot of clips, I found it becomes cumbersome to select them for editing in the animation window. If you try to access your animation clips from the project window instead, it won't actually show on your game object in the scene thus not allowing you to edit the animation properly. With this script, you can select any animation clip in your project tab and it will select itself in the animation window on the last game object you had selected.

    It comes with the caveat of needing to have a custom editor window open/visible, but I find you can just dock it somewhere in the corner and forget about it. On the plus side, it does tell you the status of things in a little info bubble.


    How it works:
    1. Have the new window open from "Window > Animation > Animation Selector"
    2. Select a game object in your scene that has an Animator component.
    3. Now you can select any animation clip in your project tab, and it should work just like when using the animation window to select a clip.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using UnityEditor.SceneManagement;
    4. using UnityEngine.SceneManagement;
    5. using System.Threading.Tasks;
    6.  
    7. public class AnimationSelector : EditorWindow {
    8.  
    9.     protected GameObject[] gameObjects;
    10.     protected AnimationClip animationClip;
    11.     protected bool gameObjectsSelected;
    12.     protected bool animMismatch;
    13.     protected bool active = true;
    14.     protected bool closing = false;
    15.     protected bool gamePlaying = false;
    16.  
    17.     [MenuItem("Window/Animation/Animation Selector", false)]
    18.     private static void DoWindow() {
    19.         var window = GetWindowWithRect<AnimationSelector>(new Rect(0, 0, 300, 80), false, "Animation Selector");
    20.         window.Show();
    21.     }
    22.  
    23.     private void OnSelectionChange() {
    24.         gameObjectsSelected = Selection.gameObjects.Length > 0;
    25.  
    26.         if (Selection.gameObjects.Length > 0) {
    27.             animMismatch = false;
    28.             gameObjects = Selection.gameObjects;
    29.             Repaint();
    30.         }
    31.     }
    32.  
    33.     private void OnInspectorUpdate() {
    34.         Repaint();
    35.     }
    36.  
    37.     private void OnGUI() {
    38.         active = EditorGUILayout.Toggle("Active", active);
    39.  
    40.         if (!active) {
    41.             EditorGUILayout.HelpBox("Manually disabled. Toggle to re-activate.", MessageType.Info);
    42.             return;
    43.         }
    44.  
    45.         if (gameObjects == null) {
    46.             EditorGUILayout.HelpBox("Please select a GameObject.", MessageType.None);
    47.             return;
    48.         }
    49.  
    50.         Animator anim = null;
    51.  
    52.         if (gameObjects.Length == 1) {
    53.             anim = gameObjects[0].GetComponent<Animator>();
    54.             anim = anim ? anim : gameObjects[0].transform.root.GetComponent<Animator>();
    55.  
    56.             if (!anim) {
    57.                 EditorGUILayout.HelpBox("No Animator was found on GameObject '" + gameObjects[0].name + "'.", MessageType.None);
    58.                 return;
    59.             }
    60.         } else if (gameObjects.Length > 1) {
    61.             // If we have multiple game objects selected, just take the animator of the root.
    62.             Transform root = gameObjects[0].transform.root;
    63.             foreach (GameObject go in gameObjects) {
    64.                 if (go.transform.root != root) {
    65.                     EditorGUILayout.HelpBox("Please only select GameObjects that share the same root.", MessageType.None);
    66.                     return;
    67.                 }
    68.             }
    69.  
    70.             anim = gameObjects[0].transform.root.GetComponent<Animator>();
    71.             if (!anim) {
    72.                 EditorGUILayout.HelpBox("No Animator was found on the current selection of GameObjects.", MessageType.None);
    73.                 return;
    74.             }
    75.         }
    76.  
    77.         if (!anim) {
    78.             EditorGUILayout.HelpBox("No Animator was found on the current selection.", MessageType.None);
    79.             return;
    80.         }
    81.  
    82.         if (!anim.runtimeAnimatorController) {
    83.             EditorGUILayout.HelpBox("No Animator Controller was found on GameObject '" + anim.gameObject.name + "'.", MessageType.Warning);
    84.             return;
    85.         }
    86.  
    87.         if (animMismatch) {
    88.             EditorGUILayout.HelpBox("Animation not present on Animator '" + anim.runtimeAnimatorController.name + "'.", MessageType.Warning);
    89.             return;
    90.         }
    91.  
    92.         EditorGUILayout.HelpBox("Currently selected Animator: " + anim.runtimeAnimatorController.name, MessageType.Info);
    93.     }
    94.  
    95.     private void Awake() {
    96.         EditorSceneManager.activeSceneChangedInEditMode += OnSceneChanged;
    97.         EditorApplication.playModeStateChanged += OnPlayModeChanged;
    98.     }
    99.  
    100.     private void Update() {
    101.         if (!active) {
    102.             return;
    103.         }
    104.  
    105.         if (gameObjects == null)
    106.             return;
    107.  
    108.         if (Selection.activeObject != null && Selection.activeObject is AnimationClip) {
    109.             Animator anim = null;
    110.  
    111.             if (gameObjects.Length == 1) {
    112.                 anim = gameObjects[0].GetComponent<Animator>();
    113.                 anim = anim ? anim : gameObjects[0].transform.root.GetComponent<Animator>();
    114.             } else if (gameObjects.Length > 1) {
    115.                 // If we have multiple game objects selected, just take the animator of the root.
    116.                 Transform root = gameObjects[0].transform.root;
    117.                 foreach (GameObject go in gameObjects) {
    118.                     if (go.transform.root != root)
    119.                         return;
    120.                 }
    121.  
    122.                 anim = gameObjects[0].transform.root.GetComponent<Animator>();
    123.             }
    124.  
    125.             if (anim == null)
    126.                 return;
    127.  
    128.             animationClip = Selection.activeObject as AnimationClip;
    129.                
    130.             bool found = false;
    131.             foreach(AnimationClip clip in anim.runtimeAnimatorController.animationClips) {
    132.                 if (clip == animationClip) {
    133.                     animMismatch = false;
    134.                     found = true;
    135.                     break;
    136.                 }
    137.             }
    138.  
    139.             if (!found) {
    140.                 animMismatch = true;
    141.                 Repaint();
    142.                 return;
    143.             }
    144.  
    145.             Selection.objects = gameObjects;
    146.         }
    147.  
    148.         if (animationClip == null)
    149.             return;
    150.  
    151.         AnimationWindow animWindow = (AnimationWindow)AnimationWindow.GetWindow(typeof(AnimationWindow), false, null);
    152.  
    153.         if (!gameObjectsSelected)
    154.             Selection.objects = null;
    155.  
    156.         if (animWindow == null)
    157.             return;
    158.  
    159.         EditorGUI.BeginChangeCheck();
    160.         animWindow.animationClip = animationClip;
    161.         animationClip = null;
    162.         EditorGUI.EndChangeCheck();
    163.     }
    164.  
    165.     private void OnDestroy() {
    166.         closing = true;
    167.     }
    168.  
    169.     private void OnBecameInvisible() {
    170.         CheckWindowVisibility();
    171.     }
    172.  
    173.     async void CheckWindowVisibility() {
    174.         await Task.Delay(100);
    175.  
    176.         if (!gamePlaying && !closing)
    177.             Debug.LogError("Animation Selector window needs to be visible in order for it to work.");
    178.     }
    179.  
    180.     private void OnSceneChanged(Scene scene1, Scene scene2) {
    181.         gameObjects = null;
    182.         gameObjectsSelected = false;
    183.     }
    184.  
    185.     private void OnPlayModeChanged(PlayModeStateChange state) {
    186.         gamePlaying = false;
    187.         if (state == PlayModeStateChange.ExitingEditMode)
    188.             gamePlaying = true;
    189.     }
    190. }

    Tested it on 2021.3. This is my first time dabbling in Editor scripts, so if something's really inefficient or could be improved, I'd love to know ^^
     
    Last edited: Feb 14, 2023