Straafe / unity-editor-tools

Some simple quality of life tools for some specific tasks in Unity
50 stars 17 forks source link

Setting negative clip speed #1

Closed OmarWKH closed 2 years ago

OmarWKH commented 2 years ago

Why is ReverseAnimationContext needed instead of just copying the state and setting its speed to -1?

I read through the Unity thread and I don't see any answer to this.

Straafe commented 2 years ago

Hey Omar, -1 speed on a duplicate state might cover it in lots of situations, but there still could be reasons why you'd need a separate motion/clip instead of just a new state. The most obvious reason that comes to mind is if you needed to be able to edit the motion separately on the original and the reversed clip. I was also dealing with a large number of clips and had to find a way to automate reversing them (even if it only took 1 or 2 clicks to make a copied -1 speed state, doing that hundred(s) of times was not tenable). I can't remember for sure, but Unity changes a lot over the years and there may have been other limitations that have since been resolved that made having a separate clip an advantage.

You could also write a script to automate creating new states with a -1 speed, but you'd have to do it for all states on the Animator and couldn't easily control which individual states get reversed. Here is an example of how you might do something like that (with this script, you can right click on a AnimatorController, and create copies of all states on it with -1 speed):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;

public static class ReverseStatesContext
{
    [MenuItem("Assets/Reverse States", false, 16)]
    private static void SetInitialStates()
    {
        List<AnimatorController> conts = GetSelectedControllers();
        if (conts != null && conts.Count > 0)
        {
            foreach (AnimatorController con in conts)
            {
                ReverseState(con);
            }
        }
    }

    public static List<AnimatorController> GetSelectedControllers()
    {
        var conts = Selection.GetFiltered(typeof(AnimatorController), SelectionMode.Assets);
        List<AnimatorController> animConts = new List<AnimatorController>();
        if (conts.Length > 0)
        {
            foreach (var cont in conts)
            {
                animConts.Add(cont as AnimatorController);
            }
            return animConts;
        }
        return null;
    }

    private static void ReverseState(AnimatorController cont)
    {
        foreach (var layer in cont.layers)
        {
            AnimatorStateMachine asm = layer.stateMachine;
            foreach (var state in asm.states)
            {
                AnimatorState newState = asm.AddState(state.state.name + "_Reversed");
                newState.motion = state.state.motion;
                newState.speed = -1;
            }
        }
    }

    [MenuItem("Assets/Set Initial State", true)]
    static bool SetInitialStateValidation()
    {
        return Selection.activeObject && Selection.activeObject.GetType() == typeof(AnimatorController);
    }

}