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

一 、事故描述

我有一个需求:在工程文件中找得到所有的图片(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-Transform类-父子关系
java·unity·游戏引擎
躺下睡觉~7 小时前
Unity-Transform类-缩放和看向
unity·游戏引擎
君莫愁。9 小时前
【Unity】检测鼠标点击位置是否有2D对象
unity·c#·游戏引擎
咩咩觉主10 小时前
Unity实战案例全解析:PVZ 植物卡片状态分析
unity·c#·游戏引擎
蓝裕安13 小时前
伪工厂模式制造敌人
开发语言·unity·游戏引擎
谢泽浩17 小时前
Unity 给模型贴上照片
unity·游戏引擎
z2014z17 小时前
Unity Resource System 优化笔记
unity·游戏引擎
王维志17 小时前
Unity 高亮插件HighlightPlus介绍
unity·游戏引擎
zaizai100718 小时前
我的demo保卫萝卜中的技术要点
unity
菌菌巧乐兹19 小时前
Unity 百度AI实现无绿幕拍照抠像功能(详解版)
人工智能·百度·unity