一次【自定义编辑器功能脚本】【调用时内存爆仓】事故排查

一 、事故描述

我有一个需求:在工程文件中找得到所有的图片(Texture 2D),然后把WebGL发布打包时的图片压缩规则进行修改。

项目中有图片2千多张,其中2k分辨率的图片上百张,当我右键进行批量处理的时候,内存蹭蹭蹭往上冒,直接吃满,然后显示Unity挂掉,接着其他程序也会挂,最后电脑卡住...

二、原因分析

当我全选整个项目中的图片,然后右键菜单执行该脚本后,它加载图片的代码如下:

csharp 复制代码
 Object[] textures = Selection.GetFiltered(typeof(Texture2D), SelectionMode.DeepAssets);

这个数组会爆仓内存。

其次:每张图单独处理的时候,我new了两个东西,如下

new的第一个组件

csharp 复制代码
// 创建特定平台压缩实例
TextureImporterPlatformSettings platformSettings = new TextureImporterPlatformSettings();
//设置属性

new的第二个组件

csharp 复制代码
//设置importSettings
TextureImporter importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)) as TextureImporter;
//设置属性

这两个组件是在一个for循环里面,用完之后,没有设置为null。

三、换种思路

  • 1、只抓取资源id
csharp 复制代码
string[] guids = AssetDatabase.FindAssets("t:texture2d");
  • 2、用id取获取图片对象
csharp 复制代码
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
  • 3、new 出来的组件记得清空
csharp 复制代码
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path) ;
TextureImporterPlatformSettings platformSettings = new TextureImporterPlatformSettings();
TextureImporter importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)) as TextureImporter;

//属性赋值
//.....

//-----------------清空对象-----------------至于什么时候触发GC,那就不知道了
platformSettings = null;
importer = null;
texture = null;
  • 3、图片上的组件用完及时清空
csharp 复制代码
  static void FindAllTexture2D()
    {

        //****************************************参数设置区**********begin
        //TODO 做成EditWindow类型

        TextureImporterFormat format = TextureImporterFormat.ASTC_12x12; //图片压缩格式
        int compressionQuality = 60; //压缩比例
        string platform = "WebGL"; //发布的平台 

        //************************************************************end

        //查找工程文件中的所有精灵图片
        string[] guids = AssetDatabase.FindAssets("t:texture2d");
        Debug.Log($"Found {guids.Length} Texture2d assets.");
        foreach (string guid in guids)
        {
            try
            {
                string path = AssetDatabase.GUIDToAssetPath(guid);

                Debug.Log($"{path}");
                // 使用AssetDatabase加载Texture2D
                Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
                //Debug.Log($"{texture.name}");

                if (texture == null) continue;

                // 创建特定平台压缩实例
                TextureImporterPlatformSettings platformSettings = new TextureImporterPlatformSettings();
                platformSettings.overridden = true;
                platformSettings.name = platform;

                // 设置为压缩
                platformSettings.textureCompression = TextureImporterCompression.Compressed;

                // 设置压缩格式
                platformSettings.format = format; //TextureImporterFormat.ASTC_12x12;
                platformSettings.compressionQuality = compressionQuality; //40
                platformSettings.maxTextureSize = GetMaxSize(texture as Texture2D); //32

                //设置importSettings
                TextureImporter importer =
                    AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)) as TextureImporter;
                if (importer == null) continue;

                importer.SetPlatformTextureSettings(platformSettings);

                //Apply 设置
                importer.SetPlatformTextureSettings(platformSettings);

                //保存资源
                importer.SaveAndReimport();

                //资源清空
                platformSettings = null;
                importer = null;
                texture = null;
            }
            catch (Exception ex)
            {
                Debug.Log( $" ~~~~~error~~~~~ 设置报错:{ex.Message}");
            }
        }
    }

四、附录:出错的脚本

csharp 复制代码
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;

public class SetTextureCompression
{
    //****************************************参数设置区**********begin
    //TODO 做成EditWindow类型

    private static TextureImporterFormat format = TextureImporterFormat.ASTC_12x12;  //图片压缩格式
    private static int compressionQuality = 60;                                      //压缩比例
    private static string platform = "WebGL";                                        //发布的平台 

    //************************************************************end

    /// <summary>
    /// 设置贴图在build时的压缩选项
    /// </summary>
    [MenuItem("Assets/设置发布WebGL时贴图的压缩格式")]
    static void SetCompression()
    {
        int count = 0;

        Object[] textures = Selection.GetFiltered(typeof(Texture2D), SelectionMode.DeepAssets);
        if (textures.Length > 0)
        {
            foreach (Object texture in textures)
            {
                // 创建特定平台压缩实例
                TextureImporterPlatformSettings platformSettings = new TextureImporterPlatformSettings();
                platformSettings.overridden = true;
                platformSettings.name = platform;

                // 设置为压缩
                platformSettings.textureCompression = TextureImporterCompression.Compressed;

                // 设置压缩格式
                platformSettings.format = format;                                    
                platformSettings.compressionQuality = compressionQuality;             
                platformSettings.maxTextureSize = GetMaxSize(texture as Texture2D);   

                //设置importSettings
                TextureImporter importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)) as TextureImporter;
                importer.SetPlatformTextureSettings(platformSettings);

                //Apply 设置
                importer.SetPlatformTextureSettings(platformSettings);

                //保存资源
                importer.SaveAndReimport();

                count++;
            }
            //Debug.Log("Texture Compression Set!");
        }
        else
        {
            Debug.LogWarning("没有选中图片!");
        }
        Debug.Log($"一共处理了{count}张图片!");
    }

    /// <summary>
    /// 获取图片的分辨率,取分辨率中高宽的最大值,然后返回图片的【MaxSize】
    /// MaxSize的定义:assets->Image->【Texture2D ImportSettings】->【Override For WebGL】->【Max Size】 
    /// 区间:16,32,64,128,256,512,1024,2048,4096,8192,16384
    ///
    /// 举例:图片分辨率 = 12 * 24,那么图片的MaxSize = 32
    /// </summary>
    /// <param name="texture"></param>
    /// <returns></returns>
    static int GetMaxSize(Texture2D texture)
    {
        //分辨率区间的预备
        var start = new List<int> { 0, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384 };
        var end = new List<int> { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 100000 };
        var zones = start.Zip(end, (item1, item2) => (startIdx: item1, endIdx: item2)).ToList();

        //取分辨率高宽的最大值
        var size = new List<int> { texture.width, texture.height }.Max();  //取【宽】【高】中的最大值

        //判断所属的区间
        var maxSize = zones
            .First(x => x.startIdx <= size && size <= x.endIdx)
            .endIdx;
        //Debug.Log($"图的分辨率 = {texture.width} * {texture.height} size = {size}, MaxSize = {maxSize}");
        return maxSize;
    }
}
相关推荐
张老师带你学6 小时前
unity TerrainSampleAssets
科技·游戏·unity·游戏引擎·模型
RReality9 小时前
【Unity Shader URP】色带渐变着色(Ramp Shading)实战教程
ui·unity·游戏引擎·图形渲染
mxwin17 小时前
Unity URP 体积光与雾效 基于深度重建世界空间位置,实现体积雾与体积光
unity·游戏引擎
张老师带你学19 小时前
unity 树资源 有樱花树 buildin
科技·游戏·unity·游戏引擎·模型
魔士于安19 小时前
unity 植物 不常见 花 触手植物
游戏·unity·游戏引擎·贴图·模型
魔士于安20 小时前
unity=>传送门特效 带自由视角旋转放大 鼠标操作
前端·游戏·unity·游戏引擎·贴图·模型
南無忘码至尊21 小时前
Unity学习90天 - 第4天 - 认识物理系统基础并实现物体碰撞反弹
学习·unity·游戏引擎
南無忘码至尊21 小时前
Unity学习90天 - 第4天 - 学习预制体 Prefab + 实例化并实现按鼠标生成子弹
学习·unity·游戏引擎
魔士于安1 天前
Unity资源Toon City Pack 发电厂 工地 公园 地铁站口 银行 车 直升飞机 可动 URP
游戏·unity·游戏引擎·贴图·模型
心前阳光2 天前
Unity之运行时标准材质半透明无效果
unity·游戏引擎·材质