OOIII/

导入的FBX动画无法添加keyframe、curves、events显示read-only解决方案

在导入FBX动画后,如果想添加动画事件,则打开Animation,添加事件即可,但是直接编辑导入的Animation clips是不行的,显示read-only,这里有两种解决方法。
1、选中要操作的Animation clip,Ctrl+D复制一份出来,直接使用复制出来的这份即可。
2、可以使用插件来方便操作,以下代码修改自Unity论坛,加工完善了一下下,使用方法:选中要操作的Animation clip,单击Assets/Transfer Clips Curves to Copy选项即可。

using UnityEditor;
using UnityEngine;
using System.IO;
using System.Collections;

public class CurvesTransferer
{
    const string duplicatePostfix = "_copy";
    const string animationFolder = "Animations";

    static void CopyClip(string importedPath, string copyPath)
    {
        AnimationClip src = AssetDatabase.LoadAssetAtPath(importedPath, typeof(AnimationClip)) as AnimationClip;
        AnimationClip newClip = new AnimationClip();
        newClip.name = src.name + duplicatePostfix;
        AssetDatabase.CreateAsset(newClip, copyPath);
        AssetDatabase.Refresh();
    }

    [MenuItem("Assets/Transfer Clips Curves to Copy")]
    static void CopyCurvesToDuplicate()
    {
        // Get selected AnimationClip
        Object[] imported = Selection.GetFiltered(typeof(AnimationClip), SelectionMode.Unfiltered);
        if (imported.Length == 0)
        {
            Debug.LogWarning("Either no objects were selected or the objects selected were not AnimationClips.");
            return;
        }

        //If necessary, create the animations folder.
        if (Directory.Exists("Assets/" + animationFolder) == false)
        {
            AssetDatabase.CreateFolder("Assets", animationFolder);
        }

        foreach (AnimationClip clip in imported)
        {
            string importedPath = AssetDatabase.GetAssetPath(clip);

            Debug.Log("OriginalPath: " + importedPath);

            //If the animation came from an FBX, then use the FBX name as a subfolder to contain the animations.
            string copyPath;
            if (importedPath.Contains(".fbx") || importedPath.Contains(".FBX"))
            {
                //With subfolder.
                string folder = importedPath.Substring(importedPath.LastIndexOf("/") + 1, importedPath.LastIndexOf(".") - importedPath.LastIndexOf("/") - 1);
                if (!Directory.Exists("Assets/Animations/" + folder))
                {
                    AssetDatabase.CreateFolder("Assets/Animations", folder);
                }
                copyPath = "Assets/Animations/" + folder + "/" + clip.name + duplicatePostfix + ".anim";
            }
            else
            {
                //No Subfolder
                copyPath = "Assets/Animations/" + clip.name + duplicatePostfix + ".anim";
            }

            Debug.Log("CopyPath: " + copyPath);

            CopyClip(importedPath, copyPath);

            AnimationClip copy = AssetDatabase.LoadAssetAtPath(copyPath, typeof(AnimationClip)) as AnimationClip;
            if (copy == null)
            {
                Debug.Log("No copy found at " + copyPath);
                return;
            }
            // Copy curves from imported to copy
            AnimationClipCurveData[] curveDatas = AnimationUtility.GetAllCurves(clip, true);
            for (int i = 0; i < curveDatas.Length; i++)
            {
                AnimationUtility.SetEditorCurve(
                    clip, 
                    EditorCurveBinding.FloatCurve(curveDatas[i].path, curveDatas[i].type, curveDatas[i].propertyName), 
                    curveDatas[i].curve
                    );
             //   AnimationUtility.SetEditorCurve(
            //	copy,
            //	curveDatas[i].path,
            //	curveDatas[i].type,
            //	curveDatas[i].propertyName,
            //	curveDatas[i].curve
           // );
            }
            Debug.Log("Copying curves into " + copy.name + " is done");
        }
    }
}

补充:
第二种用脚本复制的方法,在某些情况下使用复制出来的Animation clip 时会报警告,不能使用,原因不明,不过根据警告信息也能猜个12345;还有一点,脚本复制出来的Animation clip 在Inspector中只有一个可调节的地方,但是手动复制的Animation clip 中基本原有的可调节项都在,可能脚本复制丢失一些信息,最好还是手动Ctrl+D吧,警告信息如下:

Animation clip 'chongfeng_copy' is not retargetable. Animation clips used within the Animator Controller need to have Muscle Definition set up in the Asset Importer Inspector
UnityEditor.DockArea:OnGUI()