Unity项目较大刷新及编译卡顿解决方法

文章介绍了在Unity开发中应对大型项目延迟刷新问题的解决方案,包括禁用自动刷新、调整ScriptChangesWhilePlaying选项以及自定义资源变更检测。通过检查Scripts和Prefab目录的MD5值,以及对比SVN的Revision版本,实现快速定位并刷新变动资源,有效减少卡顿,提高开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开发中当Unity项目比较大的时候,每次Unity窗口获取焦点时刷新需要3-5秒,非常麻烦!常见有几种方法来缓解这个问题:

  •      关闭unity自动刷新Edit->Preferences->General->Auto Refresh,改为自己手动Ctrl+R刷新;

           

        缺点:每次都需要手动刷新比较麻烦

  •    修改unity的ScriptChangesWhilePlaying选项Editor->Preferences->General->Script Changes While Playing

          

        Recompile And Continue Playing 编译并继续播放

        Recompile After Finished Playing 停止播放后再编译

        Stop Playing And Recompile 停止播放进行编译

  •  开发工具在Editor模式自动检查资源变更自动刷新,代码如下:

        第一步:检查开发过程中需要及时刷新的资源,如:Scripts目录,prefab目录

        第二步:获取最后改动时间是否与上一次检查的时间一致,不一致则启动刷新

        第三步:将时间写入文件缓存,方便下一次对比

        第四步:在窗口获取焦点以及Playing启动时启动检测

2024.3.8:新增SVN的Resivion版本对比刷新功能 ,如果Resivion比缓存中版本号大则启动刷新并保存当前Resivion号;

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;

[InitializeOnLoad]
public class EditorNotification : AssetPostprocessor
{
    private static bool isFocused;
    private static bool isPlaymode;
    static EditorNotification()
    {
        EditorApplication.update -= Update;
        EditorApplication.update += Update;
        EditorApplication.playmodeStateChanged -= PlaymodeStateChanged;
        EditorApplication.playmodeStateChanged += PlaymodeStateChanged;
    }

    private static void Update()
    {
        if (isFocused == UnityEditorInternal.InternalEditorUtility.isApplicationActive)
        {
            return;
        }
        isFocused = UnityEditorInternal.InternalEditorUtility.isApplicationActive;
        OnEditorFocus(isFocused);
    }

    private static void PlaymodeStateChanged()
    {
        if(EditorApplication.isPlayingOrWillChangePlaymode && !isPlaymode)
        {
            //Debug.LogError(EditorApplication.isPlaying.ToString() + " - " + EditorApplication.isPlayingOrWillChangePlaymode.ToString());
            isPlaymode = true;
            Refresh();
        }
        else if(!EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isPlaying)
        {
            isPlaymode = false;
        }
    }
    /// <summary>
    /// Unity窗口聚焦状态改变回调
    /// </summary>
    /// <param name="focus"></param>
    private static void OnEditorFocus(bool focus)
    {
        if (focus)
        {
            //Debug.LogErrorFormat("编辑器激活状态:{0}", focus);
            if(!EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode)
            {
                Refresh();
            }
        }
    }

    private void OnPreprocessAsset()
    {
        //Debug.LogError("Asset下文件改变时回调");
    }

    //[MenuItem("File/RefreshManual")]
    static void Refresh()
    {
        bool needRefresh = false;
        MD5File md5 = new MD5File();
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Reset();
        sw.Start();
        string path = Path.Combine($"{Application.dataPath}/../Library", "md5Refresh.dat");
        if(File.Exists(path))
        {
            FileStream fs = new FileStream(path, FileMode.OpenOrCreate);
            BinaryFormatter bin = new BinaryFormatter();
            md5 = (MD5File)bin.Deserialize(fs);
            fs.Close();
        }
        
        string scriptPath = Path.Combine($"{Application.dataPath}", "Scripts");
        DirectoryInfo dinfo = new DirectoryInfo(scriptPath);
        FileInfo[] fileInfos = dinfo.GetFiles("*.cs", SearchOption.AllDirectories);
        foreach(FileInfo file in fileInfos)
        {
            string timestr = file.LastWriteTime.ToLongTimeString();
            string value = string.Empty;
            if(md5.m_DicMD5.TryGetValue(file.FullName, out value))
            {
                System.DateTime dt = System.DateTime.Parse(value);
                System.DateTime dt2 = System.DateTime.Parse(timestr);
                if(!System.DateTime.Equals(dt, dt2))
                {
                    needRefresh = true;
                    md5.m_DicMD5[file.FullName] = timestr;
                    Debug.LogError(file.FullName + " - " + value + " -- " + timestr);
                }
            }
            else
            {
                needRefresh = true;
                md5.m_DicMD5.Add(file.FullName, timestr);
                Debug.LogError(file.FullName + " -------- " + timestr);
            }
        }
        //刷新SVN版本号并对比
        bool bNeedRefreshSVN = CheckSVNRevision(md5);
        {
            FileStream fs = new FileStream(path, FileMode.OpenOrCreate);
            BinaryFormatter bin = new BinaryFormatter();
            bin.Serialize(fs, md5);
            fs.Close();
        }
        sw.Stop();
        string str;
        if (needRefresh)
        {
            str = "C#变更检查结束,需要刷新!";
            string t = (sw.Elapsed.TotalMilliseconds / 1000).ToString("N2");
            Debug.LogError($"{str}FileCount:{fileInfos.Length} Time:{t}秒");
            AssetDatabase.Refresh();
            EditorUtility.RequestScriptReload();
        }
        else if(bNeedRefreshSVN)
        {
            str = "SVN有更新,需要刷新!";
            Debug.LogError(str);
            AssetDatabase.Refresh();
            EditorUtility.RequestScriptReload();
        }
    }

    static bool CheckSVNRevision(MD5File md5)
    {
        if (md5.m_DicSVNRevision == null)
            md5.m_DicSVNRevision = new System.Collections.Generic.Dictionary<string, uint>();
        bool bret = false;
        string path = Application.dataPath;
        uint revisionID = 0;
        revisionID = GetSVNRevision(path);
        uint cacheID = 0;
        if (md5.m_DicSVNRevision.TryGetValue(path, out cacheID))
            cacheID = md5.m_DicSVNRevision[path];
        if (revisionID > 0 && revisionID > cacheID)
        {
            md5.m_DicSVNRevision[path] = revisionID;
            bret = true;
        }
        //DirectoryInfo dirInfo = new DirectoryInfo(path);
        //DirectoryInfo[] infos = dirInfo.GetDirectories("*", SearchOption.TopDirectoryOnly);
        string[] infos = new string[4];
        infos[0] = Path.Combine(path, "Art");
        infos[1] = Path.Combine(path, "Document");
        infos[2] = Path.Combine(path, "lua");
        infos[3] = Path.Combine(path, "UI");
        foreach (var info in infos)
        {
            cacheID = 0;
            path = info;
            revisionID = GetSVNRevision(path);
            if (md5.m_DicSVNRevision.TryGetValue(path, out cacheID))
                cacheID = md5.m_DicSVNRevision[path];
            if (revisionID > 0 && revisionID > cacheID)
            {
                md5.m_DicSVNRevision[path] = revisionID;
                bret = true;
            }
        }
        return bret;
    }
    /// <summary>
    /// 获取svn版本revision号
    /// </summary>
    /// <param name="path">svn路径</param>
    /// <returns>uint revision号</returns>
    static uint GetSVNRevision(string path = "")
    {
        var startInfo = new System.Diagnostics.ProcessStartInfo("svn", "info " + path)
        {
            UseShellExecute = false,
            RedirectStandardOutput = true,
            CreateNoWindow = true
        };

        using (var process = System.Diagnostics.Process.Start(startInfo))
        {
            using (var reader = process.StandardOutput)
            {
                // 读取命令的输出直到结束
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    if(line.StartsWith("Last Changed Rev"))
                    {
                        int index = line.IndexOf(":");
                        string strID = line.Substring(index + 1);
                        uint revisionID = 0;
                        if(uint.TryParse(strID, out revisionID))
                        {
                            //Debug.LogError(revisionID.ToString() + " - " + path);
                            return revisionID;
                        }
                    }
                }
            }
        }
        return 0;
    }
}
#endif

using System.Collections;
using System.Collections.Generic;

[System.Serializable]
public class MD5File
{
    public Dictionary<string, string> m_DicMD5 = new Dictionary<string, string>();
    public Dictionary<string, uint> m_DicSVNRevision = new Dictionary<string, uint>();
}

 这种方法只需要检查少部分资源自动刷新,基本没有卡顿,体验相对比较好!

### 关于 Unity 中 AVPro Live Camera 插件的常见问题与解决方案 #### 1. **插件安装与配置** 在使用 AVPro Live Camera 插件前,需确认已正确导入并完成基本配置。如果未正确设置,可能会导致运行时错误或功能异常。例如,“Component GUI Layer in Main Camera for Scene Assets/main.unity is no longer available” 的提示通常表明场景中的某些组件被移除或丢失[^1]。 为了防止此类问题发生,建议执行以下操作: - 确认项目中是否存在 `Main Camera` 并检查其关联的脚本和组件。 - 如果缺失必要组件,则重新添加对应的 `GUI Layer` 或其他依赖项。 ```csharp using UnityEngine; public class CheckCameraComponents : MonoBehaviour { void Start() { Camera mainCam = Camera.main; if (mainCam != null && !mainCam.GetComponent<GUICameraLayer>()) { Debug.LogError("Missing GUICameraLayer component on the Main Camera."); } } } ``` --- #### 2. **动态调整摄像机视图** 对于涉及多平台适配的情况,可以参考动态调整正交摄像机对焦距离的方法[^2]。这种方法通过计算屏幕分辨率的比例因子来适应不同设备上的显示需求: ```csharp using UnityEngine; public class AdjustOrthographicSize : MonoBehaviour { public float baseScreenHeight = 768f; // 设计稿高度 private Camera cam; void Awake() { cam = GetComponent<Camera>(); if (cam != null) { float screenHeightRatio = Screen.height / baseScreenHeight; cam.orthographicSize = 5 * screenHeightRatio; // 自定义缩放比例 } } } ``` 此方法适用于需要精确控制画面范围的应用程序,尤其是当目标设备具有不同的宽高比时。 --- #### 3. **解决与其他 SDK 的冲突** 当项目中引入多个第三方库(如 EasyAR 和 AVPro/UMP),可能出现编译失败的问题[^3]。这类错误通常是由于 Java SDK 版本不一致引起的。以下是几种常见的处理方式: - 统一所有使用的 Android SDK 工具链版本; - 修改 Gradle 构建文件以排除重复依赖项; - 手动替换冲突的 `.jar` 文件或将特定模块打包到单独的 AAR 中。 示例:Gradle 配置修改 ```gradle android { configurations.all { resolutionStrategy.eachDependency { DependencyResolveDetails details -> if (details.requested.group == 'com.android.support') { details.useVersion "28.0.0" // 强制指定兼容版本 } } } } dependencies { implementation(name: 'avpro_live_camera', version: 'latest') implementation(name: 'easyar', version: 'compatible_version') } ``` --- #### 4. **性能优化技巧** AVPro Live Camera 提供了多种参数用于提升渲染效率。合理利用这些选项可以帮助减少卡顿现象以及降低功耗: - 启用硬件加速模式; - 设置合适的帧率上限; - 减少不必要的纹理采样次数。 具体实现如下所示: ```csharp using Visus.AVProLiveCamera; public class OptimizePerformance : MonoBehaviour { private AVProLiveCameraDevice device; void OnEnable() { device = AVProLiveCameraManager.Instance.GetFirstAvailableDevice(); if (device != null) { device.SetFrameRateLimit(30); // 控制最大刷新频率 device.EnableHardwareAcceleration(true); } } void OnDestroy() { if (device != null) { device.Release(); // 清理资源 } } } ``` --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值