Custom Animation Editor Workflow - Triggering Animation Mode

Is there a way to trigger the animation mode from an Editor script? I’m overlaying existing humanoid body animations with hitboxes (encoded as it’s own asset separate from the clip) for a fighting game, and would like to be able to temporarily induce a pose based on an Animation clip onto a skinned mesh, in the Editor, without having the changes be explicitly saved to the underlying GameObject in the scene. Is there a way to do this without ripping at UnityEditorInternal?

I do that by creating a playable graph, evaluating it, and then destroying it. Here’s the editor window script - it uses some helper methods, but you can probably figure out what those does and replace them. If you don’t want to save, you just take out the SetDirty call.

Snap Model to Animation Frame

using UnityEditor;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;

public class SnapModelToAnimationFrame : EditorWindow {

    public bool specificGameObject = true;
    public GameObject gameObject;
    public AnimationClip clip;
    public float time;
    public string[] specificGameobjectOptions = {"Selected objects", "Specific gameobject"};

    private void OnGUI() {
        EditorGUILayout.HelpBox("This window allows you to snap gameobjects to a target animation frame", MessageType.Info);

        specificGameObject = DropDownToggleAttributeDrawer.LayoutDropDownToggle(specificGameObject, specificGameobjectOptions);
        if (specificGameObject)
            gameObject = EditorTools.ObjectField("Target Gameobject", gameObject);
        clip = EditorTools.ObjectField("Use Clip", clip);
        time = EditorGUILayout.Slider("Time (normalized)", time, 0f, 1f);

        if ((gameObject != null || !specificGameObject) && clip != null) {
            if (EditorTools.Button($"Snap {gameObject.name} to {clip.name}'s frame at time {time * clip.length}")) {
                if (specificGameObject) {
                    SnapGameObject(gameObject);
                }
                else {
                    foreach (var go in Selection.gameObjects) {
                        SnapGameObject(go);
                    }
                }
            }
        }
    }

    private void SnapGameObject(GameObject gameObject) {
        var snapGraph = PlayableGraph.Create();
        var animator = gameObject.GetComponent<Animator>();
        var hadAnimator = animator != null;
        if (!hadAnimator)
            animator = gameObject.AddComponent<Animator>();

        var animOutput = AnimationPlayableOutput.Create(snapGraph, "AnimationOutput", animator);

        var clipPlayable = AnimationClipPlayable.Create(snapGraph, clip);
        animOutput.SetSourcePlayable(clipPlayable);

        snapGraph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
        snapGraph.GetRootPlayable(0).SetPropagateSetTime(true);
        snapGraph.GetRootPlayable(0).SetTime(time * clip.length);
        snapGraph.Evaluate();

        if (!hadAnimator)
            DestroyImmediate(animator);

        EditorFix.SetObjectDirty(this.gameObject);

        snapGraph.Destroy();
    }
}
1 Like

Is there a way to save and reset t he state of those objects? As well as prevent anything else from dirtying the object while the evaluated animation is active?