Unity引擎Profiling工具使用技术总结

一、性能分析概述

Unity的性能分析(Profiling)是游戏优化过程中不可或缺的环节,它帮助开发者识别性能瓶颈并提供解决方案。Unity提供了多种性能分析工具,适用于不同的分析场景和平台。

主要性能分析工具

  1. Unity Profiler - 内置的主要性能分析工具
  2. Frame Debugger - 分析渲染调用和绘制顺序
  3. Memory Profiler - 详细的内存使用分析
  4. Profile Analyzer - 多帧性能对比分析
  5. Physics Debugger - 物理系统可视化调试
  6. GPU Profiler - GPU性能分析

二、Unity Profiler基础

开启Profiler

// 在代码中控制Profiler
using UnityEngine.Profiling;

void Start()
{
    // 开启Profiler记录
    Profiler.enabled = true;
    
    // 设置是否深度采样
    Profiler.deepProfiling = true;
    
    // 设置Profiler是否在后台运行
    Profiler.enableBinaryLog = true;
    Profiler.logFile = "myLog.raw";
}

基本界面组成

  1. 工具栏 - 控制记录、连接目标等
  2. 模块视图 - CPU、GPU、内存、音频等不同模块
  3. 时间线视图 - 按时间轴展示性能数据
  4. 详情视图 - 显示所选时刻的详细信息
  5. 层级视图 - 函数调用堆栈和耗时占比

常见模块分析

  1. CPU Usage - 处理器使用情况
  2. GPU Usage - 图形处理器使用情况
  3. Memory - 内存分配与使用
  4. Audio - 音频系统性能
  5. Physics - 物理系统计算耗时
  6. Rendering - 渲染管线性能
  7. UI - 用户界面性能
  8. Network - 网络传输性能

三、自定义性能分析

使用Profiler Markers手动标记

using Unity.Profiling;
using UnityEngine;

public class CustomProfilerExample : MonoBehaviour
{
    // 创建持久化的Profiler标记
    private static readonly ProfilerMarker s_SimulationMarker = 
        new ProfilerMarker("MyGame.CharacterController.Simulation");
    
    private static readonly ProfilerMarker s_PhysicsMarker = 
        new ProfilerMarker("MyGame.CharacterController.Physics");
    
    void Update()
    {
        // 使用标记包裹代码块
        s_SimulationMarker.Begin();
        
        // 进行角色模拟计算
        SimulateCharacter();
        
        s_SimulationMarker.End();
    }
    
    void SimulateCharacter()
    {
        // 一些计算...
        
        // 嵌套标记示例
        s_PhysicsMarker.Begin();
        
        // 物理相关计算...
        for (int i = 0; i < 1000; i++)
        {
            float calculation = Mathf.Sin(i) * Mathf.Cos(i);
        }
        
        s_PhysicsMarker.End();
    }
}

使用代码计时器

using System.Diagnostics;
using UnityEngine;
using Debug = UnityEngine.Debug;

public class TimerExample : MonoBehaviour
{
    private Stopwatch stopwatch = new Stopwatch();
    
    void HeavyMethod()
    {
        stopwatch.Reset();
        stopwatch.Start();
        
        // 执行需要计时的代码
        for (int i = 0; i < 100000; i++)
        {
            float result = Mathf.Sqrt(i) * Mathf.Log10(i + 1);
        }
        
        stopwatch.Stop();
        Debug.Log($"Method execution time: {stopwatch.ElapsedMilliseconds}ms");
    }
}

使用Profiler.BeginSample/EndSample

using UnityEngine;
using UnityEngine.Profiling;

public class ProfilerSampleExample : MonoBehaviour
{
    void Update()
    {
        // 开始一个分析样本
        Profiler.BeginSample("Custom Update Logic");
        
        // 执行代码...
        for (int i = 0; i < 1000; i++)
        {
            float calculation = Mathf.Pow(i, 0.5f);
        }
        
        // 结束分析样本
        Profiler.EndSample();
        
        // 嵌套样本
        Profiler.BeginSample("AI Processing");
        
        // AI相关计算...
        
        Profiler.BeginSample("Pathfinding");
        // 寻路相关代码...
        Profiler.EndSample();
        
        Profiler.EndSample();
    }
}

四、远程Profiling技术

设置远程Profiling

// 在Build Settings中:
// 1. 勾选 "Development Build"
// 2. 勾选 "Autoconnect Profiler"
// 3. 设置 "Profiling Functor"

// 在代码中设置
#if ENABLE_PROFILER
using UnityEngine.Profiling;

void Start()
{
    // 设置详细的性能数据收集
    Profiler.maxUsedMemory = 512 * 1024 * 1024; // 512MB缓冲区
    Profiler.enabled = true;
}
#endif

使用ADB连接Android设备

# 列出连接的设备
adb devices

# 转发Profiler端口
adb forward tcp:34999 tcp:34999

# 启用USB调试并确保开发者选项已开启

使用Wi-Fi连接设备

// 在设备上启动的应用程序中
void Start()
{
    Debug.Log("Device IP: " + Network.player.ipAddress);
    // 记下IP地址,在Unity Profiler中填入"<IP>:34999"进行连接
}

五、Memory Profiler详解

开启内存分析器

// 安装Memory Profiler包
// 通过Package Manager -> Unity Registry -> Memory Profiler

// 启动Memory Profiler
// Window -> Analysis -> Memory Profiler

内存快照与比较

// 在代码中拍摄内存快照
using Unity.MemoryProfiler;

void CaptureMemorySnapshot()
{
    string snapshotPath = "MemoryCapture_" + System.DateTime.Now.ToString("yyyyMMdd_HHmmss");
    MemoryProfiler.TakeSnapshot(snapshotPath, OnSnapshotComplete);
}

void OnSnapshotComplete(string snapshotPath, bool success)
{
    if (success)
    {
        Debug.Log("Memory snapshot saved to: " + snapshotPath);
    }
    else
    {
        Debug.LogError("Failed to take memory snapshot");
    }
}

分析内存泄漏

public class MemoryLeakExample : MonoBehaviour
{
    private List<Texture2D> leakingTextures = new List<Texture2D>();
    
    // 有问题的方法 - 创建但从不释放纹理
    void CreateTexturesWithoutCleanup()
    {
        for (int i = 0; i < 10; i++)
        {
            Texture2D texture = new Texture2D(1024, 1024, TextureFormat.RGBA32, false);
            leakingTextures.Add(texture);
        }
    }
    
    // 修复的方法
    void CleanupTextures()
    {
        foreach (Texture2D texture in leakingTextures)
        {
            Destroy(texture);
        }
        leakingTextures.Clear();
    }
    
    void OnDestroy()
    {
        // 在此清理资源可避免内存泄漏
        CleanupTextures();
    }
}

六、Frame Debugger实战

启用Frame Debugger

// 打开Frame Debugger
// Window -> Analysis -> Frame Debugger

// 或在代码中使用
#if UNITY_EDITOR
using UnityEditor;

void TriggerFrameDebugger()
{
    FrameDebuggerUtility.EnableFrameDebugger(true);
}
#endif

分析渲染调用

// 创建自定义绘制事件进行标记
using UnityEngine.Rendering;

void OnRenderObject()
{
    // 用于在Frame Debugger中标识这个绘制块
    CommandBuffer cmd = new CommandBuffer();
    cmd.name = "MyCustomRenderingPass";
    
    // 添加一些绘制命令...
    
    // 执行命令
    Graphics.ExecuteCommandBuffer(cmd);
}

优化DrawCall

// 常见的DrawCall优化技术
public class DrawCallOptimizer : MonoBehaviour
{
    void OptimizeMeshRenderers()
    {
        // 1. 静态批处理
        // 将相关静态对象标记为Static
        
        // 2. 动态批处理 - 确保符合条件:
        // - 少于900个顶点 (300 for skinned mesh)
        // - 使用相同材质
        // - 不使用光照贴图
        
        // 3. GPU实例化
        MaterialPropertyBlock props = new MaterialPropertyBlock();
        MeshRenderer[] renderers = GetComponentsInChildren<MeshRenderer>();
        
        foreach (MeshRenderer renderer in renderers)
        {
            props.SetColor("_Color", Random.ColorHSV());
            renderer.SetPropertyBlock(props);
        }
    }
}

七、Profile Analyzer深度分析

捕获与比较帧数据

// 通过Package Manager安装Profile Analyzer

// 使用代码触发帧捕获
#if UNITY_EDITOR
using Unity.ProfileAnalyzer;

void CaptureFrames()
{
    ProfileAnalyzer analyzer = ProfileAnalyzer.CreateInstance();
    
    // 捕获100帧数据
    analyzer.SetFrameRange(100, 200);
    ProfilerFrameDataIterator frameData = analyzer.GetFrameDataIterator();
    
    // 处理帧数据...
}
#endif

分析热点函数

// 在Profile Analyzer中识别热点函数后优化示例

// 优化前
void InefficiencyMethod()
{
    for (int i = 0; i < entities.Count; i++)
    {
        for (int j = 0; j < entities.Count; j++)
        {
            CalculateDistanceBetween(entities[i], entities[j]);
        }
    }
}

// 优化后
void ImprovedMethod()
{
    // 预先计算距离
    if (needsDistanceUpdate)
    {
        CalculateAllDistances();
        needsDistanceUpdate = false;
    }
    
    // 直接使用缓存结果
    for (int i = 0; i < entities.Count; i++)
    {
        for (int j = 0; j < entities.Count; j++)
        {
            float distance = distanceCache[i, j];
            // 使用缓存距离...
        }
    }
}

八、GPU Profiler深入解析

启用GPU Profiling

// 1. 在Profiler窗口中选择GPU模块
// 2. 启用GPU Profiling

// 创建GPU事件标记
using UnityEngine;
using UnityEngine.Rendering;

public class GPUProfilerExample : MonoBehaviour
{
    private CommandBuffer cmd;
    
    void Start()
    {
        cmd = new CommandBuffer();
    }
    
    void Update()
    {
        // 创建命令缓冲区以添加GPU Profiler标记
        cmd.Clear();
        
        // 添加开始采样标记
        cmd.BeginSample("Custom GPU Effect");
        
        // 添加你的GPU操作...
        cmd.SetGlobalColor("_EffectColor", Color.red);
        
        // 结束采样标记
        cmd.EndSample("Custom GPU Effect");
        
        // 执行命令
        Graphics.ExecuteCommandBuffer(cmd);
    }
}

分析着色器性能

// 使用着色器变种收集器
#if UNITY_EDITOR
using UnityEditor;

public static class ShaderVariantCollector
{
    [MenuItem("Tools/Collect Shader Variants")]
    public static void CollectVariants()
    {
        ShaderVariantCollection collection = new ShaderVariantCollection();
        
        // 收集项目中所有材质使用的着色器变种
        foreach (Material material in FindAllMaterials())
        {
            Shader shader = material.shader;
            ShaderVariantCollection.ShaderVariant variant = new ShaderVariantCollection.ShaderVariant(
                shader, 
                PassType.Normal, 
                new string[] { material.GetShaderKeywords() }
            );
            
            collection.Add(variant);
        }
        
        // 保存收集的变种
        string path = "Assets/CollectedVariants.shadervariants";
        AssetDatabase.CreateAsset(collection, path);
        AssetDatabase.SaveAssets();
    }
    
    private static Material[] FindAllMaterials()
    {
        // 查找项目中的所有材质...
        return new Material[0]; // 实际实现会返回项目中的所有材质
    }
}
#endif

九、Physics Profiler与优化

物理性能监控

using UnityEngine;
using UnityEngine.Profiling;

public class PhysicsProfiler : MonoBehaviour
{
    CustomSampler physicsSampler;
    
    void Start()
    {
        physicsSampler = CustomSampler.Create("PhysicsSimulation");
    }
    
    void FixedUpdate()
    {
        physicsSampler.Begin();
        
        // 执行物理相关计算
        SimulateCustomPhysics();
        
        physicsSampler.End();
    }
    
    void SimulateCustomPhysics()
    {
        Rigidbody[] rigidbodies = FindObjectsOfType<Rigidbody>();
        
        // 对每个物体进行一些自定义物理模拟
        foreach (Rigidbody rb in rigidbodies)
        {
            if (rb.IsSleeping()) continue;
            
            // 记录详细信息
            Profiler.BeginSample("PhysicsCalculation: " + rb.name);
            
            // 进行计算...
            
            Profiler.EndSample();
        }
    }
}

物理优化技术

// 物理优化示例
public class PhysicsOptimizer : MonoBehaviour
{
    void Start()
    {
        OptimizePhysics();
    }
    
    void OptimizePhysics()
    {
        // 1. 调整物理时间步长
        Time.fixedDeltaTime = 0.02f; // 50Hz, 默认但可根据需求调整
        
        // 2. 使用合适的碰撞检测模式
        Rigidbody rb = GetComponent<Rigidbody>();
        if (rb)
        {
            if (rb.velocity.magnitude > 10f)
            {
                rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
            }
            else
            {
                rb.collisionDetectionMode = CollisionDetectionMode.Discrete;
            }
        }
        
        // 3. 优化碰撞体
        // 尽量使用基本碰撞体类型: 球体、胶囊体、盒体
        
        // 4. 设置物理层级掩码减少碰撞检测
        Physics.IgnoreLayerCollision(8, 9, true); // 例如:忽略8和9层之间的碰撞
    }
    
    void SetupCompoundCollider()
    {
        // 5. 使用复合碰撞体替代复杂Mesh Collider
        GameObject compoundObj = new GameObject("CompoundCollider");
        compoundObj.transform.SetParent(transform);
        
        // 添加基本碰撞体模拟复杂形状
        SphereCollider sphere = compoundObj.AddComponent<SphereCollider>();
        sphere.center = new Vector3(0, 1, 0);
        sphere.radius = 0.5f;
        
        BoxCollider box = compoundObj.AddComponent<BoxCollider>();
        box.center = new Vector3(0, 0, 0);
        box.size = new Vector3(1, 1, 1);
    }
}

十、UI性能分析与优化

UI Profiler标记

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Profiling;

public class UIPerformanceMonitor : MonoBehaviour
{
    void Update()
    {
        // 标记UI更新
        Profiler.BeginSample("UI Updates");
        
        UpdateComplexUI();
        
        Profiler.EndSample();
    }
    
    void UpdateComplexUI()
    {
        // UI更新细分
        Profiler.BeginSample("Text Updates");
        UpdateTextElements();
        Profiler.EndSample();
        
        Profiler.BeginSample("Layout Recalculation");
        RecalculateLayouts();
        Profiler.EndSample();
    }
    
    void UpdateTextElements()
    {
        // 更新文本元素...
    }
    
    void RecalculateLayouts()
    {
        // 重新计算布局...
        LayoutRebuilder.ForceRebuildLayoutImmediate(GetComponent<RectTransform>());
    }
}

UI批处理分析

// UI批处理分析工具
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

public class UIBatchingAnalyzer : MonoBehaviour
{
    // 在编辑器模式使用
#if UNITY_EDITOR
    [ContextMenu("Analyze UI Batching")]
    public void AnalyzeUIBatching()
    {
        Canvas[] canvases = FindObjectsOfType<Canvas>();
        Dictionary<Material, List<Graphic>> materialToGraphic = new Dictionary<Material, List<Graphic>>();
        
        foreach (Canvas canvas in canvases)
        {
            Graphic[] graphics = canvas.GetComponentsInChildren<Graphic>();
            
            foreach (Graphic graphic in graphics)
            {
                Material material = graphic.materialForRendering;
                
                if (!materialToGraphic.ContainsKey(material))
                {
                    materialToGraphic[material] = new List<Graphic>();
                }
                
                materialToGraphic[material].Add(graphic);
            }
        }
        
        // 输出分析结果
        Debug.Log($"Found {canvases.Length} canvases and {materialToGraphic.Count} unique materials");
        
        foreach (var kvp in materialToGraphic)
        {
            Debug.Log($"Material: {kvp.Key.name} - Used by {kvp.Value.Count} graphics");
            
            if (kvp.Value.Count == 1)
            {
                Debug.LogWarning($"Material {kvp.Key.name} is only used by one graphic. Consider combining or sharing materials.");
            }
        }
    }
#endif
}

十一、网络Profiling

网络性能分析

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using UnityEngine.Profiling;

public class NetworkProfiler : MonoBehaviour
{
    CustomSampler downloadSampler;
    CustomSampler uploadSampler;
    
    void Start()
    {
        downloadSampler = CustomSampler.Create("Network Download");
        uploadSampler = CustomSampler.Create("Network Upload");
    }
    
    public IEnumerator DownloadWithProfiling(string url)
    {
        downloadSampler.Begin();
        
        UnityWebRequest request = UnityWebRequest.Get(url);
        yield return request.SendWebRequest();
        
        if (request.result == UnityWebRequest.Result.Success)
        {
            long downloadedBytes = request.downloadedBytes;
            float downloadTimeSec = request.downloadProgress > 0 
                ? downloadedBytes / (request.downloadedBytes * request.downloadProgress) 
                : 0;
                
            Debug.Log($"Downloaded {downloadedBytes} bytes in {downloadTimeSec}s " +
                     $"({downloadedBytes/downloadTimeSec/1024} KB/s)");
        }
        
        downloadSampler.End();
    }
    
    public IEnumerator UploadWithProfiling(string url, byte[] data)
    {
        uploadSampler.Begin();
        
        UnityWebRequest request = new UnityWebRequest(url, "POST");
        request.uploadHandler = new UploadHandlerRaw(data);
        request.downloadHandler = new DownloadHandlerBuffer();
        
        float startTime = Time.realtimeSinceStartup;
        yield return request.SendWebRequest();
        float uploadTime = Time.realtimeSinceStartup - startTime;
        
        if (request.result == UnityWebRequest.Result.Success)
        {
            Debug.Log($"Uploaded {data.Length} bytes in {uploadTime}s " +
                     $"({data.Length/uploadTime/1024} KB/s)");
        }
        
        uploadSampler.End();
    }
}

十二、自动化性能测试

性能测试框架

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Unity.Profiling;
using System.IO;

public class AutomatedPerformanceTest : MonoBehaviour
{
    [System.Serializable]
    public class TestCase
    {
        public string name;
        public GameObject scenePrefab;
        public float duration = 10f;
        public bool captureMemory = true;
        public bool captureCPU = true;
        public bool captureGPU = true;
    }
    
    public List<TestCase> testCases = new List<TestCase>();
    public bool saveResultsToFile = true;
    public string resultsDirectory = "PerformanceResults";
    
    // 性能metrics
    private ProfilerRecorder cpuRecorder;
    private ProfilerRecorder gpuRecorder;
    private ProfilerRecorder memoryRecorder;
    
    private class TestResult
    {
        public string testName;
        public float avgCpuTime;
        public float maxCpuTime;
        public float avgGpuTime;
        public float maxGpuTime;
        public float avgMemory;
        public float maxMemory;
        public List<float> frameTimeSamples = new List<float>();
    }
    
    private List<TestResult> results = new List<TestResult>();
    
    private void Start()
    {
        StartCoroutine(RunTestSuite());
    }
    
    IEnumerator RunTestSuite()
    {
        foreach (TestCase test in testCases)
        {
            yield return StartCoroutine(RunTestCase(test));
        }
        
        if (saveResultsToFile)
        {
            SaveResultsToFile();
        }
        
        Debug.Log("Performance tests completed.");
    }
    
    IEnumerator RunTestCase(TestCase test)
    {
        Debug.Log($"Starting test: {test.name}");
        
        // 清理之前的场景和垃圾收集
        Resources.UnloadUnusedAssets();
        System.GC.Collect();
        
        yield return new WaitForSeconds(1f);
        
        // 初始化测试场景
        GameObject testScene = null;
        if (test.scenePrefab != null)
        {
            testScene = Instantiate(test.scenePrefab);
        }
        
        // 设置Profiler记录器
        SetupRecorders(test);
        
        // 创建结果对象
        TestResult result = new TestResult
        {
            testName = test.name,
            avgCpuTime = 0,
            maxCpuTime = 0,
            avgGpuTime = 0,
            maxGpuTime = 0,
            avgMemory = 0,
            maxMemory = 0
        };
        
        // 记录数据点数量
        int sampleCount = 0;
        
        // 设置测试开始时间
        float startTime = Time.realtimeSinceStartup;
        float lastFrameTime = startTime;
        
        // 测试循环
        while (Time.realtimeSinceStartup - startTime < test.duration)
        {
            // 记录帧时间
            float currentTime = Time.realtimeSinceStartup;
            float frameTime = currentTime - lastFrameTime;
            lastFrameTime = currentTime;
            
            result.frameTimeSamples.Add(frameTime * 1000f); // 转换为毫秒
            
            // 收集CPU数据
            if (test.captureCPU && cpuRecorder.Valid)
            {
                float cpuTime = cpuRecorder.LastValue / 1000000f; // 纳秒转换为毫秒
                result.avgCpuTime += cpuTime;
                result.maxCpuTime = Mathf.Max(result.maxCpuTime, cpuTime);
            }
            
            // 收集GPU数据
            if (test.captureGPU && gpuRecorder.Valid)
            {
                float gpuTime = gpuRecorder.LastValue / 1000000f; // 纳秒转换为毫秒
                result.avgGpuTime += gpuTime;
                result.maxGpuTime = Mathf.Max(result.maxGpuTime, gpuTime);
            }
            
            // 收集内存数据
            if (test.captureMemory && memoryRecorder.Valid)
            {
                float memoryUsage = memoryRecorder.LastValue / (1024f * 1024f); // 转换为MB
                result.avgMemory += memoryUsage;
                result.maxMemory = Mathf.Max(result.maxMemory, memoryUsage);
            }
            
            sampleCount++;
            
            yield return null;
        }
        
        // 计算平均值
        if (sampleCount > 0)
        {
            result.avgCpuTime /= sampleCount;
            result.avgGpuTime /= sampleCount;
            result.avgMemory /= sampleCount;
        }
        
        // 输出测试结果
        Debug.Log($"Test: {test.name} completed");
        Debug.Log($"Avg CPU: {result.avgCpuTime:F2}ms, Max CPU: {result.maxCpuTime:F2}ms");
        Debug.Log($"Avg GPU: {result.avgGpuTime:F2}ms, Max GPU: {result.maxGpuTime:F2}ms");
        Debug.Log($"Avg Memory: {result.avgMemory:F2}MB, Max Memory: {result.maxMemory:F2}MB");
        
        // 停止记录器
        DisposeRecorders();
        
        // 保存结果
        results.Add(result);
        
        // 清理测试场景
        if (testScene != null)
        {
            Destroy(testScene);
        }
        
        yield return null;
    }
    
    private void SetupRecorders(TestCase test)
    {
        if (test.captureCPU)
        {
            cpuRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Internal, "MainThreadTime");
        }
        
        if (test.captureGPU)
        {
            gpuRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, "GPU");
        }
        
        if (test.captureMemory)
        {
            memoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Total Used Memory");
        }
    }
    
    private void DisposeRecorders()
    {
        if (cpuRecorder.Valid) cpuRecorder.Dispose();
        if (gpuRecorder.Valid) gpuRecorder.Dispose();
        if (memoryRecorder.Valid) memoryRecorder.Dispose();
    }
    
    private void SaveResultsToFile()
    {
        string directory = Path.Combine(Application.persistentDataPath, resultsDirectory);
        
        if (!Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
        }
        
        string timestamp = System.DateTime.Now.ToString("yyyyMMdd_HHmmss");
        string filename = Path.Combine(directory, $"PerformanceResults_{timestamp}.csv");
        
        using (StreamWriter writer = new StreamWriter(filename))
        {
            // 写入标题行
            writer.WriteLine("Test,AvgCPU(ms),MaxCPU(ms),AvgGPU(ms),MaxGPU(ms),AvgMemory(MB),MaxMemory(MB)");
            
            // 写入测试结果
            foreach (TestResult result in results)
            {
                writer.WriteLine($"{result.testName},{result.avgCpuTime:F2},{result.maxCpuTime:F2}," +
                                $"{result.avgGpuTime:F2},{result.maxGpuTime:F2}," +
                                $"{result.avgMemory:F2},{result.maxMemory:F2}");
            }
            
            // 写入帧时间详情
            writer.WriteLine("\nFrame times (ms):");
            
            // 写入每个测试的帧时间
            foreach (TestResult result in results)
            {
                writer.Write($"{result.testName}");
                
                foreach (float frametime in result.frameTimeSamples)
                {
                    writer.Write($",{frametime:F2}");
                }
                
                writer.WriteLine();
            }
        }
        
        Debug.Log($"Results saved to: {filename}");
    }
}

十三、高级Profiling技术

自定义Profiler窗口

#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;

// 自定义Profiler窗口示例
public class CustomProfilerWindow : EditorWindow
{
    [MenuItem("Window/Analysis/Custom Profiler")]
    public static void ShowWindow()
    {
        GetWindow<CustomProfilerWindow>("Custom Profiler");
    }
    
    private List<ProfilerMarkerInfo> customMarkers = new List<ProfilerMarkerInfo>();
    private Vector2 scrollPosition;
    private bool recordData = false;
    private float recordTimer = 0f;
    private float recordInterval = 0.5f;
    
    // 自定义标记信息结构
    private class ProfilerMarkerInfo
    {
        public string name;
        public float avgTime;
        public float maxTime;
        public int callCount;
        public List<float> timeHistory = new List<float>();
        
        public ProfilerMarkerInfo(string markerName)
        {
            name = markerName;
            avgTime = 0f;
            maxTime = 0f;
            callCount = 0;
        }
    }
    
    void OnGUI()
    {
        GUILayout.Label("Custom Performance Markers", EditorStyles.boldLabel);
        
        EditorGUILayout.BeginHorizontal();
        
        if (GUILayout.Button(recordData ? "Stop Recording" : "Start Recording"))
        {
            recordData = !recordData;
            
            if (recordData)
            {
                // 启动记录
                EditorApplication.update += OnEditorUpdate;
                ClearData();
            }
            else
            {
                // 停止记录
                EditorApplication.update -= OnEditorUpdate;
            }
        }
        
        if (GUILayout.Button("Clear Data"))
        {
            ClearData();
        }
        
        EditorGUILayout.EndHorizontal();
        
        recordInterval = EditorGUILayout.Slider("Record Interval (s)", recordInterval, 0.1f, 2.0f);
        
        EditorGUILayout.Space();
        
        DrawMarkersTable();
    }
    
    void OnEditorUpdate()
    {
        if (!recordData) return;
        
        recordTimer += Time.deltaTime;
        
        if (recordTimer >= recordInterval)
        {
            UpdateMarkerData();
            recordTimer = 0f;
            Repaint();
        }
    }
    
    void UpdateMarkerData()
    {
        // 获取自定义Marker数据
        // 注意:此方法需要你已经使用了自定义标记
        
        // 这里只是模拟数据,实际应用中需要连接到Unity Profiler API
        string[] markerNames = new string[] { 
            "MyGame.Update", 
            "MyGame.Physics", 
            "MyGame.Rendering", 
            "MyGame.AI"
        };
        
        foreach (string markerName in markerNames)
        {
            // 查找或创建标记信息
            ProfilerMarkerInfo marker = customMarkers.FirstOrDefault(m => m.name == markerName);
            
            if (marker == null)
            {
                marker = new ProfilerMarkerInfo(markerName);
                customMarkers.Add(marker);
            }
            
            // 模拟获取标记数据
            float time = Random.Range(0.1f, 10.0f);
            int calls = Random.Range(1, 100);
            
            // 更新数据
            marker.timeHistory.Add(time);
            if (marker.timeHistory.Count > 100)
                marker.timeHistory.RemoveAt(0);
                
            marker.maxTime = Mathf.Max(marker.maxTime, time);
            marker.callCount = calls;
            
            // 计算平均时间
            marker.avgTime = marker.timeHistory.Count > 0 ? 
                marker.timeHistory.Average() : 0f;
        }
    }
    
    void ClearData()
    {
        customMarkers.Clear();
        recordTimer = 0f;
    }
    
    void DrawMarkersTable()
    {
        EditorGUILayout.BeginVertical(GUI.skin.box);
        
        // 表头
        EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
        GUILayout.Label("Marker Name", EditorStyles.toolbarButton, GUILayout.Width(200));
        GUILayout.Label("Avg Time (ms)", EditorStyles.toolbarButton, GUILayout.Width(100));
        GUILayout.Label("Max Time (ms)", EditorStyles.toolbarButton, GUILayout.Width(100));
        GUILayout.Label("Call Count", EditorStyles.toolbarButton, GUILayout.Width(80));
        GUILayout.Label("Graph", EditorStyles.toolbarButton);
        EditorGUILayout.EndHorizontal();
        
        scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
        
        // 表行
        foreach (ProfilerMarkerInfo marker in customMarkers.OrderByDescending(m => m.avgTime))
        {
            EditorGUILayout.BeginHorizontal();
            
            GUILayout.Label(marker.name, GUILayout.Width(200));
            GUILayout.Label(marker.avgTime.ToString("F2"), GUILayout.Width(100));
            GUILayout.Label(marker.maxTime.ToString("F2"), GUILayout.Width(100));
            GUILayout.Label(marker.callCount.ToString(), GUILayout.Width(80));
            
            // 简单的性能图表
            Rect graphRect = GUILayoutUtility.GetRect(100, 20, GUILayout.ExpandWidth(true));
            DrawPerformanceGraph(graphRect, marker);
            
            EditorGUILayout.EndHorizontal();
        }
        
        EditorGUILayout.EndScrollView();
        EditorGUILayout.EndVertical();
    }
    
    void DrawPerformanceGraph(Rect rect, ProfilerMarkerInfo marker)
    {
        if (marker.timeHistory.Count == 0) return;
        
        EditorGUI.DrawRect(rect, new Color(0.2f, 0.2f, 0.2f));
        
        float maxValue = marker.timeHistory.Max();
        if (maxValue <= 0) return;
        
        // 绘制背景网格
        DrawGraphGrid(rect);
        
        // 绘制图形
        int count = marker.timeHistory.Count;
        float barWidth = rect.width / count;
        
        for (int i = 0; i < count; i++)
        {
            float value = marker.timeHistory[i];
            float normalizedValue = value / maxValue;
            float barHeight = rect.height * normalizedValue;
            
            // 根据性能状况选择颜色
            Color barColor = GetPerformanceColor(value);
            
            Rect barRect = new Rect(
                rect.x + i * barWidth,
                rect.y + rect.height - barHeight,
                barWidth,
                barHeight
            );
            
            EditorGUI.DrawRect(barRect, barColor);
        }
        
        // 显示阈值线
        DrawThresholdLine(rect, 16.67f, maxValue, Color.yellow); // 60 FPS
        DrawThresholdLine(rect, 33.33f, maxValue, Color.red);    // 30 FPS
    }
    
    void DrawGraphGrid(Rect rect)
    {
        // 绘制水平线
        for (int i = 1; i < 4; i++)
        {
            float y = rect.y + rect.height * (i / 4f);
            EditorGUI.DrawRect(new Rect(rect.x, y, rect.width, 1), new Color(0.3f, 0.3f, 0.3f, 0.5f));
        }
        
        // 绘制垂直线
        for (int i = 1; i < 4; i++)
        {
            float x = rect.x + rect.width * (i / 4f);
            EditorGUI.DrawRect(new Rect(x, rect.y, 1, rect.height), new Color(0.3f, 0.3f, 0.3f, 0.5f));
        }
    }
    
    void DrawThresholdLine(Rect rect, float thresholdMs, float maxValue, Color color)
    {
        if (thresholdMs > maxValue) return;
        
        float normalizedThreshold = thresholdMs / maxValue;
        float y = rect.y + rect.height * (1f - normalizedThreshold);
        
        EditorGUI.DrawRect(new Rect(rect.x, y, rect.width, 1), color);
    }
    
    Color GetPerformanceColor(float timeMs)
    {
        if (timeMs < 8.33f) return Color.green;       // 120+ FPS: 绿色
        if (timeMs < 16.67f) return Color.cyan;       // 60-120 FPS: 青色
        if (timeMs < 33.33f) return Color.yellow;     // 30-60 FPS: 黄色
        return Color.red;                           // <30 FPS: 红色
    }
}
#endif

集成CI/CD性能测试

using UnityEngine;
using System.IO;
using System.Collections.Generic;

// CI/CD性能测试辅助类
public static class CIPerfTestHelper
{
    // 性能测试预设路径
    private const string TEST_PRESET_PATH = "Assets/CI/PerformanceTests";
    
    // 性能阈值
    [System.Serializable]
    public class PerformanceThresholds
    {
        public float maxAverageCpuTime = 16.0f; // 60 FPS
        public float maxPeakCpuTime = 33.0f;    // 30 FPS
        public float maxMemoryUsage = 1024.0f;  // 1 GB
    }
    
    // 运行性能测试,返回通过/失败结果
    public static bool RunPerformanceTests(string outputPath = null, PerformanceThresholds thresholds = null)
    {
        if (thresholds == null)
        {
            thresholds = new PerformanceThresholds();
        }
        
        if (string.IsNullOrEmpty(outputPath))
        {
            outputPath = Path.Combine(Application.persistentDataPath, "CIPerformanceResults.json");
        }
        
        // 查找和运行所有测试
        List<TestResult> results = new List<TestResult>();
        
        // 查找测试预设
        string[] testAssets = GetAllPerformanceTestAssets();
        
        foreach (string testPath in testAssets)
        {
            // 加载测试预设
            GameObject testPrefab = Resources.Load<GameObject>(testPath);
            if (testPrefab == null) continue;
            
            // 运行测试
            TestResult result = RunSingleTest(testPrefab, thresholds);
            results.Add(result);
            
            // 清理
            Resources.UnloadAsset(testPrefab);
        }
        
        // 保存结果
        SaveTestResults(results, outputPath);
        
        // 检查是否所有测试都通过
        bool allPassed = results.TrueForAll(r => r.passed);
        
        // 输出摘要
        OutputTestSummary(results);
        
        return allPassed;
    }
    
    private static string[] GetAllPerformanceTestAssets()
    {
        // 此处需要根据实际项目中的资源管理方式调整
        // 示例中假设测试预设位于Resources文件夹下
        return new string[] {
            "PerformanceTests/CPUTest",
            "PerformanceTests/GPUTest",
            "PerformanceTests/MemoryTest"
        };
    }
    
    [System.Serializable]
    private class TestResult
    {
        public string testName;
        public bool passed;
        public float averageCpuTime;
        public float peakCpuTime;
        public float memoryUsage;
        public string[] failures;
    }
    
    private static TestResult RunSingleTest(GameObject testPrefab, PerformanceThresholds thresholds)
    {
        TestResult result = new TestResult
        {
            testName = testPrefab.name,
            passed = true
        };
        
        List<string> failures = new List<string>();
        
        // 实例化测试场景
        GameObject testInstance = Object.Instantiate(testPrefab);
        
        // 运行测试逻辑 (简化示例)
        // 在实际项目中,这里应该有更复杂的逻辑来收集性能数据
        result.averageCpuTime = Random.Range(5.0f, 25.0f);
        result.peakCpuTime = result.averageCpuTime * Random.Range(1.2f, 2.0f);
        result.memoryUsage = Random.Range(512.0f, 1536.0f);
        
        // 验证结果
        if (result.averageCpuTime > thresholds.maxAverageCpuTime)
        {
            failures.Add($"Average CPU time ({result.averageCpuTime}ms) exceeds threshold ({thresholds.maxAverageCpuTime}ms)");
            result.passed = false;
        }
        
        if (result.peakCpuTime > thresholds.maxPeakCpuTime)
        {
            failures.Add($"Peak CPU time ({result.peakCpuTime}ms) exceeds threshold ({thresholds.maxPeakCpuTime}ms)");
            result.passed = false;
        }
        
        if (result.memoryUsage > thresholds.maxMemoryUsage)
        {
            failures.Add($"Memory usage ({result.memoryUsage}MB) exceeds threshold ({thresholds.maxMemoryUsage}MB)");
            result.passed = false;
        }
        
        result.failures = failures.ToArray();
        
        // 清理
        Object.Destroy(testInstance);
        
        return result;
    }
    
    private static void SaveTestResults(List<TestResult> results, string outputPath)
    {
        string json = JsonUtility.ToJson(new { results = results.ToArray() }, true);
        File.WriteAllText(outputPath, json);
        Debug.Log($"Performance test results saved to: {outputPath}");
    }
    
    private static void OutputTestSummary(List<TestResult> results)
    {
        int passCount = results.FindAll(r => r.passed).Count;
        int failCount = results.Count - passCount;
        
        Debug.Log($"Performance Tests Summary: {passCount} passed, {failCount} failed");
        
        foreach (TestResult result in results)
        {
            if (result.passed)
            {
                Debug.Log($"✓ {result.testName} - CPU: {result.averageCpuTime:F2}ms, Peak: {result.peakCpuTime:F2}ms, Memory: {result.memoryUsage:F1}MB");
            }
            else
            {
                Debug.LogError($"✗ {result.testName} - Failed");
                foreach (string failure in result.failures)
                {
                    Debug.LogError($"  - {failure}");
                }
            }
        }
    }
}

十四、Profiling实践与案例分析

内存泄漏检测案例

using UnityEngine;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class MemoryLeakDetector : MonoBehaviour
{
    // 事件订阅引起的内存泄漏示例
    public class EventManager
    {
        public delegate void TestEventHandler();
        public event TestEventHandler OnTestEvent;
        
        public void RaiseEvent()
        {
            OnTestEvent?.Invoke();
        }
    }
    
    public class EventSubscriber
    {
        private EventManager eventManager;
        
        public EventSubscriber(EventManager manager)
        {
            eventManager = manager;
            // 添加事件监听
            eventManager.OnTestEvent += OnEventRaised;
        }
        
        private void OnEventRaised()
        {
            Debug.Log("Event received");
        }
        
        // 问题:没有提供取消订阅的方法,导致内存泄漏
        // 正确做法应该是实现一个Cleanup方法:
        public void Cleanup()
        {
            eventManager.OnTestEvent -= OnEventRaised;
        }
    }
    
    // 未释放的GDI资源示例
    public class NativeResourceExample
    {
        private IntPtr nativeResource;
        
        public NativeResourceExample()
        {
            // 分配一个原生资源 (模拟)
            nativeResource = Marshal.AllocHGlobal(1024 * 1024);
        }
        
        // 问题:没有正确实现IDisposable或析构函数
        // 正确做法应该是:
        ~NativeResourceExample()
        {
            if (nativeResource != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(nativeResource);
                nativeResource = IntPtr.Zero;
            }
        }
        
        public void Dispose()
        {
            if (nativeResource != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(nativeResource);
                nativeResource = IntPtr.Zero;
            }
            System.GC.SuppressFinalize(this);
        }
    }
    
    // 循环引用示例
    public class NodeA
    {
        public NodeB Reference { get; set; }
    }
    
    public class NodeB
    {
        public NodeA Reference { get; set; }
    }
    
    // 创建循环引用 (无法被GC回收)
    void CreateCircularReference()
    {
        NodeA nodeA = new NodeA();
        NodeB nodeB = new NodeB();
        
        nodeA.Reference = nodeB;
        nodeB.Reference = nodeA;
        
        // 两个对象都无法被垃圾回收,因为它们互相引用
    }
    
    // 查找静态集合泄漏示例
    private static List<object> staticCollection = new List<object>();
    
    void AddToStaticCollection()
    {
        for (int i = 0; i < 1000; i++)
        {
            // 创建1MB的数据并添加到静态集合
            byte[] data = new byte[1024 * 1024];
            staticCollection.Add(data);
        }
        
        // 静态集合不会被清理,导致内存持续增长
    }
    
    // 检测泄漏的方法示例
    public void DetectLeaks()
    {
        // 1. 触发垃圾回收
        System.GC.Collect();
        System.GC.WaitForPendingFinalizers();
        System.GC.Collect();
        
        // 2. 记录初始内存使用量
        long startMemory = System.GC.GetTotalMemory(true);
        
        // 3. 执行潜在的泄漏操作
        for (int i = 0; i < 100; i++)
        {
            EventManager manager = new EventManager();
            EventSubscriber subscriber = new EventSubscriber(manager);
            
            // 如果不调用subscriber.Cleanup(),会导致内存泄漏
        }
        
        // 4. 再次触发垃圾回收
        System.GC.Collect();
        System.GC.WaitForPendingFinalizers();
        System.GC.Collect();
        
        // 5. 检查内存增长
        long endMemory = System.GC.GetTotalMemory(true);
        long memoryLeaked = endMemory - startMemory;
        
        Debug.Log($"Memory potentially leaked: {memoryLeaked / 1024} KB");
        
        if (memoryLeaked > 1024 * 100) // 超过100KB
        {
            Debug.LogWarning("Potential memory leak detected!");
        }
    }
}

渲染管线优化案例

using UnityEngine;
using UnityEngine.Rendering;

public class RenderingPipelineOptimizer : MonoBehaviour
{
    [Header("Optimization Settings")]
    [SerializeField] private bool optimizeBatching = true;
    [SerializeField] private bool optimizeCulling = true;
    [SerializeField] private bool optimizeShaders = true;
    [SerializeField] private bool optimizeTextures = true;
    
    [Header("Profiling")]
    [SerializeField] private bool enableProfiling = true;
    
    private CommandBuffer profilingCommandBuffer;
    
    private void Start()
    {
        if (enableProfiling)
        {
            SetupProfiling();
        }
        
        // 应用优化
        ApplyOptimizations();
    }
    
    private void SetupProfiling()
    {
        profilingCommandBuffer = new CommandBuffer();
        profilingCommandBuffer.name = "Rendering Pipeline Profiling";
        
        // 向渲染管线添加性能标记
        Camera.main.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, profilingCommandBuffer);
        
        // 设置渲染标记
        profilingCommandBuffer.BeginSample("CameraRender");
        
        // 在特定渲染阶段添加细分标记
        profilingCommandBuffer.BeginSample("OpaqueGeometry");
        profilingCommandBuffer.EndSample("OpaqueGeometry");
        
        profilingCommandBuffer.BeginSample("Shadows");
        profilingCommandBuffer.EndSample("Shadows");
        
        profilingCommandBuffer.BeginSample("Transparency");
        profilingCommandBuffer.EndSample("Transparency");
        
        profilingCommandBuffer.BeginSample("PostProcessing");
        profilingCommandBuffer.EndSample("PostProcessing");
        
        profilingCommandBuffer.EndSample("CameraRender");
    }
    
    private void ApplyOptimizations()
    {
        if (optimizeBatching)
        {
            OptimizeBatching();
        }
        
        if (optimizeCulling)
        {
            OptimizeCulling();
        }
        
        if (optimizeShaders)
        {
            OptimizeShaders();
        }
        
        if (optimizeTextures)
        {
            OptimizeTextures();
        }
    }
    
    private void OptimizeBatching()
    {
        // 1. 分析当前场景中的批处理情况
        MeshRenderer[] renderers = FindObjectsOfType<MeshRenderer>();
        
        // 按材质分组
        Dictionary<Material, List<MeshRenderer>> materialGroups = new Dictionary<Material, List<MeshRenderer>>();
        
        foreach (MeshRenderer renderer in renderers)
        {
            Material[] materials = renderer.sharedMaterials;
            
            foreach (Material material in materials)
            {
                if (material == null) continue;
                
                if (!materialGroups.ContainsKey(material))
                {
                    materialGroups[material] = new List<MeshRenderer>();
                }
                
                materialGroups[material].Add(renderer);
            }
        }
        
        // 2. 输出批处理分析报告
        Debug.Log($"Found {materialGroups.Count} different materials in the scene");
        
        foreach (var group in materialGroups)
        {
            Debug.Log($"Material '{group.Key.name}' is used by {group.Value.Count} renderers");
            
            // 检查材质是否可以批处理
            if (group.Value.Count > 1 && !IsBatchable(group.Key))
            {
                Debug.LogWarning($"Material '{group.Key.name}' has properties that prevent batching!");
            }
        }
        
        // 3. 启用GPU实例化 (对于共享相同材质但无法静态批处理的对象)
        foreach (var group in materialGroups)
        {
            if (group.Value.Count >= 5 && group.Key.enableInstancing == false)
            {
                Debug.Log($"Enabling GPU instancing for material: {group.Key.name}");
                group.Key.enableInstancing = true;
            }
        }
    }
    
    private bool IsBatchable(Material material)
    {
        // 检查材质属性是否允许批处理
        if (material.HasProperty("_MainTex") && material.mainTexture != null && material.mainTexture.wrapMode == TextureWrapMode.Repeat)
        {
            return false; // 重复纹理模式可能阻止批处理
        }
        
        // 检查其他批处理障碍...
        
        return true;
    }
    
    private void OptimizeCulling()
    {
        // 1. 优化摄像机剔除设置
        Camera mainCamera = Camera.main;
        if (mainCamera != null)
        {
            // 调整摄像机远近裁剪面
            float sceneSize = CalculateSceneSize();
            mainCamera.farClipPlane = Mathf.Min(1000f, sceneSize * 1.5f);
            
            // 根据场景复杂度设置剔除遮罩
            // mainCamera.cullingMask = ...
        }
        
        // 2. 为大型静态对象添加遮挡剔除器
        MeshRenderer[] largeRenderers = FindObjectsOfType<MeshRenderer>();
        foreach (MeshRenderer renderer in largeRenderers)
        {
            if (IsLargeStaticObject(renderer) && !renderer.gameObject.GetComponent<OcclusionArea>())
            {
                Debug.Log($"Adding OcclusionArea to large static object: {renderer.gameObject.name}");
                renderer.gameObject.AddComponent<OcclusionArea>();
            }
        }
    }
    
    private float CalculateSceneSize()
    {
        Renderer[] allRenderers = FindObjectsOfType<Renderer>();
        if (allRenderers.Length == 0) return 100f;
        
        Bounds sceneBounds = new Bounds(allRenderers[0].bounds.center, Vector3.zero);
        foreach (Renderer renderer in allRenderers)
        {
            sceneBounds.Encapsulate(renderer.bounds);
        }
        
        // 返回场景的最大尺寸
        return Mathf.Max(sceneBounds.size.x, sceneBounds.size.y, sceneBounds.size.z);
    }
    
    private bool IsLargeStaticObject(MeshRenderer renderer)
    {
        // 检查对象是否足够大且是静态的
        bool isStatic = (renderer.gameObject.isStatic);
        float volume = renderer.bounds.size.x * renderer.bounds.size.y * renderer.bounds.size.z;
        
        return isStatic && volume > 100f; // 体积阈值可以根据场景调整
    }
    
    private void OptimizeShaders()
    {
        // 1. 查找并分析场景中使用的着色器
        Renderer[] renderers = FindObjectsOfType<Renderer>();
        Dictionary<Shader, int> shaderUsage = new Dictionary<Shader, int>();
        
        foreach (Renderer renderer in renderers)
        {
            foreach (Material material in renderer.sharedMaterials)
            {
                if (material != null && material.shader != null)
                {
                    Shader shader = material.shader;
                    
                    if (!shaderUsage.ContainsKey(shader))
                    {
                        shaderUsage[shader] = 0;
                    }
                    
                    shaderUsage[shader]++;
                }
            }
        }
        
        // 2. 输出着色器分析报告
        Debug.Log($"Found {shaderUsage.Count} different shaders in the scene");
        
        foreach (var entry in shaderUsage)
        {
            Debug.Log($"Shader '{entry.Key.name}' is used {entry.Value} times");
            
            // 检查复杂着色器
            if (IsComplexShader(entry.Key) && entry.Value > 10)
            {
                Debug.LogWarning($"Complex shader '{entry.Key.name}' is used frequently!");
            }
        }
    }
    
    private bool IsComplexShader(Shader shader)
    {
        // 检查复杂度的简单启发式方法 (基于名称)
        string name = shader.name.ToLower();
        return name.Contains("pbr") || name.Contains("specular") || 
               name.Contains("tessellation") || name.Contains("parallax");
    }
    
    private void OptimizeTextures()
    {
        // 1. 分析场景中的纹理
        Renderer[] renderers = FindObjectsOfType<Renderer>();
        HashSet<Texture> sceneTextures = new HashSet<Texture>();
        
        foreach (Renderer renderer in renderers)
        {
            foreach (Material material in renderer.sharedMaterials)
            {
                if (material != null)
                {
                    // 收集材质中的所有纹理
                    int[] texturePropertyIds = material.GetTexturePropertyNameIDs();
                    
                    foreach (int propertyId in texturePropertyIds)
                    {
                        Texture texture = material.GetTexture(propertyId);
                        if (texture != null)
                        {
                            sceneTextures.Add(texture);
                        }
                    }
                }
            }
        }
        
        // 2. 输出纹理分析报告
        Debug.Log($"Found {sceneTextures.Count} textures in the scene");
        
        long totalMemory = 0;
        
        foreach (Texture texture in sceneTextures)
        {
            // 估算纹理内存用量
            long estimatedMemory = EstimateTextureMemory(texture);
            totalMemory += estimatedMemory;
            
            Debug.Log($"Texture '{texture.name}': {texture.width}x{texture.height}, " +
                     $"Format: {GetTextureFormat(texture)}, " +
                     $"~{estimatedMemory / (1024 * 1024)}MB");
            
            // 检查大型纹理
            if (texture.width > 2048 || texture.height > 2048)
            {
                Debug.LogWarning($"Large texture detected: '{texture.name}' ({texture.width}x{texture.height})");
            }
        }
        
        Debug.Log($"Total estimated texture memory: {totalMemory / (1024 * 1024)}MB");
    }
    
    private long EstimateTextureMemory(Texture texture)
    {
        // 简单的纹理内存估算
        int width = texture.width;
        int height = texture.height;
        int bpp = GetTextureBytesPerPixel(texture);
        
        // 计算基本大小
        long baseSize = (long)width * height * bpp;
        
        // 考虑mipmaps (如果有)
        Texture2D tex2D = texture as Texture2D;
        if (tex2D != null && tex2D.mipmapCount > 1)
        {
            // mipmaps大约增加1/3的内存
            baseSize = (long)(baseSize * 1.33f);
        }
        
        return baseSize;
    }
    
    private int GetTextureBytesPerPixel(Texture texture)
    {
        // 根据纹理类型和格式估算每像素字节数
        Texture2D tex2D = texture as Texture2D;
        if (tex2D != null)
        {
            // 根据格式返回估算值
            switch (tex2D.format)
            {
                case TextureFormat.RGB24:
                    return 3;
                case TextureFormat.RGBA32:
                    return 4;
                case TextureFormat.DXT1:
                    return 1;  // 近似值
                case TextureFormat.DXT5:
                    return 2;  // 近似值
                // 更多格式...
                default:
                    return 4;  // 默认假设
            }
        }
        
        RenderTexture rt = texture as RenderTexture;
        if (rt != null)
        {
            // 根据格式返回估算值
            switch (rt.format)
            {
                case RenderTextureFormat.ARGB32:
                    return 4;
                case RenderTextureFormat.ARGBHalf:
                    return 8;
                // 更多格式...
                default:
                    return 4;
            }
        }
        
        return 4; // 默认假设
    }
    
    private string GetTextureFormat(Texture texture)
    {
        Texture2D tex2D = texture as Texture2D;
        if (tex2D != null)
        {
            return tex2D.format.ToString();
        }
        
        RenderTexture rt = texture as RenderTexture;
        if (rt != null)
        {
            return rt.format.ToString();
        }
        
        return "Unknown";
    }
}

CPU密集计算优化案例

using UnityEngine;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Burst;
using System.Threading.Tasks;

public class CPUOptimizationExample : MonoBehaviour
{
    [SerializeField] private int entityCount = 10000;
    [SerializeField] private OptimizationMode mode = OptimizationMode.Standard;
    
    public enum OptimizationMode
    {
        Standard,
        Optimized,
        JobSystem,
        ParallelFor
    }
    
    private struct Entity
    {
        public Vector3 position;
        public Vector3 velocity;
        public float mass;
        public bool active;
    }
    
    private List<Entity> entities = new List<Entity>();
    
    private void Start()
    {
        // 初始化实体
        for (int i = 0; i < entityCount; i++)
        {
            entities.Add(new Entity 
            {
                position = Random.insideUnitSphere * 100f,
                velocity = Random.insideUnitSphere * 10f,
                mass = Random.Range(1f, 10f),
                active = true
            });
        }
    }
    
    private void Update()
    {
        // 使用选定的优化模式更新实体
        switch (mode)
        {
            case OptimizationMode.Standard:
                UpdateEntitiesStandard();
                break;
                
            case OptimizationMode.Optimized:
                UpdateEntitiesOptimized();
                break;
                
            case OptimizationMode.JobSystem:
                UpdateEntitiesJobSystem();
                break;
                
            case OptimizationMode.ParallelFor:
                UpdateEntitiesParallel();
                break;
        }
    }
    
    // 标准方法 - 未优化
    private void UpdateEntitiesStandard()
    {
        // 每帧计算所有实体的重力相互作用
        for (int i = 0; i < entities.Count; i++)
        {
            if (!entities[i].active) continue;
            
            Vector3 totalForce = Vector3.zero;
            
            // 计算与其他所有实体的重力相互作用
            for (int j = 0; j < entities.Count; j++)
            {
                if (i == j || !entities[j].active) continue;
                
                Vector3 direction = entities[j].position - entities[i].position;
                float distance = direction.magnitude;
                
                if (distance < 0.1f) continue;
                
                // 重力公式: F = G * (m1 * m2) / d^2
                float forceMagnitude = 0.01f * entities[i].mass * entities[j].mass / (distance * distance);
                
                totalForce += direction.normalized * forceMagnitude;
            }
            
            // 更新速度和位置 (F = ma, 因此 a = F/m)
            Vector3 acceleration = totalForce / entities[i].mass;
            
            Entity entity = entities[i];
            entity.velocity += acceleration * Time.deltaTime;
            entity.position += entity.velocity * Time.deltaTime;
            entities[i] = entity;
        }
    }
    
    // 优化方法 - 使用简单的CPU优化技巧
    private void UpdateEntitiesOptimized()
    {
        float deltaTime = Time.deltaTime;
        float gravityConstant = 0.01f;
        
        // 预计算重复使用的数据
        Vector3[] forces = new Vector3[entities.Count];
        
        // 使用空间分区优化 (这里使用简单网格)
        const int gridSize = 10;
        List<int>[ , , ] grid = new List<int>[gridSize, gridSize, gridSize];
        
        // 初始化网格
        for (int x = 0; x < gridSize; x++)
        {
            for (int y = 0; y < gridSize; y++)
            {
                for (int z = 0; z < gridSize; z++)
                {
                    grid[x, y, z] = new List<int>();
                }
            }
        }
        
        // 将实体分配到网格
        for (int i = 0; i < entities.Count; i++)
        {
            if (!entities[i].active) continue;
            
            // 计算网格坐标
            Vector3 pos = entities[i].position + new Vector3(100, 100, 100); // 偏移以处理负值
            int x = Mathf.Clamp((int)(pos.x / 20), 0, gridSize - 1);
            int y = Mathf.Clamp((int)(pos.y / 20), 0, gridSize - 1);
            int z = Mathf.Clamp((int)(pos.z / 20), 0, gridSize - 1);
            
            grid[x, y, z].Add(i);
        }
        
        // 计算力
        for (int i = 0; i < entities.Count; i++)
        {
            if (!entities[i].active) continue;
            
            Vector3 totalForce = Vector3.zero;
            Vector3 pos = entities[i].position;
            
            // 计算网格坐标
            Vector3 gridPos = pos + new Vector3(100, 100, 100);
            int gx = Mathf.Clamp((int)(gridPos.x / 20), 0, gridSize - 1);
            int gy = Mathf.Clamp((int)(gridPos.y / 20), 0, gridSize - 1);
            int gz = Mathf.Clamp((int)(gridPos.z / 20), 0, gridSize - 1);
            
            // 只检查相邻网格的实体
            for (int nx = Mathf.Max(0, gx - 1); nx <= Mathf.Min(gx + 1, gridSize - 1); nx++)
            {
                for (int ny = Mathf.Max(0, gy - 1); ny <= Mathf.Min(gy + 1, gridSize - 1); ny++)
                {
                    for (int nz = Mathf.Max(0, gz - 1); nz <= Mathf.Min(gz + 1, gridSize - 1); nz++)
                    {
                        // 检查该网格中的所有实体
                        foreach (int j in grid[nx, ny, nz])
                        {
                            if (i == j) continue;
                            
                            Vector3 direction = entities[j].position - pos;
                            float sqrDistance = direction.sqrMagnitude; // 避免开方
                            
                            if (sqrDistance < 0.01f) continue;
                            
                            // 重力公式: F = G * (m1 * m2) / d^2
                            float forceMagnitude = gravityConstant * entities[i].mass * entities[j].mass / sqrDistance;
                            
                            // 使用归一化的方向向量
                            totalForce += direction * (forceMagnitude / Mathf.Sqrt(sqrDistance));
                        }
                    }
                }
            }
            
            forces[i] = totalForce;
        }
        
        // 更新速度和位置
        for (int i = 0; i < entities.Count; i++)
        {
            if (!entities[i].active) continue;
            
            Entity entity = entities[i];
            
            // 更新速度和位置 (F = ma, 因此 a = F/m)
            Vector3 acceleration = forces[i] / entity.mass;
            entity.velocity += acceleration * deltaTime;
            entity.position += entity.velocity * deltaTime;
            
            entities[i] = entity;
        }
    }
    
    // Unity Job System 方法
    private void UpdateEntitiesJobSystem()
    {
        int count = entities.Count;
        
        // 创建NativeArrays来存储数据
        NativeArray<float3> positions = new NativeArray<float3>(count, Allocator.TempJob);
        NativeArray<float3> velocities = new NativeArray<float3>(count, Allocator.TempJob);
        NativeArray<float> masses = new NativeArray<float>(count, Allocator.TempJob);
        NativeArray<bool> active = new NativeArray<bool>(count, Allocator.TempJob);
        NativeArray<float3> forces = new NativeArray<float3>(count, Allocator.TempJob);
        
        // 填充数据
        for (int i = 0; i < count; i++)
        {
            positions[i] = entities[i].position;
            velocities[i] = entities[i].velocity;
            masses[i] = entities[i].mass;
            active[i] = entities[i].active;
        }
        
        // 创建计算力的作业
        ComputeForcesJob forceJob = new ComputeForcesJob
        {
            positions = positions,
            masses = masses,
            active = active,
            forces = forces,
            gravityConstant = 0.01f
        };
        
        // 创建和调度力计算作业
        JobHandle forceHandle = forceJob.Schedule();
        
        // 创建更新位置的作业
        UpdatePositionsJob positionJob = new UpdatePositionsJob
        {
            positions = positions,
            velocities = velocities,
            masses = masses,
            forces = forces,
            active = active,
            deltaTime = Time.deltaTime
        };
        
        // 在力计算作业完成后调度位置更新作业
        JobHandle positionHandle = positionJob.Schedule(forceHandle);
        
        // 等待作业完成
        positionHandle.Complete();
        
        // 更新主线程中的实体数据
        for (int i = 0; i < count; i++)
        {
            Entity entity = entities[i];
            entity.position = positions[i];
            entity.velocity = velocities[i];
            entities[i] = entity;
        }
        
        // 释放本机数组
        positions.Dispose();
        velocities.Dispose();
        masses.Dispose();
        active.Dispose();
        forces.Dispose();
    }
    
    // Parallel.For 方法
    private async void UpdateEntitiesParallel()
    {
        int count = entities.Count;
        Vector3[] forces = new Vector3[count];
        float deltaTime = Time.deltaTime;
        
        // 并行计算力
        await Task.Run(() => {
            Parallel.For(0, count, i => {
                if (!entities[i].active) return;
                
                Vector3 pos = entities[i].position;
                Vector3 totalForce = Vector3.zero;
                
                for (int j = 0; j < count; j++)
                {
                    if (i == j || !entities[j].active) continue;
                    
                    Vector3 direction = entities[j].position - pos;
                    float sqrDistance = direction.sqrMagnitude;
                    
                    if (sqrDistance < 0.01f) continue;
                    
                    float forceMagnitude = 0.01f * entities[i].mass * entities[j].mass / sqrDistance;
                    totalForce += direction.normalized * forceMagnitude;
                }
                
                forces[i] = totalForce;
            });
        });
        
        // 在主线程中更新位置
        for (int i = 0; i < count; i++)
        {
            if (!entities[i].active) continue;
            
            Entity entity = entities[i];
            
            Vector3 acceleration = forces[i] / entity.mass;
            entity.velocity += acceleration * deltaTime;
            entity.position += entity.velocity * deltaTime;
            
            entities[i] = entity;
        }
    }
    
    // Unity Job System 作业定义
    [BurstCompile]
    private struct ComputeForcesJob : IJob
    {
        [ReadOnly] public NativeArray<float3> positions;
        [ReadOnly] public NativeArray<float> masses;
        [ReadOnly] public NativeArray<bool> active;
        public NativeArray<float3> forces;
        public float gravityConstant;
        
        public void Execute()
        {
            int count = positions.Length;
            
            for (int i = 0; i < count; i++)
            {
                if (!active[i]) continue;
                
                float3 totalForce = float3.zero;
                float3 pos = positions[i];
                
                for (int j = 0; j < count; j++)
                {
                    if (i == j || !active[j]) continue;
                    
                    float3 direction = positions[j] - pos;
                    float sqrDistance = math.lengthsq(direction);
                    
                    if (sqrDistance < 0.01f) continue;
                    
                    float distance = math.sqrt(sqrDistance);
                    float forceMagnitude = gravityConstant * masses[i] * masses[j] / sqrDistance;
                    
                    totalForce += math.normalize(direction) * forceMagnitude;
                }
                
                forces[i] = totalForce;
            }
        }
    }
    
    [BurstCompile]
    private struct UpdatePositionsJob : IJob
    {
        public NativeArray<float3> positions;
        public NativeArray<float3> velocities;
        [ReadOnly] public NativeArray<float> masses;
        [ReadOnly] public NativeArray<float3> forces;
        [ReadOnly] public NativeArray<bool> active;
        public float deltaTime;
        
        public void Execute()
        {
            int count = positions.Length;
            
            for (int i = 0; i < count; i++)
            {
                if (!active[i]) continue;
                
                float3 acceleration = forces[i] / masses[i];
                float3 velocity = velocities[i] + acceleration * deltaTime;
                float3 position = positions[i] + velocity * deltaTime;
                
                velocities[i] = velocity;
                positions[i] = position;
            }
        }
    }
}

十五、结论与最佳实践

性能预算管理

using UnityEngine;
using System.Collections.Generic;
using Unity.Profiling;

public class PerformanceBudgetManager : MonoBehaviour
{
    [System.Serializable]
    public class BudgetEntry
    {
        public string name;
        public float budgetMs;
        public string profilerSampleName;
        [HideInInspector] public float current;
        [HideInInspector] public float peak;
        [HideInInspector] public float average;
        [HideInInspector] public int overBudgetCount;
    }
    
    [Header("Performance Budgets")]
    [SerializeField] private BudgetEntry[] budgets = new BudgetEntry[] 
    {
        new BudgetEntry { name = "CPU Total", budgetMs = 16.67f, profilerSampleName = "PlayerLoop" },
        new BudgetEntry { name = "Rendering", budgetMs = 8.0f, profilerSampleName = "Render" },
        new BudgetEntry { name = "Physics", budgetMs = 3.0f, profilerSampleName = "Physics.Processing" },
        new BudgetEntry { name = "Scripts", budgetMs = 4.0f, profilerSampleName = "Update.ScriptRunBehaviourUpdate" },
        new BudgetEntry { name = "Animation", budgetMs = 2.0f, profilerSampleName = "Animation.Update" }
    };
    
    [Header("Settings")]
    [SerializeField] private bool logWarnings = true;
    [SerializeField] private int framesBetweenChecks = 10;
    [SerializeField] private int warningThreshold = 5;
    [SerializeField] private bool enableAutoOptimizations = false;
    
    // 采样器
    private Dictionary<string, ProfilerRecorder> recorders = new Dictionary<string, ProfilerRecorder>();
    
    // 性能历史
    private Dictionary<string, Queue<float>> sampleHistory = new Dictionary<string, Queue<float>>();
    private const int historyLength = 100;
    
    private int frameCounter = 0;
    
    private void OnEnable()
    {
        // 创建Profiler记录器
        foreach (BudgetEntry budget in budgets)
        {
            string[] samplePath = budget.profilerSampleName.Split('.');
            ProfilerCategory category = ProfilerCategory.Scripts;
            string name = budget.profilerSampleName;
            
            // 根据路径推断类别
            if (samplePath.Length > 1)
            {
                switch (samplePath[0])
                {
                    case "Render":
                        category = ProfilerCategory.Render;
                        name = samplePath[1];
                        break;
                    case "Physics":
                        category = ProfilerCategory.Physics;
                        name = samplePath[1];
                        break;
                    case "Animation":
                        category = ProfilerCategory.Animation;
                        name = samplePath[1];
                        break;
                    // 添加更多类别...
                }
            }
            else if (name == "PlayerLoop")
            {
                category = ProfilerCategory.Internal;
            }
            
            ProfilerRecorder recorder = ProfilerRecorder.StartNew(category, name);
            recorders[budget.name] = recorder;
            
            // 初始化历史
            sampleHistory[budget.name] = new Queue<float>();
        }
    }
    
    private void OnDisable()
    {
        // 释放记录器
        foreach (ProfilerRecorder recorder in recorders.Values)
        {
            recorder.Dispose();
        }
        
        recorders.Clear();
    }
    
    private void Update()
    {
        frameCounter++;
        
        // 每隔几帧检查一次预算
        if (frameCounter % framesBetweenChecks != 0) return;
        
        CheckBudgets();
    }
    
    private void CheckBudgets()
    {
        bool performanceIssuesDetected = false;
        
        foreach (BudgetEntry budget in budgets)
        {
            if (recorders.TryGetValue(budget.name, out ProfilerRecorder recorder))
            {
                if (!recorder.Valid) continue;
                
                // 从纳秒转换为毫秒
                float milliseconds = recorder.LastValue / 1000000f;
                
                // 更新当前值
                budget.current = milliseconds;
                
                // 更新峰值
                budget.peak = Mathf.Max(budget.peak, milliseconds);
                
                // 添加到历史
                Queue<float> history = sampleHistory[budget.name];
                history.Enqueue(milliseconds);
                
                if (history.Count > historyLength)
                {
                    history.Dequeue();
                }
                
                // 计算平均值
                float sum = 0;
                foreach (float sample in history)
                {
                    sum += sample;
                }
                budget.average = sum / history.Count;
                
                // 检查是否超出预算
                if (milliseconds > budget.budgetMs)
                {
                    budget.overBudgetCount++;
                    
                    if (budget.overBudgetCount >= warningThreshold && logWarnings)
                    {
                        Debug.LogWarning($"Performance Budget '{budget.name}' exceeded: {milliseconds:F2}ms (Budget: {budget.budgetMs:F2}ms)");
                        performanceIssuesDetected = true;
                    }
                }
                else
                {
                    budget.overBudgetCount = Mathf.Max(0, budget.overBudgetCount - 1);
                }
            }
        }
        
        // 如果启用自动优化并检测到性能问题
        if (enableAutoOptimizations && performanceIssuesDetected)
        {
            ApplyAutomaticOptimizations();
        }
    }
    
    private void ApplyAutomaticOptimizations()
    {
        // 根据超出的预算应用相应的优化
        foreach (BudgetEntry budget in budgets)
        {
            if (budget.overBudgetCount >= warningThreshold)
            {
                switch (budget.name)
                {
                    case "Rendering":
                        OptimizeRendering(budget.current / budget.budgetMs);
                        break;
                        
                    case "Physics":
                        OptimizePhysics(budget.current / budget.budgetMs);
                        break;
                        
                    case "Scripts":
                        OptimizeScripts(budget.current / budget.budgetMs);
                        break;
                        
                    // 其他系统的优化...
                }
            }
        }
    }
    
    private void OptimizeRendering(float overBudgetFactor)
    {
        // 示例:根据性能问题的严重程度调整渲染质量
        QualitySettings.shadows = overBudgetFactor > 1.5f ? 
            ShadowQuality.Disable : 
            (overBudgetFactor > 1.2f ? ShadowQuality.HardOnly : ShadowQuality.All);
            
        // 调整LOD偏置
        float biasAdjustment = Mathf.Clamp(overBudgetFactor - 1.0f, 0f, 1f);
        QualitySettings.lodBias = Mathf.Max(0.3f, 1.0f - biasAdjustment);
        
        Debug.Log($"Auto-optimized rendering: LOD Bias = {QualitySettings.lodBias}, Shadows = {QualitySettings.shadows}");
    }
    
    private void OptimizePhysics(float overBudgetFactor)
    {
        // 示例:调整物理更新频率
        Time.fixedDeltaTime = Mathf.Clamp(0.02f * overBudgetFactor, 0.02f, 0.05f);
        
        Debug.Log($"Auto-optimized physics: FixedDeltaTime = {Time.fixedDeltaTime}");
    }
    
    private void OptimizeScripts(float overBudgetFactor)
    {
        // 通知游戏系统调整复杂性
        // 这通常需要自定义实现,因为它高度依赖于游戏
        
        // 示例:查找并调整AI管理器
        AIManager aiManager = FindObjectOfType<AIManager>();
        if (aiManager != null)
        {
            int targetPriority = overBudgetFactor > 1.5f ? 0 : (overBudgetFactor > 1.2f ? 1 : 2);
            aiManager.SetAIPriority(targetPriority);
            Debug.Log($"Auto-optimized AI: Priority = {targetPriority}");
        }
    }
    
    // 提供外部访问性能数据的方法
    public Dictionary<string, BudgetEntry> GetBudgetReport()
    {
        Dictionary<string, BudgetEntry> report = new Dictionary<string, BudgetEntry>();
        
        foreach (BudgetEntry budget in budgets)
        {
            report[budget.name] = budget;
        }
        
        return report;
    }
    
    // 模拟AI管理器
    private class AIManager : MonoBehaviour
    {
        public void SetAIPriority(int priority)
        {
            // 0 = 低, 1 = 中, 2 = 高
            // 实际实现...
        }
    }
}

Unity性能优化最佳实践总结

using UnityEngine;

public class PerformanceBestPracticesGuide : MonoBehaviour
{
    [Header("Unity Profiling Best Practices")]
    [TextArea(5, 10)]
    public string profilingBestPractices = 
    @"1. 始终在目标平台上进行性能分析,而不仅仅是在编辑器中
    2. 使用Development Build并启用Autoconnect Profiler进行设备分析
    3. 先专注于主要瓶颈,不要过早优化小问题
    4. 建立性能预算,并持续监控是否符合预期
    5. 使用标记(markers)和自定义采样来精确定位性能问题
    6. 比较优化前后的性能数据,确保改进有效";
    
    [Header("CPU Optimization Best Practices")]
    [TextArea(5, 10)]
    public string cpuBestPractices =
    @"1. 避免在Update中进行复杂计算,考虑将工作分散到多个帧中
    2. 使用对象池而不是频繁的实例化和销毁
    3. 最小化GC开销:
       - 避免在频繁调用的方法中分配临时对象
       - 重用集合而非每次创建新的
       - 使用struct代替class来减少堆分配
    4. 利用缓存来避免重复计算
    5. 使用Unity Job System或Parallel.For进行并行计算
    6. 实现LOD系统降低远处对象的更新频率
    7. 使用协程分散计算密集型任务";

    [Header("Memory Optimization Best Practices")]
    [TextArea(5, 10)]
    public string memoryBestPractices =
    @"1. 使用内存分析器定期检查内存使用情况
    2. 注意静态集合的使用,它们可能导致内存泄漏
    3. 防止循环引用导致的泄漏
    4. 正确取消事件订阅
    5. 适当管理资源加载和卸载:
       - 使用Resources.UnloadUnusedAssets()清理未使用资源
       - 通过Addressables或AssetBundles管理大型资源
    6. 监控纹理和网格内存使用
    7. 实现适当的场景转换清理流程";

    [Header("Rendering Optimization Best Practices")]
    [TextArea(5, 10)]
    public string renderingBestPractices =
    @"1. 减少DrawCall:
       - 使用静态批处理合并静态对象
       - 为相似对象启用GPU实例化
       - 使用图集合并纹理
    2. 简化着色器和材质:
       - 避免过度复杂的像素着色器
       - 精简使用的材质和纹理变体
    3. 使用遮挡剔除减少渲染对象
    4. 优化光照:
       - 使用烘焙光照代替实时光照
       - 限制实时光源的数量和范围
    5. 谨慎使用后期处理效果
    6. 根据目标设备选择合适的渲染路径";

    [Header("Physics Optimization Best Practices")]
    [TextArea(5, 10)]
    public string physicsBestPractices =
    @"1. 使用简单碰撞体(盒体、球体、胶囊体)代替复杂的Mesh碰撞体
    2. 合理设置碰撞层以减少不必要的碰撞检测
    3. 使用触发器代替碰撞体来进行简单的检测
    4. 对远处或不活跃的对象禁用物理组件
    5. 避免刚体堆叠
    6. 根据需要调整Time.fixedDeltaTime
    7. 使用连续碰撞检测仅适用于高速移动的对象";

    [Header("UI Optimization Best Practices")]
    [TextArea(5, 10)]
    public string uiBestPractices =
    @"1. 减少Canvas重建:
       - 避免频繁更改Text组件内容
       - 使用Canvas.enableBatchingForUI选项
    2. 确保UI元素不重叠/不交叉布局组
    3. 禁用不可见UI元素而非仅设置Alpha为0
    4. 使用UI元素对象池处理列表和重复元素
    5. 为复杂UI结构使用多个Canvas
    6. 谨慎使用Layout组件,它们可能导致级联重建";

    [Header("Asset Optimization Best Practices")]
    [TextArea(5, 10)]
    public string assetBestPractices =
    @"1. 纹理优化:
       - 使用适当的压缩格式
       - 根据实际需求设置分辨率
       - 为移动平台启用mipmap
    2. 模型优化:
       - 减少顶点数量和骨骼数量
       - 移除不可见的多边形
       - 优化UV映射
    3. 音频优化:
       - 为背景音乐使用压缩格式
       - 为短音效使用未压缩格式
       - 谨慎使用3D音频源
    4. 使用Addressables系统管理资源加载
    5. 实现资产捆绑策略最小化内存占用";

    [Header("Tools and Workflow Best Practices")]
    [TextArea(5, 10)]
    public string workflowBestPractices =
    @"1. 在开发周期早期建立性能测试和监控流程
    2. 使用自动化性能测试检测性能退化
    3. 为主要功能设定明确的性能预算
    4. 建立持续集成性能检查
    5. 记录优化结果并分享最佳实践
    6. 针对不同平台维护配置文件
    7. 定期进行完整的性能分析";
    
    void Start()
    {
        Debug.Log("这个脚本包含Unity性能优化和Profiling的最佳实践总结。请在Inspector中查看详细内容。");
    }
}

这份Unity引擎Profiling工具使用技术总结涵盖了Unity性能分析的各个方面,从基础工具使用到高级优化技术。通过这些代码示例和最佳实践,开发者可以全面掌握Unity性能分析工具的使用方法,优化游戏性能,提供更好的用户体验。

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐