Unity编辑器(Editor)的问题全解以及使用

Unity基础和Unity Editor基础 一、MonoBehaviour        所有需要挂载到GameObject上面的脚本,都需要继承自MonoBehaviour,MonoBehaviour是Unity中所有脚本驱动的基类。MonoBehaviour有一些事件函数会在特定的时间被调用,Unity的MonoBehaviour脚本的生命周期示意图如下: 1、FixedUpdate与Update的不同在于,Update在每帧被... 阅读详情

最近写了一个Unity优化工具,主要是搜索某一个文件夹中所有的场景和预设。如果是场景的话,就遍历场景中的所有对象,观察对象身上是否绑定了AudioListener组件,如果有的话移除该组件并保存场景。如果是预设的话,就遍历预设中的所有对象,观察预设中的对象是否绑定AudioListener组件,如果有的话移除该组件。

脚本如下:

增加:过滤非指定资源的执行(动画代码修改:)

using ML_Animation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;

class BlendAnimation : EditorWindow
{
    public static string[] saveLimitKey = new string[] {
        //"Bip001 Footsteps",
        "Bip001 Pelvis","Bip001 Spine",
        "Bip001 L Thigh","Bip001 R Thigh",
        "Bip001 L Calf", "Bip001 R Calf",
        //"Bip001 L Foot", "Bip001 R Foot",
        //"Bip001 L Toe0","Bip001 R Toe0", "Bip001 L Toe0Nub","Bip001 R Toe0Nub",
        //"Bip001 Spine1","Bone017","Bone021",
        //"Bip001 Prop1","Dummy001"
    };
    private static StringBuilder strBuilder;
    private static OptimizeAnimationClipProperty optAnimProperty = null;

    [MenuItem("Assets/优化/动画/选中AnimationClip-ModifyHeroCameraFOV", true, 0)]
    public static bool AnimationAddProperty_()
    {
        AnimationClip clip = Selection.activeObject as AnimationClip;
        if (clip == null)
            return false;
        else
            return true;
    }

    [MenuItem("Assets/优化/动画/选中AnimationClip-ModifyHeroCameraFOV", false, 0)]
    public static void AnimationAddProperty()
    {
        UnityEngine.Object[] objs = Selection.GetFiltered(typeof(AnimationClip), SelectionMode.DeepAssets);
        int count = 0;
        foreach (UnityEngine.Object obj in objs)
        {
            ++count;
            EditorUtility.DisplayProgressBar("正在修改动画,请勿操作Unity!!!", count + "/" + objs.Length, count * 1.0f / objs.Length);
            AnimationClip clip = obj as AnimationClip;
            EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
            for (int i = 0; i < curveBindings.Length; i++)
            {
                EditorCurveBinding curveBind = curveBindings[i];
                if (curveBind.type == typeof(Camera))
                {
                    AnimationCurve animCurve = AnimationUtility.GetEditorCurve(clip, curveBind);
                    AnimationUtility.SetEditorCurve(clip, curveBind, null);
                    curveBind.path = "cam/m_3DHeroCamera";
                    AnimationUtility.SetEditorCurve(clip, curveBind, animCurve);

                    AnimationCurve addCurve = new AnimationCurve();
                    addCurve.AddKey(0, 0);
                    addCurve.AddKey(clip.length, 0);
                    clip.SetCurve("cam/m_3DHeroCamera", typeof(Transform), "m_LocalRotation.x", addCurve);
                    clip.SetCurve("cam/m_3DHeroCamera", typeof(Transform), "m_LocalRotation.y", addCurve);
                    clip.SetCurve("cam/m_3DHeroCamera", typeof(Transform), "m_LocalRotation.z", addCurve);
                }
            }
        }
        AssetDatabase.Refresh();
        EditorUtility.ClearProgressBar();
    }

    [MenuItem("Assets/优化/动画/选中AnimationClip-优化动画", false, 1)]
    public static void AnimOptimizeTrilogy()
    {
        int count = 0;
        UnityEngine.Object[] objs = Selection.GetFiltered(typeof(AnimationClip), SelectionMode.DeepAssets);
        foreach (UnityEngine.Object o in objs)
        {
            count++;
            EditorUtility.DisplayProgressBar("优化动画三部曲...", AssetDatabase.GetAssetPath(o), count * 1.0f / objs.Length);

            AnimationClip clip = RemoveAnimationSameKeyframe(o as AnimationClip);
            ExecuteOptimaze_(clip);
            ReduceAnimationCurve(ref clip);
        }
        AssetDatabase.SaveAssets();
        EditorUtility.ClearProgressBar();
    }

    [MenuItem("Assets/优化/动画/选中Prefab-优化动画", false, 2)]
    public static void OptimizeAnimation()
    {
        strBuilder = new StringBuilder();
        optAnimProperty = new OptimizeAnimationClipProperty();

        UnityEngine.Object[] objs = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.DeepAssets);
        for (int i = 0; i < objs.Length; i++)
        {
            EditorUtility.DisplayProgressBar("优化动画三部曲...", AssetDatabase.GetAssetPath(objs[i]), i * 1.0f / objs.Length);

            string path = AssetDatabase.GetAssetPath(objs[i]);
            string suffix = Path.GetExtension(path);
            if (suffix == ".prefab")
            {
                optAnimProperty.AllPrefabNum++;
                OptimizeAnimationClips(path);
            }
        }

        PrintLog();
        AssetDatabase.SaveAssets();
        EditorUtility.ClearProgressBar();
    }

    public static void OptimizeAnimationClips(string path)
    {
        bool isModifyAnimClip = false;
        List<string> optAnims = new List<string>();
        string[] depends = AssetDatabase.GetDependencies(path);
        for (int i = 0; i < depends.Length; i++)
        {
            if (Path.GetExtension(depends[i]) == ".anim")
            {
                if (optAnimProperty.hadOperation.Contains(depends[i]))
                {
                    continue;
                }
                else
                {
                    optAnimProperty.hadOperation.Add(depends[i]);
                    optAnims.Add(depends[i]);
                }

                AnimationClip clip = AssetDatabase.LoadAssetAtPath(depends[i], typeof(AnimationClip)) as AnimationClip;
                if (clip == null)
                {
                    Debug.LogError(depends[i] + " is not Exist");
                    continue;
                }
                clip = RemoveAnimationSameKeyframe(clip);
                ExecuteOptimaze_(clip);
                ReduceAnimationCurve(ref clip);

                optAnimProperty.ModifyAnimNum++;
                isModifyAnimClip = true;
            }
        }

        if (optAnims.Count > 0)
        {
            optAnimProperty.dicLog.Add(path, optAnims);
        }
        if (isModifyAnimClip)
            optAnimProperty.ModifyPrefabNum++;
    }

    //[MenuItem("Assets/优化/动画/精简动画小数点")]
    private static void AnimationDataOptimaze()
    {
        int number = 0;
        DateTime time = DateTime.Now;
        foreach (UnityEngine.Object o in Selection.GetFiltered(typeof(AnimationClip), SelectionMode.DeepAssets))
        {
            number++;
            ExecuteOptimaze(Instantiate(o) as AnimationClip, AssetDatabase.GetAssetPath(o));
        }
        AssetDatabase.SaveAssets();
        Debug.Log("一共压缩了" + number + "个动画文件!");
        Debug.Log("耗时:" + (DateTime.Now - time).TotalMilliseconds / 1000 + "秒.");
    }

    //[MenuItem("Assets/优化/动画/优化动画小数点,不对动画重新创建")]
    public static void OptimazeAnimationDecimalPoint()
    {
        int count = 0;
        UnityEngine.Object[] objs = Selection.GetFiltered(typeof(AnimationClip), SelectionMode.DeepAssets);
        foreach (UnityEngine.Object o in objs)
        {
            count++;
            EditorUtility.DisplayProgressBar("优化动画小数点,不重新创建动画", count + "/" + objs.Length, count * 1.0f / objs.Length);
            ExecuteOptimaze_(o as AnimationClip);
        }
        EditorUtility.ClearProgressBar();
    }
    private static AnimationClip ExecuteOptimaze_(AnimationClip clip)
    {
        EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
        for (int i = 0; i < curveBindings.Length; i++)
        {
            AnimationCurve animCurve = AnimationUtility.GetEditorCurve(clip, curveBindings[i]);
            var keyFrames = animCurve.keys;
            for (int j = 0; j < keyFrames.Length; j++)
            {
                var key = keyFrames[j];
                key.value = float.Parse(key.value.ToString("f4"));
                key.inTangent = float.Parse(key.inTangent.ToString("f4"));
                key.outTangent = float.Parse(key.outTangent.ToString("f4"));
                keyFrames[j] = key;
            }
            animCurve.keys = keyFrames;
            AnimationUtility.SetEditorCurve(clip, curveBindings[i], animCurve);
        }
        //AssetDatabase.SaveAssets();
        return clip;
    }

    private static AnimationClip ExecuteOptimaze(AnimationClip clip, string clipName)
    {
        EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
        AnimationClipCurveData[] curves = new AnimationClipCurveData[curveBindings.Length];
        for (int index = 0; index < curves.Length; ++index)
        {
            curves[index] = new AnimationClipCurveData(curveBindings[index]);
            curves[index].curve = AnimationUtility.GetEditorCurve(clip, curveBindings[index]);
        }
        foreach (AnimationClipCurveData curveDate in curves)
        {
            var keyFrames = curveDate.curve.keys;
            for (int i = 0; i < keyFrames.Length; i++)
            {
                var key = keyFrames[i];
                key.value = float.Parse(key.value.ToString("f4"));
                key.inTangent = float.Parse(key.inTangent.ToString("f4"));
                key.outTangent = float.Parse(key.outTangent.ToString("f4"));
                keyFrames[i] = key;
            }
            curveDate.curve.keys = keyFrames;
            clip.SetCurve(curveDate.path, curveDate.type, curveDate.propertyName, curveDate.curve);
        }
        AssetDatabase.CreateAsset(clip, clipName);

        return clip;
    }

    private static void ControlAnimationMetaFile(string metaPath, bool bRead)
    {
        if (bRead)
        {
            strBuilder = new StringBuilder();
            FileStream fs = new FileStream(metaPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            StreamReader sr = new StreamReader(fs, System.Text.Encoding.Default);
            while (!sr.EndOfStream)
            {
                strBuilder.AppendLine(sr.ReadLine());
            }
            sr.Dispose();
        }
        else
        {
            if (File.Exists(metaPath))
                File.Delete(metaPath);
            StreamWriter sw = new StreamWriter(metaPath);
            sw.Write(strBuilder);
            sw.Dispose();
        }
    }

    //[MenuItem("Assets/优化/动画/移除相同关键帧")]
    public static void AnimationRemoveKeyFrame()
    {
        UnityEngine.Object[] objs = Selection.objects;
        for (int i = 0; i < objs.Length; i++)
        {
            UnityEngine.Object obj = objs[i];
            if (obj.GetType() == typeof(AnimationClip))
            {
                EditorUtility.DisplayProgressBar("移除动画相同关键帧...", AssetDatabase.GetAssetPath(obj), i * 1.0f / objs.Length);
                RemoveAnimationSameKeyframe(obj as AnimationClip);
            }
        }
        AssetDatabase.SaveAssets();
        EditorUtility.ClearProgressBar();
    }
    public static AnimationClip RemoveAnimationSameKeyframe(AnimationClip clip)
    {
        string path = AssetDatabase.GetAssetPath(clip);
        EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
        foreach (var curve in curveBindings)
        {
            AnimationCurve animCurve = AnimationUtility.GetEditorCurve(clip, curve);
            if (animCurve.keys.Length > 1 && !curve.isPPtrCurve)
            {
                Keyframe cntKeyframe = animCurve.keys[0];
                List<Keyframe> m_Keyframes = new List<Keyframe>();
                m_Keyframes.Add(cntKeyframe);
                int sameIndex = 0;
                for (int i = 1; i < animCurve.keys.Length; ++i)
                {
                    Keyframe item = animCurve.keys[i];
                    m_Keyframes.Add(item);

                    if (Mathf.Abs(item.value - cntKeyframe.value) > 0.00001f
                        || i == animCurve.keys.Length - 1
                        || Math.Abs(item.inTangent - cntKeyframe.inTangent) > 0.00001f
                        || Math.Abs(item.outTangent - cntKeyframe.outTangent) > 0.00001f
                        || item.tangentMode != cntKeyframe.tangentMode)
                    {
                        sameIndex = 0;
                    }
                    else
                    {
                        sameIndex += 1;
                    }
                    cntKeyframe = item;
                    if (sameIndex > 1)
                    {
                        m_Keyframes.RemoveAt(m_Keyframes.Count - 2);
                    }
                }

                animCurve.keys = m_Keyframes.ToArray();
                AnimationUtility.SetEditorCurve(clip, curve, animCurve);
            }
        }
        return clip;
    }

    [MenuItem("Assets/优化/动画/根据FBX(原始资源)生成混合动画", false, 3)]
    public static void AnimationOptimization()
    {
        List<AnimationClip> animClips = new List<AnimationClip>();
        UnityEngine.Object[] animObjs = Selection.objects;
        for (int i = 0; i < animObjs.Length; i++)
        {
            AnimationClip anim = animObjs[i] as AnimationClip;
            if (anim)
            {
                animClips.Add(anim);
            }
        }
        UnityEngine.Object[] objs = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.DeepAssets);
        for (int i = 0; i < objs.Length; i++)
        {
            string path = AssetDatabase.GetAssetPath(objs[i]);
            string suffix = Path.GetExtension(path).ToLower();
            if (suffix == ".fbx")
            {
                GetAnimationClip(path, animClips);
            }
        }
    }

    public static void GetAnimationClip(string fbxPath, List<AnimationClip> animClips)
    {
        UnityEngine.Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath(fbxPath);
        AnimationClip idleClip = null;
        for (int i = 0; i < objs.Length; i++)
        {
            idleClip = objs[i] as AnimationClip;
            if (idleClip != null && !idleClip.name.ToLower().Contains("action"))
            {
                if (animClips.Count > 0)
                {
                    if (animClips.Contains(idleClip))
                        CreateBlendAnimation(idleClip, fbxPath);
                    else
                        continue;
                }
                else
                {
                    CreateBlendAnimation(idleClip, fbxPath);
                }
            }
            else
                continue;
        }
    }

    public static void CreateBlendAnimation(AnimationClip idleClip, string fbxPath)
    {
        List<AnimationClipCurveData> lstAnimClipCurve = new List<AnimationClipCurveData>();
        List<AnimationClipCurveData> lstAnimCompressClipCurve = new List<AnimationClipCurveData>();
        List<AnimationClipCurveData> lstAnimBlendClipCurve = new List<AnimationClipCurveData>();

        //AnimationClip clip = AssetDatabase.LoadAssetAtPath<AnimationClip>(fbxPath);
        ModelImporter importer = AssetImporter.GetAtPath(fbxPath) as ModelImporter;
        if (idleClip == null || importer == null)
            return;

        if (importer.animationCompression != ModelImporterAnimationCompression.Off)
        {
            importer.animationCompression = ModelImporterAnimationCompression.Off;
            importer.SaveAndReimport();
        }
        AnimationClip blendAnimClip = UnityEngine.Object.Instantiate(idleClip);
        blendAnimClip.ClearCurves();

        lstAnimClipCurve = RecordAnimationCurve(idleClip);
        importer.animationCompression = ModelImporterAnimationCompression.KeyframeReduction;
        importer.SaveAndReimport();
        lstAnimCompressClipCurve = RecordAnimationCurve(idleClip);

        for (int i = 0; i < lstAnimClipCurve.Count; i++)
        {
            string[] splitPath = lstAnimClipCurve[i].path.Split('/');
            string keys = splitPath[splitPath.Length - 1];
            if (saveLimitKey.ToList().Contains(keys))
            {
                lstAnimBlendClipCurve.Add(lstAnimClipCurve[i]);
            }
            else
            {
                lstAnimBlendClipCurve.Add(lstAnimCompressClipCurve[i]);
            }
        }

        for (int i = 0; i < lstAnimBlendClipCurve.Count; i++)
        {
            AnimationClipCurveData data = lstAnimBlendClipCurve[i];
            blendAnimClip.SetCurve(data.path, data.type, data.propertyName, data.curve);
        }
        ReduceAnimationCurve(ref blendAnimClip);
        string path2 = string.Format("{0}/{1}.anim", Path.GetDirectoryName(fbxPath), idleClip.name);
        SaveAnim(blendAnimClip, path2);
    }

    /// <summary>
    /// 记录AnimationClip的曲线
    /// </summary>
    /// <param name="clip"></param>
    /// <returns></returns>
    public static List<AnimationClipCurveData> RecordAnimationCurve(AnimationClip clip)
    {
        EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
        List<AnimationClipCurveData> curveDatas = new List<AnimationClipCurveData>();

        for (int i = 0; i < curveBindings.Length; i++)
        {
            AnimationCurve animCurve = AnimationUtility.GetEditorCurve(clip, curveBindings[i]);
            AnimationClipCurveData curveData = new AnimationClipCurveData(curveBindings[i]);
            curveData.curve = animCurve;

            curveDatas.Add(curveData);
        }

        return curveDatas;
    }

    /// <summary>
    /// 移除动画不必要的曲线
    /// </summary>
    /// <param name="clip"></param>
    public static void ReduceAnimationCurve(ref AnimationClip clip)
    {
        SerializedObject serializedObject = new SerializedObject(clip);
        serializedObject.FindProperty("m_EditorCurves").arraySize = 0;
        serializedObject.FindProperty("m_EulerEditorCurves").arraySize = 0;
        //serializedObject.FindProperty("m_FloatCurves").arraySize = 0;
        serializedObject.ApplyModifiedProperties();
    }

    public static void SaveAnim(AnimationClip clip, string clipName)
    {
        if (!Directory.Exists(Path.GetDirectoryName(clipName)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(clipName));
        }
        //clip.legacy = true;
        //clip.wrapMode = WrapMode.Loop;
        AssetDatabase.CreateAsset(clip, clipName);
        AssetDatabase.Refresh();
    }

    private static void PrintLog()
    {
        string path = Application.dataPath.Replace("Assets", "ProfilerData/AnimOptimize.txt");
        Debug.Log(path);
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        if (File.Exists(path))
            File.Delete(path);

        foreach (var item in optAnimProperty.dicLog)
        {
            string labelName = string.Empty;
            for (int i = 0; i < item.Value.Count; i++)
            {
                labelName += item.Value[i] + ",";
            }
            strBuilder.Append(string.Format("{0}:{1}\n", item.Key, labelName));
        }
        strBuilder.Append(string.Format("检索Prefab数量:{0}\n需要修改的Prefab数量:{1},占总量的:{2}%\n修改的Animation数量:{3}"
            , optAnimProperty.AllPrefabNum, optAnimProperty.ModifyPrefabNum, optAnimProperty.ModifyPrefabNum * 1.0f / optAnimProperty.AllPrefabNum * 100, optAnimProperty.ModifyAnimNum));
        using (StreamWriter sw = new StreamWriter(path))
        {
            sw.Write(strBuilder);
            sw.Dispose();
        }
    }
}

public class OptimizeAnimationClipProperty
{
    public int AllPrefabNum;
    public int ModifyAnimNum;
    public int ModifyPrefabNum;
    public Dictionary<string, List<string>> dicLog = null;
    public List<string> hadOperation = null;

    public OptimizeAnimationClipProperty()
    {
        dicLog = new Dictionary<string, List<string>>();
        hadOperation = new List<string>();
    }
}
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;

public class SceneOptimize : Editor {

    #region 场景优化
    //某一文件夹中的所有场景的路径,复制的场景路径用于把所有场景都复制到另一个文件夹中
    public static List<string> optimizedScenePaths;
    //某一文件夹中的所有场景的路径
    public static List<string> optimizedOriginalScenePaths;
    [MenuItem("Tools/场景优化")]
    public static void CopyAndOptimizeScenes()
    {
        //复制的场景路径
        optimizedScenePaths = new List<string>();
        //原来的场景路径
        optimizedOriginalScenePaths = new List<string>();
        //项目的路径,eg:D:/Project/Test/Assets
        string headPath = Application.dataPath;
        //Debug.Log(headPath);
        //Debug.Log(headPath.IndexOf("Assets"));
        //eg:D:/Project/Test/
        headPath = headPath.Substring(0, headPath.IndexOf("Assets"));
        Debug.Log(headPath);

        try
        {
            //复制场景到另一个文件夹中
            //CopyAllScenes("Assets/Level", "Assets/Level_optimized", headPath);
            //获取某一文件夹中所有场景的Path信息
            AddRootSourcePathToList("Assets/Level", headPath, "Scene", ref optimizedOriginalScenePaths);
        }
        catch (System.Exception e)
        {
            PlayerPrefs.SetInt("ModifySaving", 0);
            Debug.LogError(e);
        }
        
        //遍历出所有场景的路径
        for (int i = 0; i < optimizedOriginalScenePaths.Count; i++)
        {
            PlayerPrefs.SetInt("ModifySaving", 0);
            //显示遍历场景时候的进度条
            EditorUtility.DisplayCancelableProgressBar("Scene Optimizing Bar", "Save And Optimize Scenes" + i + "/" + optimizedOriginalScenePaths.Count, (float)i / optimizedOriginalScenePaths.Count);
            try
            {
                Debug.Log(optimizedOriginalScenePaths[i]);
                //获取到场景的完整路径
                string tempPath = headPath + "Assets/Level/";
                Debug.Log("tempPath :" + tempPath);
                //只获取场景的名字(eg:Main.unity,Main场景的全称)
                string tempScene = optimizedOriginalScenePaths[i].Substring(tempPath.Length);
                Debug.Log("tempScene:" + tempScene);
                //增加判断条件,对于不需要做修改的场景
                if(tempScene == "Main.unity")
                {
                    Debug.Log("Continue");
                    continue;
                }
                //打开场景
                EditorSceneManager.OpenScene(optimizedOriginalScenePaths[i], OpenSceneMode.Single);
                //开始优化
                AudioListenerOptimize();
                PlayerPrefs.SetInt("ModifySaving", 1);
                //保存修改之后的场景
                EditorSceneManager.SaveScene(SceneManager.GetActiveScene());
            }
            catch (System.Exception e)
            {
                PlayerPrefs.SetInt("ModifySaving", 0);
                //如果发生错误,要关闭加载时候显示的滚动条
                EditorUtility.ClearProgressBar();
                Debug.Log(e);
            }
        }
        //加载完毕,关闭滚动条
        EditorUtility.ClearProgressBar();
        Debug.Log("finished");
        PlayerPrefs.SetInt("ModifySaving", 0);
    }
    //对于要修改的内容
    public static void AudioListenerOptimize()
    {
        //获取一个场景中所有的绑定了AudioListener对象
        AudioListener[] audios = FindObjectsOfType<AudioListener>();
        for (int i = 0; i < audios.Length; i++)
        {
            if (audios[i] is AudioListener)
            {
                //移除对象身上的AudioListener组件
                DestroyImmediate(audios[i].GetComponent<AudioListener>(),true);
            }
        }
    }

    /// <summary>
    /// 复制场景到另一个文件夹中
    /// </summary>
    /// <param name="rootPath"></param>
    /// <param name="targetPath"></param>
    /// <param name="headPath"></param>
    public static void CopyAllScenes(string rootPath, string targetPath, string headPath)
    {
        if (Directory.Exists(rootPath))
        {
            string[] guids;
            guids = AssetDatabase.FindAssets("t:Scene", new[] { rootPath });
            foreach (string guid in guids)
            {
                Debug.Log("guid: " + guid);
                string source = AssetDatabase.GUIDToAssetPath(guid);
                Debug.Log("source: " + source);
                string dest = source;
                Debug.Log("dest: " + dest);

                for (int i = 0; i < CountSubString("Level", "/") + 2; i++)
                {
                    Debug.Log("dest.IndexOf: " + dest.IndexOf("/"));
                    dest = dest.Substring(dest.IndexOf("/") + 1);
                }

                dest = "/" + dest;
                Debug.Log("dest: " + dest);
                dest = targetPath + dest;
                Debug.Log("dest: " + dest);

                CopySingleScene(source, dest, headPath);
            }
        }
        else
        {
            Debug.Log("No source path");
        }
    }

    public static void CopySingleScene(string source, string dest, string headPath)
    {
        source = headPath + source;
        dest = headPath + dest;

        string destDirectory = dest.Substring(0, dest.LastIndexOf("/"));
        if (!Directory.Exists(destDirectory))
        {
            Directory.CreateDirectory(destDirectory);
        }
        dest = dest.Substring(0, dest.LastIndexOf(".")) + "_Optimized.unity";
        File.Copy(source, dest, true);
        optimizedScenePaths.Add(dest);
    }

    public static int CountSubString(string targetString, string subString)
    {
        int p = 0;
        while (targetString.Contains(subString))
        {
            p++;
            targetString = targetString.Substring(targetString.IndexOf(subString) + 1);
        }
        Debug.Log("p:" + p);
        return p;
    }
    #endregion

    /// <summary>
    /// 根据在固定文件夹中的路径,根据类型(type)搜索该文件夹中的所符合的对象,然后把这些对象的路径添加到array中
    /// </summary>
    /// <param name="rootPath">目标文件路径(eg:Assets/Scenes)</param>
    /// <param name="headPath">项目路径(eg:D:Project/Test/)</param>
    /// <param name="searchType">搜索类型(Scene(搜索的是场景),Prefab(搜多的是预设))</param>
    /// <param name="array"></param>
    public static void AddRootSourcePathToList(string rootPath, string headPath, string searchType, ref List<string> array)
    {
        if (Directory.Exists(rootPath))
        {
            string[] guids;
            //搜索
            guids = AssetDatabase.FindAssets("t:" + searchType, new[] { rootPath });
            foreach (string guid in guids)
            {
                string source = AssetDatabase.GUIDToAssetPath(guid);
                //path:(D:Project/Test/Assets/Scenes/Main.unity)
                string path = headPath + source;
                array.Add(path);
            }
        }
        else
        {
            Debug.LogError("No Find Source Path");
        }
    }

    #region Prefab优化
    public static List<string> optimizePrefabsList;
    [MenuItem("Tools/预设优化")]
    public static void OptimizePrefabs()
    {
        optimizePrefabsList = new List<string>();
        string headPath = Application.dataPath;
        headPath = headPath.Substring(0, headPath.IndexOf("Assets"));
        Debug.Log(headPath);
        try
        {
            AddRootSourcePathToList("Assets/CurPrefabs", headPath,"Prefab",ref optimizePrefabsList);
        }
        catch (System.Exception e)
        {
            Debug.LogError(e);
        }
        Debug.Log("optimizePrefabsList:" + optimizePrefabsList.Count);
        for (int i = 0; i < optimizePrefabsList.Count; i++)
        {
            Debug.Log(optimizePrefabsList[i]);
            EditorUtility.DisplayCancelableProgressBar("Prefabs Optimizing Bar", "Save And Optimize Prefabs" + i + "/" + optimizePrefabsList.Count, (float)i / optimizePrefabsList.Count);
            //tempPath(eg:Assets/Prefabs/Cube.prefab)
            string tempPath = optimizePrefabsList[i].Substring(headPath.Length);
            //根据路径找到对象
            GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(tempPath);
            //Debug.Log(tempPath);
            //Debug.Log(prefab.name);
            OptimizePrefabRemoveAudioListener(prefab);
        }
        EditorUtility.ClearProgressBar();
        Debug.Log("finished");
    }

    public static void OptimizePrefabRemoveAudioListener(GameObject prefab)
    {
        AudioListener[] audios = prefab.GetComponentsInChildren<AudioListener>();
        Debug.Log(audios.Length);
        for (int i = 0; i < audios.Length; i++)
        {
            Debug.Log(audios[i].name);
            DestroyImmediate(audios[i].GetComponent<AudioListener>(),true);
        }
    }
    #endregion
}

下面分析Editor中的问题:

1,Unity中的Editor的基础篇:

Unity Editor 基础篇(十一):MenuItem属性_鳄鱼泪的博客-优快云博客_menuitem属性

2,Unity编辑器不用实例化Prefab获取,删除,更新组件:

Unity3D研究院编辑器之不实例化Prefab获取删除更新组件(十五) | 雨松MOMO程序研究院

3,根据对象名字,标签或者类型查找某一个文件夹中所有对象的路径

AssetDatabase-FindAssets的解析:AssetDatabase-FindAssets_京畿小李_新浪博客

4,游戏运行的时候对场景中对象的修改,通过代码控制保存的方法

[Unity]保存运行时对Scene中GameObject属性的修改_tlrainty的专栏-优快云博客_unity运行时保存场景文件

5,脚本自动定位选择Hierarchy或Project下的对象

[UnityEditor基础]脚本自动定位选择Hierarchy或Project下的对象_鳄鱼泪的博客-优快云博客

6,Unity编辑器扩展(自动保存当前打开的场景)

https://blog.youkuaiyun.com/crazyape/article/details/78803549

7,移除某个对象身上绑定的组件:

DestroyImmediate(audios[i].GetComponent<AudioListener>());

为了安全起见,我们移除的时候要添加一个参数:

对于Prefab身上的组件我们不能用DestroyImmediate(audios[i].GetComponent<AudioListener>());

方法,会报错。用DestroyImmediate(audios[i].GetComponent<AudioListener>(), true);方法

Unity动画关键帧与曲线操作 在项目开发中,动画的编辑一般由美术完成,他们会输出一个.anim的文件。 其实.anim文件实际上由一系列的关键帧组成,可以对这些关键帧进行操作,达到一定的目的。 本文章介绍实现将曲线变换形状,值偏移,时间偏移,上下、左右翻转。 目录 一、获取一个动画文件上的所有曲线 1.获得动画文件的bind属性 2.通过bind属性获得曲线 3.拿到曲线后获得关键帧 4.操作关键帧后,创建新的曲线 5.通过bind属性,将对应的新曲线应用回去,或者应用到新的动画文件,最后保存刷新资源 二、关键帧. 阅读详情

相关推荐

Unity-编辑器扩展(Editor)

1. 窗口形式 主面板 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class AudioWindowEditor : EditorWindow { static SaveData data; string path = "Assets/Resources/MainData.asset"; string text;

qq_50682713的博客 4498

Unity编辑器Unity Editor基础(一)

Unity编辑器Unity Editor基础(一) 关于 Unity 内置属性可以从到官方文档中查询,本篇文章只介绍一些常用的内置属性,如下图所示: 接下来进行项目设置,创建一个空的 Unity 工程,目前还不需要Editor文件夹; 全部代码如下: [RequireComponent(typeof(Rigidbody))] [HelpURL("h

一入編程深似海! 2万+

使用SuperTiled2Unity 将 Tiled Map Editor 导入 Unity 报错问题

一、情况说明 新的SuperTiled2Unity貌似没有自动导入的功能了,手动拖拽导入或通过Unity的Import Assert导入都会出现类似“资源丢失”的错误。 至于原因,简而言之,是使用Tiled Map Editor生成的tmx和tsx文件,会记录你绘制时使用的图像的目录,从而在导入后目录不匹配。 Tile Map Editor: 1.2.4 SuperTiled2Unity.: 1....

qq_38637916的博客 2964

Unity 编辑器(UnityEditor)

<small>前几天开始准备看源码,结合自己工作使用Lua,想看sLua框架源码,但是Lua确实不太熟悉,于是还是选择C#,打算看NGUI的源码(因为工作中也有用到)。 自己看源码经验不是很多,有些感觉无从下手,因为里面的代码跳转太多了,打算先看看NGUI的编辑器是怎么做出来的,于是开始研究Unity的编辑器制作。</small> 一、引言 Unity允许开发者自己制定编辑器,这也给了我们很多的方便,例如:要打包AssetBundle的时候,我们可以自己写一个菜单项点击进行我们所

Improve_life的博客 7156

【Unity】一键切换是否允许#if UNITY_EDITOR条件编译生效

在项目中经常会用下面的方式来让编译器环境和非编译器环境跑不同的代码,但这种方式会让非编译器环境的代码测试变得十分不方便。这种情况下可以使用#undef UNITY_EDITOR来取消宏定义。

weixin_61427881的博客 4488

Unity3D 入门:Unity Editor 编辑器常用快捷键

本文为 Unity3D 入门小伙伴整理 Unity 编辑器中的常用快捷键。 本文内容调节工具视图调节窗口调整进入退出播放模式所有快捷键 调节工具 Unity 编辑器左上角的一组按钮,正好也对应着键盘左上角的字母: 图标 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-17NMWB0q-1593478731245)(/static/posts/2020-05-05-17-29-17.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img

walterlv - 吕毅 2987

Unity编辑器功能及拓展(1) —特殊的Editor文件夹

Unity中的Editor文件夹是一个具有特殊用途的目录,主要用于存放与编辑器扩展功能相关的脚本和资源。 我们Unity中进行游戏构建时,我们经常遇到关于UnityEditor相关命名空间丢失的报错,这时候,只得将报错一条条打开,清除无用的命名空间代码片段。注:在使用vs进行Unity脚本编程时,出现手误时,vs会自动引用相关的命名空间,而有时这些多余的命名空间我们并没有使用到。回到Editor话题上,在构建时出现Editor相关命名空间丢失报错的原因是:在打包时,Unity默认不会将任何Uni

xxxxx666666的博客 1709

unity地图路径编辑器

游戏开发的过程中,我们经常需要让某些物体按照固定的路线来移动,这时候就需要提供给策划同学们一个路径编辑器来让他们充分发挥"想象力"。。。 先来看下最终效果: 下面来简单说下实现过程 制作路径点 首先制作路径点,每一个路径点要记录自己都连接到哪些点,我们用一个数组来记录,所以路径点的脚本应该是这样: public class MapWayPoint : MonoBehavi

zgjllf1011的博客 7341

Unity Editor编辑器实用扩展

目录 1.编辑器实用方法汇总 2.inspector 扩展GM工具 3.Inspector_PopList 1.编辑器实用方法汇总 //1.编辑器内物体查找(指定路径下搜索类型是scene/object.. ,并且名字中包含unity的文件) var mlist = new List<string>(AssetDatabase.FindAssets("unity......

Thewen 4881

unity编辑器中使用Gizmos绘制辅助线

前面给大家分享了一个关卡编辑器,但是我们的策划同学反映在摆道具的时候不够直观,坐标总是对不齐,于是就使用Gizmos给策划同学们写了一个辅助线的脚本,分享出来: using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif [ExecuteInEditMode] public class MapEditor : MonoBehavi

zgjllf1011的博客 7957

使用VS Code作为Unity的代码编辑器

不知道路径可以在终端输入dotnet --info即可查看(前提是你配置了环境变量)如果要使用该扩展的话还需要手动在它的扩展详情页手动设置一个颜色主题。完成对应平台的.NET下载与安装。unity调试工具(Debugger for Unity)然后设置Omnisharp.MonoPath设置成你的路径。c#脚本能看到代码提示代表配置完成。使用该工具可以提示你Unity的生命周期方法、类等。unity代码提示(Unity Snippets)控制台查看报错信息,根据报错信息修正即可。c#编程支持(c#)

cracal的博客 2935

Unity 预编译相关宏定义 (如:UNITY_EDITOR)-Unity3D判断所在平台

https://docs.unity3d.com/2019.1/Documentation/Manual/PlatformDependentCompilation.html Unity 预编译相关宏定义

iDaDi的博客 1万+

UnitySaveEditor:在Unity中查看和编辑保存文件

Unity保存编辑器 简而言之,这是Unity的编辑器脚本,可让您查看和编辑使用内置序列化模型保存到磁盘的保存游戏。 如果您的保存功能看起来像这样,那么您很幸运! BinaryFormatter bf = new BinaryFormatter(); FileStream file = File.Create(savePath); bf.Serialize(file, gameData); file.Close(); 当前,您可以查看和编辑数组,列表,字典,类,枚举和基本类型。 可以使用x控件将值重置为其默认类型值。 可以使用支持的集合上的+按钮将新项目添加到集合中,并且可以使用-控件删除集合中的值。 仍有一些功能尚未添加,因此请检查这些问题,并随时做出贡献! 安装 将资源库下载到Unity项目的Editor文件夹中。 我在这里将目录结

Unity 编辑器开发实战【Custom Editor】- AudioDatabase Editor 音频库编辑器

本文实现一个音频库的自定义编辑器,效果如图: 开始实现之前,首先简单介绍该音频库模块,音频库类Audio Database继承自Scriptable Object类,是一个可配置的资源文件: 包含的内容如下,databaseName表示该音频库的名称,outputAudioMixerGroup表示音频播放时的输出混音器组,datasets则是表示所有音频数据的列表: /// <summary> /// 音频库 /// &...

当代野生程序猿 3656

【UE5.3 Cesium for Unreal】GlobePawn编译实战:从源码修改到功能实现

本文详细介绍了在UE5.3中为Cesium for Unreal插件编译和集成非官方增强组件GlobePawn的完整实战流程。从获取源码、准备资产文件、部署项目结构,到逐行修改代码适配工程并解决典型编译错误,最终实现在三维地球上实现类似Google Earth的丝滑鼠标交互体验。

weixin_29011667的博客 307

博途 V16(TIA portal V16)安装教程

本文提供了西门子TIA Portal V16软件的完整安装指南。内容包括百度网盘下载链接(提取码kv69)、详细安装步骤(关闭安全保护、典型安装设置)、授权破解方法,以及PLCSIM仿真功能的使用说明。重点演示了S7-1200 PLC程序的仿真过程,解析了仿真界面各元素功能,包括状态指示灯(RUN/STOP/ERROR)、操作按钮(RUN/STOP/MRES)和网络接口状态等。通过图文并茂的方式,完整呈现了从软件安装到PLC程序仿真的全流程操作。

lingchen1906的博客 6707

【unity游戏开发——编辑器扩展】使用MenuItem自定义菜单栏拓展

本文主要介绍了Unity编辑器扩展的相关知识,重点讲解了如何使用MenuItem特性在Unity菜单栏、Hierarchy窗口、Project窗口和Inspector中添加自定义菜单项,并支持快捷键操作。文章还提到如何通过AddComponentMenu特性在Component菜单中添加脚本。此外,作者强调了学习过程中参考了大量资料,并整合了多位老师的教程,最终形成了自己的学习笔记。文章还解释了为何选择收费模式,并鼓励读者支持其创作。最后,作者计划在博客首页展示参考过的博主链接,以表达感谢。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 2140

海康视觉平台VisionMaster实战入门:从通讯配置到工具应用

本文为海康VisionMaster视觉平台实战入门指南,详细介绍了从软件安装准备、与PLC等外部设备的通讯配置,到匹配定位、测量、畸变矫正等核心工具的应用方法。通过实战案例拆解,帮助工程师快速掌握如何搭建完整的机器视觉检测与引导定位系统,实现工业自动化项目落地。

weixin_29224309的博客 1010

Unity使用Cursor Editor

此文章记录如何使用Cursor作为Unity的IDE工具

weixin_45836091的博客 5873

ROS-Industrial :simple_message

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录1. 概述2. 协议(Protocol)3. HEADER解析3.1 MSG_TYPE3.2 COMM_TYPE3.3 REPLY CODE 1. 概述 Simple_Message是ROS-Industrial的一部分,定义了用于与工业机器人控制器通信的简单消息连接协议。另外还包括处理连接受限系统的处理器和管理器类。 2. 协议(Protocol) 简单消息(simplemessage)协议定义了ROS的驱动层和机器人控制器之.

flsxyw的博客 780

C++游戏开发实战:从零构建局域网联机对战系统(附完整代码解析)

本文详细介绍了如何使用C++开发局域网联机对战游戏系统,涵盖从环境搭建到实战代码解析的全过程。重点讲解了Winsock库的应用、Socket通信原理、服务端与客户端开发技巧,以及性能优化和安全防护方案,帮助开发者快速掌握C++游戏开发的核心技术。

weixin_42524945的博客 445

【Unity】Unity插件:地形编辑器MTE(Mesh Terrain Editor)

MTE(Mesh Terrain Editor)是一款Unity的地形编辑器插件,该插件能够更方便的创建和编辑地图,提升场景开发效率。

一个普普通通的游戏开发者 1万+

大麦网抢票协议逆向分析:从Web安全到高并发风控实战

在当今高并发Web应用场景下,理解网络协议交互与反爬虫机制是后端开发和系统架构设计的核心技能。其基本原理涉及客户端与服务器之间通过HTTP/HTTPS协议进行数据交换,关键环节包括请求参数构造、身份认证与会话管理。这一技术的核心价值在于保障系统安全与公平性,通过动态令牌、签名算法及设备指纹等多重手段,有效抵御自动化脚本攻击。典型应用场景包括票务系统、电商秒杀等需要严格管控资源访问的高并发业务。本文以热门票务平台为例,深入剖析其**请求链与令牌体系**及**行为生物特征分析**等风控策略,通过实战演示协议逆向

weixin_34354173的博客 451

Unity Editor拓展编辑器大全

文章目录拓展Project右键菜单拓展Project布局拓展Hierarchy右键菜单拓展Hierarchy布局拓展Inspector组件 拓展Project右键菜单 [MenuItem("Assets/My Tools/Tools 1", false, 2)] static void MyTools1() { Debug.Log(Selection.activeObject.name)...

linxinfa的专栏 3539

MATLAB实战:纯电动汽车动力性仿真从入门到精通(附完整代码)

本文提供了一份基于MATLAB的纯电动汽车动力性仿真从入门到精通的实战指南。通过理论建模与代码实战相结合,详细讲解了如何构建行驶阻力模型、驱动力平衡方程,并利用MATLAB脚本完整实现了最高车速、加速时间和最大爬坡度三大核心性能指标的仿真计算。文章附有完整代码,帮助工程师快速掌握纯电动汽车动力性仿真的核心技能,为设计与优化提供高效的数字工具。

weixin_29074409的博客 369

超强干货之---Python-数据爬取(爬虫)

定义: 深度优先搜索是一种遍历或搜索树或图的算法,从起始节点开始,一直沿着一个分支走到底,再回溯到上一个节点继续搜索下一个分支,直到遍历完所有节点。特点递归:通常用递归实现,或者使用栈来模拟递归过程。内存占用低:在有大量分支的情况下,内存占用比广度优先搜索低。适合目标较深的情况:如果目标节点距离起始节点较深,DFS能更快找到目标。适用场景需要遍历所有节点的情况,如生成树、迷宫搜索。目标节点较深,且分支较多时。在大规模数据爬取时,选择合适的存储方式取决于数据的规模、结构和访问需求。

房东的猫的博客 5万+
上一篇: 多个对象池的使用
下一篇: Shader对于边缘的描绘,以及缩小和放大的时候,对于边缘的shader不受影响
MonoBehaviour
博客等级 码龄10年 22粉丝 48原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值