zoukankan      html  css  js  c++  java
  • UNITY编辑器模式下static变量的坑

    在unity中写编辑器扩展工具,如在编辑器中加个菜单,点击这个菜单项时执行打包功能。

    类如下,其中的静态变量,如果每次进来不清空,则LIST会越来越大,打包函数执行完后系统不会帮我们清空

    #if UNITY_EDITOR
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using UnityEditor;
    
    using UnityEngine;
    using LitJson;
    using System.Security.Cryptography;
    
    //资源清单,列出了游戏中用到的所有资源
    [Serializable]
    public class AssetsList
    {
        /*** 资源清单
         * <相对路径, MD5串>
         */
        public Dictionary<string, string> Assets2Md5 = new Dictionary<string, string>();//容量未设置,后面可以写成常量,根据上次数据得出
    }
    
    public class AssetBundleBuilder
    {
        public static string BundlePath = "";
        public static AssetsList AssetsList = new AssetsList();
        public static List<string> DependAssets_PathList = new List<string>(8000); //InGameRes所依赖的ArtSource等文件夹下的资源
        public static List<string> Assets_PathList = new List<string>(8000); //InGameRes文件夹下的资源
        public static List<AssetBundleBuild>  BuildList = new List<AssetBundleBuild>(18000); //估计值,后面可以写成常量,根据上次数据得出
    
        public static string ResRootPath = "Assets/Resources"; //资源目录,根据此目录查找所有依赖的资源
        public static string ResDependPath = "Assets/ArtSource"; //资源目录,根据此目录查找所有依赖的资源
        public static string ResFolder = "Resources"; //资源打包的依据文件夹,此文件夹下的所有文件及所有依赖文件都被打进AB包,不要在此文件夹下放备份或无用的资源
    
        public static string Depends_List_File = "Assets/depends_list.json";
        public static string Assets_List_File = "Assets/assets_list.json";
    
        public static int FileNumLimit = 10; //测试使用,只收集限定数量的文件
        public static void BuildAB(BuildTarget buildTarget)
        {
            if (!Directory.Exists(ResRootPath))
            {
                Debug.LogError("资源目录不存在:" + ResRootPath);
                return;
            }
    
            var files = Directory.GetFiles(ResRootPath, "*", SearchOption.AllDirectories);
    
            var stopwt = System.Diagnostics.Stopwatch.StartNew();
            var t1 = stopwt.ElapsedMilliseconds;
    
            /***
             * 坑,这里必须要清空,否则每次进来就会往Assets_PathList里添加一次内容
             */
            BuildList.Clear();
            Assets_PathList.Clear();
            DependAssets_PathList.Clear();
            AssetsList.Assets2Md5.Clear();
    
            var infoTitle = "收集文件,目录 : " + ResRootPath;
            //第一步,将ResRootPath目录下的文件收集起来,此目录的东西都是游戏中要用到的,此目录之外的东西不一定会用到
            var idx = 0;
            for (int i=0, cnt=files.Length; i<cnt; ++i)
            {
                var item = files[i];
                var ext = Path.GetExtension(item);
                if (string.Compare(ext, ".meta", true)==0 || string.Compare(ext, ".cs", true)==0)
                    continue;
    
                if ((string.Compare(ext, ".prefab", true) == 0 || string.Compare(ext, ".mat", true) == 0
                    || string.Compare(ext, ".unity", true) == 0 || string.Compare(ext, ".controller", true) == 0))
                {
                    idx++;
                    if (idx > FileNumLimit)
                        break;
                    //添加到构建列表
                    var abName = AddToBuildList(item);
                    Assets_PathList.Add(abName);
                }
    
                EditorUtility.DisplayProgressBar(infoTitle, item, i * 1.0f / cnt);
            }
    
            WriteToJson(Assets_List_File, Assets_PathList);
    
    
            //第二步,将ResRootPath依赖的文件收集进来
            CheckAllDepends(files);
    
            var infoTitle2 = "收集依赖,目录 : " + ResDependPath;
    
            for (int i = 0, cnt = DependAssets_PathList.Count; i < cnt; ++i)
            {
                var item = DependAssets_PathList[i];
    
                //添加到构建列表
                AddToBuildList(item);
    
                EditorUtility.DisplayProgressBar(infoTitle2, item, i * 1.0f / cnt);
            }
    
            //第三步,增量打包
            var targetName = buildTarget == BuildTarget.iOS ? "IOS" : "Android";
            BundlePath = Application.dataPath.Substring(0, Application.dataPath.Length-6) + "Bundles" + targetName; //放到Assets同目录
            
            if (!Directory.Exists(BundlePath))
            {
                Directory.CreateDirectory(BundlePath);
            }
    
            var dt1 = stopwt.ElapsedMilliseconds - t1;
    
            ClearUnusedBundles(files);
            /***
             * 一次传入所有需要打包的数据,UNITY会保证它们之间的依赖被正确处理:先打被依赖的包,再打自己
             * 我们必须保证传入数据的完全,如果被依赖的包不在我们传入的列表中,则相应资源会被打到所有用到该包的包中,造成资源的重复包含
             */
            AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(BundlePath, BuildList.ToArray(), BuildAssetBundleOptions.ChunkBasedCompression, buildTarget);
            EditorUtility.ClearProgressBar();
    
            var dt2 = stopwt.ElapsedMilliseconds - t1 - dt1;
    
            EditorUtility.DisplayDialog("打包完成", "耗时:" + dt1 / 1000.0f + " + " + dt2/1000.0f + "
    路径:" + BundlePath, "ok");
    
            GenResList(manifest);
    
            EditorUtility.UnloadUnusedAssetsImmediate();
    
        }
    
        public static string AddToBuildList(string resPath)
        {
            var relativeDir = resPath;
    
            //不能这样处理,因为可能有同名却不同后缀的文件存在
            //relativeDir = relativeDir.Substring(0, relativeDir.LastIndexOf(".")); //去除后缀
            relativeDir = relativeDir.Replace("\", "/");
            var abBuild = new AssetBundleBuild();
            abBuild.assetBundleName = relativeDir;
            abBuild.assetNames = new string[] { resPath };
            abBuild.assetBundleVariant = "ab";
            BuildList.Add(abBuild);
    
            return relativeDir;
        }
    
        //将AB包移动到StreamingAssets目录下,为打APK或IPA作准备
        public static void CopyToStreamingAssets(BuildTarget buildTarget)
        {
            CopyFolder(BundlePath, Application.streamingAssetsPath + "/Bundles/");
        }
    
        /***
         * 增量打包的关键,遍历上次打出的包,与本次【构建列表BuildList】里的内容对比,不在列表中的删除掉。
         * 在列表中的保留不变。这样当我们再次执行打包操作时,如果文件对应的AB包已存在且文件没有更改,则不用重新打包
         */
        public static void ClearUnusedBundles(string[] files)
        {
            if (!Directory.Exists(BundlePath))
            {
                Debug.LogError("包目录不存在:" + BundlePath);
                return;
            }
    
            var abFiles = Directory.GetFiles(BundlePath + "/assets", "*", SearchOption.AllDirectories);
            var delList = new List<string>();
    
            for (int i=0, n=abFiles.Length; i<n; ++i)
            {
                var ext = Path.GetExtension(abFiles[i]);
                var file = Path.ChangeExtension(abFiles[i], null);
                if(string.Compare(ext, ".manifest", true) == 0)
                {
                    file = Path.ChangeExtension(file, null);
                }
    
                file = file.Substring(file.IndexOf("assets", StringComparison.CurrentCultureIgnoreCase));
                if (!File.Exists(file))
                {
                    delList.Add(abFiles[i]);
                }
    
                EditorUtility.DisplayProgressBar("正在检查AssetBundle", file, i * 1.0f / n);
    
            }
    
            for (int i=0, n= delList.Count; i<n; ++i)
            {
                EditorUtility.DisplayProgressBar("正在清理无用的AssetBundle", delList[i], i * 1.0f / n);
                if (!File.Exists(delList[i]))
                {
                    EditorUtility.DisplayDialog("", "不存在此文件", "ok");
                    continue;
                }
    
                File.Delete(delList[i]);
    
            }
    
            EditorUtility.ClearProgressBar();
    
            WriteToJson("Assets/delete_list.json", delList);
        }
    
        //剪切到某个目录,并重命名
        public static void MoveFolder(string src, string dest)
        {
            if (Directory.Exists(dest))
            {
                Directory.Delete(dest, true);
            }
            Directory.CreateDirectory(dest);
    
            File.Move(src, dest + "/Bundles");
        }
    
        //复制到某个目录,并重命名
        public static void CopyFolder(string src, string dest)
        {
            if (Directory.Exists(dest))
            {
                Directory.Delete(dest, true);
            }
            Directory.CreateDirectory(dest);
    
            foreach (var sub in Directory.GetDirectories(src))
            {
                CopyFolder(sub + "/", dest + Path.GetFileName(sub) + "/");
            }
    
            foreach (var file in Directory.GetFiles(src))
            {
                File.Copy(file, dest + Path.GetFileName(file));
            }
        }
    
        /*** 生成资源清单
         * 格式:MD5 : 相对于/Bundles/的路径
         * 作用:热更的对比依据,通过MD5对比,确定文件的增-删-修改
         */
        public static void GenResList(AssetBundleManifest manifest)
        {
            AssetsList.Assets2Md5.Clear();
    
            var abs = manifest.GetAllAssetBundles();
            foreach (var ab in abs)
            {
                var hashes = manifest.GetAssetBundleHash(ab);
                AssetsList.Assets2Md5.Add(ab, hashes.ToString());
            }
    
            var writer = new JsonWriter();
            writer.PrettyPrint = true;
            JsonMapper.ToJson(AssetsList, writer);
            File.WriteAllText(BundlePath + "/asset_list.json", writer.ToString(), System.Text.Encoding.ASCII);
    
        }
    
        public static void DeleteBundles(BuildTarget target)
        {
            var path = Application.dataPath + "/../" + (target == BuildTarget.Android ? "BundlesAndroid" : "BundlesIOS");
            var msg = "文件夹不存在" + " " + path;
            if (Directory.Exists(path))
            {
                Directory.Delete(path, true);
                msg = "清理完成";
            }
    
            EditorUtility.DisplayDialog("清理", msg, "ok");
        }
    
        //得到指定文件夹下资源的所有依赖
        public static void CheckAllDepends(string targetPath)
        {
            if (!Directory.Exists(targetPath))
            {
                Debug.LogError("资源目录不存在:" + targetPath);
                return;
            }
    
            var files = Directory.GetFiles(targetPath, "*", SearchOption.AllDirectories);
    
            CheckAllDepends(files);
        }
    
        //得到指定文件的所有依赖
        public static void CheckAllDepends(string[] files)
        {
            var stopWatch = System.Diagnostics.Stopwatch.StartNew();
            var startTime = stopWatch.ElapsedMilliseconds;
    
            DependAssets_PathList.Clear();
    
            var idx = 0;
            for(int i=0, fileNum = files.Length; i< fileNum; ++i)
            {
                var item = files[i];
                var ext = Path.GetExtension(item);
    
                if (string.Compare(ext, ".meta", true) == 0 || string.Compare(ext, ".cs",true) == 0)
                    continue;
                if ((string.Compare(ext, ".prefab", true) == 0 || string.Compare(ext, ".mat", true) == 0
                    || string.Compare(ext, ".unity", true) == 0 || string.Compare(ext, ".controller", true) == 0))
                {
                    idx++;
                    if (idx > FileNumLimit)
                        break;
    
                    var dpends = AssetDatabase.GetDependencies(item, true);
                    foreach (var s in dpends)
                    {
                        if (s.Contains(ResFolder) || s.Contains(".cs"))
                            continue;
                        //var substr = s.Substring(s.IndexOf("Assets/"));
                        if (!DependAssets_PathList.Contains(s))
                        {
                            DependAssets_PathList.Add(s);
                        }
                    }
                }
    
                var progress = i *1.0f/ fileNum;
                EditorUtility.DisplayProgressBar("正在查找引用" + fileNum, item, progress);
            }
    
            EditorUtility.ClearProgressBar();
    
            //var savePath = "Assets/refs_list.json";
            WriteToJson(Depends_List_File, DependAssets_PathList);
    
            var deltaTime = stopWatch.ElapsedMilliseconds - startTime;
    
            EditorUtility.DisplayDialog("检查完成", "耗时" + deltaTime/1000.0f + "
    保存路径: " + Depends_List_File, "ok");
        }
    
        public static void WriteToJson(string path, object obj)
        {
            var writer = new JsonWriter();
            writer.PrettyPrint = true;
            JsonMapper.ToJson(obj, writer);
            File.WriteAllText(path, writer.ToString());
        }
    
        [MenuItem("AssetBundle/安卓/构建AB包")]
        public static void BuildAndroidAB()
        {
            BuildAB(BuildTarget.Android);
        }
    
        [MenuItem("AssetBundle/IOS/构建AB包")]
        public static void BuildIOSAB()
        {
            BuildAB(BuildTarget.iOS);
        }
    
        [MenuItem("AssetBundle/安卓一键打包")]
        public static void AndroidOneKeyBuild()
        {
            BuildAB(BuildTarget.Android);
            CopyToStreamingAssets(BuildTarget.Android);
        }
    
        [MenuItem("AssetBundle/IOS一键打包")]
        public static void IOSOneKeyBuild()
        {
            BuildAB(BuildTarget.iOS);
            CopyToStreamingAssets(BuildTarget.iOS);
        }
    
        [MenuItem("AssetBundle/安卓/拷到StreamingAssets目录")]
        public static void CopyAndroidABToStreamingAssets()
        {
            CopyToStreamingAssets(BuildTarget.Android);
        }
    
        [MenuItem("AssetBundle/IOS/拷到StreamingAssets目录")]
        public static void CopyIOSABToStreamingAssets()
        {
            CopyToStreamingAssets(BuildTarget.iOS);
        }
    
        [MenuItem("AssetBundle/清理/清理安卓包")]
        public static void DeleteAndroidABs()
        {
            DeleteBundles(BuildTarget.Android);
        }
    
        [MenuItem("AssetBundle/清理/清理苹果包")]
        public static void DeleteIOSABs()
        {
            DeleteBundles(BuildTarget.iOS);
        }
    
        [MenuItem("AssetBundle/资源检查/检查依赖")]
        public static void CheckAssetsDepends()
        {
            CheckAllDepends(ResRootPath);
        }
    }
    
    #endif
  • 相关阅读:
    分治fft
    DSU on tree
    最小矩形覆盖
    用gallery展示图片,实现中间图片稍大,两边较小的效果
    动态刷新listview中的数据
    比较两个文件是否一样
    安卓巴士Android开发神贴整理
    Failed to register input channel. Check logs for details.
    解决scrollview嵌套ImageView时,出现除顶部和底部黑块的问题
    获取系统图片,并放入ImageList
  • 原文地址:https://www.cnblogs.com/timeObjserver/p/11024148.html
Copyright © 2011-2022 走看看