Unity 中实用方法的记录

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jingangxin666/article/details/80787133

获取Assets文件夹同级的目录

我们要读取Assets目录同级的文件夹Assets.diff, 该文件夹的全路径可以这样获得:

//方法一
DirectoryInfo topDir = Directory.GetParent(Application.dataPath);
string diffFilePath = topDir.FullName + "/" + "Assets.diff";

//方法二
string diffFilePath = GetParentDirectory(Application.dataPath, 1) + "/" + "Assets.diff";

其中diffFilePath即为全路径

GetParentDirectory方法可以获得某路径的n级父目录

    public static string GetParentDirectory(string path, int parentCount)
    {
        int index = path.Length;
        while (parentCount > 0 && index > 0)
        {
            index = path.LastIndexOf('/', index - 1, StringComparison.Ordinal);
            --parentCount;
        }
        if (index < 0){
            return null;
        }
        return path.Substring(0, index);
    }

使用Editor进程条

当我们执行时间比较久的进程时(例如遍历Assets目录下所有FBX资源),可以使用EditorUtility 中的进度条来展示进程执行到哪一步了.

具体代码记录如下:

//展示进度条
EditorUtility.DisplayProgressBar("正在检查", "正在获取文件列表...", 0.0f);

var info = string.Format("{0}/{1}", tempCount, allCount);
EditorUtility.DisplayProgressBar("正在检查", "已检查" + info, (float)tempCount / allCount);

//最后一定要记得清理掉进度条, 不然它就一直留在界面上啦
EditorUtility.ClearProgressBar();

在编辑器的MenuBar添加按钮

    [MenuItem("Art Tools/Mesh检查/1.检查fbx的mesh. 检查类型:【UVs & Colors】")]
    static void CheckMesh()
    {
        _CheckMesh();
    }

    private static void _CheckMesh(){
        //do something

关于地址路径的问题

全路径和绝对路径

    //searchPath 是绝对路径
    var searchPath = Application.dataPath;

    //获取指定类型的资源
    //得到的是相对Assets目录下的路径
    //例如"Assets\3rd\AniEditorScene\Scene0.prefab"
    public static string[] GetAllFilesNew(string dir, string searchPattern)
    {
        var allFile = Directory.GetFiles(dir, searchPattern, SearchOption.AllDirectories);
        return allFile;
    }


//需要相对路径
//根据相对路径加载指定类型的物体
var tempModel = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);

斜杠转化为反斜杠

//originPath为win平台产生的路径, 例如Assets\Source\Test.fbx
var assetPath = originPath.Replace("\\", "/");

修改路径的后缀

//fullPath为需要修改的路径, 如Assets/Prefab/Effect/Maps/Dooda.prefab

//修改为".fbx"后缀, 注意要有一个"."
var newPtah1 = Path.ChangeExtension(fullPath, ".fbx");

//去除路径的后缀, 不保留".", 传入null
var newPtah2 = Path.ChangeExtension(fullPath, null);

//去除路径的后缀, 保留".", 传入空字符
var newPtah3 = Path.ChangeExtension(fullPath, "");

参考:

如果需要用到路径的部分内容, 如”Assets/Prefab/Effect/Maps/Dooda.prefab”只需要”Effect/Maps/Dooda”, 可以利用string.Substing和Path.changeExtension来处理

var path = Path.ChangeExtension(pullPath.Substring("Assets/Prefab/".Length), null);

将日志打印指定文件

项目中需要查找一个不定时出现的bug, 加断点调试不大实际. 我就在可疑的地方加入调试代码

需要注意的是调试代码尽量不要影响原代码的结构, 不要为了访问某些变量就直接将private改为public, 或者加个static

可以利用编译宏#region来区分调试代码

#if RESOURCE_DEBUG
    TrackSource(m_asset.name, "AssetHandle.Release",
        string.Format("instanceID={0}", m_asset.GetInstanceID()));
#endif

#region test
    public static void TrackSource(string path, string title, string format, params object[] values)
    {
        foreach (var trackName in TrackNames)
        {
            if (path.Contains(trackName))
            {
                LogToFile(false, "[{2}][{0} ↓↓↓ ] found {1}", title, path, trackName);
                LogToFile(false, format, values);
                break;
            }
        }
    }
#endregion

如果符合跟踪条件, 就输出堆栈信息到一个文件里面, 出现问题了就直接看log文件, 定位到出现问题的可疑代码.


 private static string _logFilePath;
 private static bool _isInit = false;

 private static void InitThings()
 {
     _logFilePath = Directory.GetParent(Application.dataPath).FullName + "/" + string.Format("{0:yyyy_MM_dd_HH_mm_ss}Log.txt", DateTime.Now);
     File.Create(_logFilePath).Dispose();
 }
 public static void LogToFile( bool isStackTrace, string format, params object[] values)
 {
     if (!_isInit)
     {
         InitThings();
         _isInit = true;
     }
     var writer = new StreamWriter(_logFilePath, true);

     writer.WriteLine(format, values);
     if (isStackTrace)
     {
         var value = new StackTrace().ToString();
         writer.WriteLine(value);
     }

     writer.Flush();
     writer.Close();
 }

参考:

云风的BLOG
程序总是由一段段顺序执行的小片代码段辅以分支结构构成。顺序执行的代码段是很稳定的,它的代码段入口的输入状态决定了输出结果。我们关心的是输入状态是什么,多半可以跳过过程,直接看结果。因为这样一段代码无论多长,都有唯一的执行流程。而分支结构的存在会让执行流依据不同的中间状态做不同的数据处理。考虑代码的正确性时,所有的分支点都需要考虑。是什么条件导致代码会走向这条分支,什么条件导致代码走向那条分支。可以说分支的多少决定了代码的复杂度。现在比较主流的衡量代码复杂度的方法 McCabe 代码复杂度大致就是这样。

使用计时器

用于计算某个方法执行所使用的时间

System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();

stopwatch.Start();

//do something 

stopwatch.Stop();

Debug.LogFormat("do something took {0}ms", stopwatch.ElapsedMilliseconds);

stopwatch.Reset();

stopwatch.Start();

//do something 

stopwatch.Stop();

获取对资源的引用

Unity提供了一个API: AssetDatabase.GetDependencies , 用于获得资源所依赖的文件, 该方法参数recursive 默认为true, 但是此时对时间的影响很大

做了个测试, 对Assets目录下的所有prefab查找引用, 参数recursive 分别为truefalse, 结果如下图:

这里写图片描述

速度快了几乎15倍, 因此参数recursive 最好为false, 即查找直接引用即可.

如果想要找到某资源直接或间接引用的某个类型的资源, 可以参数为false, 再利用其它方式进行补充

为什么要这么折腾呢, 时间优化很重要啊

项目中为了查找Assets目录所有prefab所引用的animation clip文件, 方案有两个:

①使用GetDependencies(prefabPath, true),

  • 过滤".anim"后缀,
  • ".anim"列表即为所有引用到的animation clip文件

②使用GetDependencies(prefabPath, false),

  • 过滤".anim",".controller", ".overrideController"后缀文件,
  • ".overrideController"文件进行GetDependencies(path, false)
  • 过滤".anim", ".controller"后缀文件
  • 对所有 ".controller"文件进行GetDependencies(path, false)
  • 过滤".anim"后缀文件
  • 多次过滤得到的".anim"列表即为所有引用到的animation clip文件

经实测, 方法②和①获得的animation clip文件一致, 虽然②看起来比较折腾, 但是时间快了好几倍╮(╯▽╰)╭

更多:

使用CompareTag而不是显式字符串比较

gameObject.tag属性返回一个字符串值, 可用于标识一个或多个游戏对象. 但是, 此属性在每次访问时都会导致托管堆的分配, 因为字符串从Unity原生对象复制到托管C#字符串。

我们可以使用gameObject.CompareTag("Tank")代替gameObject.tag == "Tank"

更多:

使用foreach会造成额外的GC开销吗

这里写图片描述
其实就是一个Unity中所带的老版本mono编译器的一个bug, 在finally里,mono编译出来的代码中有一次将valuetype的Enumerator,boxing的过程

Unity5.3.5p8出了个升级mono编译器的版本,消除了这部分GC,具体可参考:C# Compiler - Upgraded C# Compiler on 5.3.5p8

更多阅读:

猜你喜欢

转载自blog.csdn.net/jingangxin666/article/details/80787133