LoginSignup
2
2

[Unity] [Editor拡張] Crunch圧縮を一括で適用するEditorWindowの作成方法

Last updated at Posted at 2024-03-30

四回目の投稿です。よろしくお願いします。

目次

  1. 概要
  2. 使用方法
  3. 作業手順
  4. 実装方法
  5. 最後に

概要

以下のような、指定した圧縮率でテクスチャをCrunch圧縮するEditorWindowの作成方法を解説します :
image.png

Crunch圧縮の設定は、個々のテクスチャのインスペクター上でも可能です :
image.png

Crunch圧縮について
Crunch圧縮は圧縮形式のことで、解凍が早く、ロード時間にほとんど影響を与えないため、アプリ容量が削減できるということで、公式が積極的な使用を推奨しています。

使用方法

まずは、外観を把握するという意味でも、使用方法を説明します。作成方法に興味のない方は、ここを読むことで使用できるようになっています。

1. 下記スクリプトを作成し、"Editor"フォルダ内に入れる :
TextureCrunchCompressTool.cs (tap)
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class TextureCrunchCompressTool : EditorWindow
{
    private const int MinCompressionRate = 0;
    private const int MaxCompressionRate = 100;
    
    private static readonly string[] TextureExtensions = new string[] { ".png", ".jpg" }; // 対象としたい画像の拡張子

    private readonly List<Texture> compressedTextures = new List<Texture>(); // 圧縮した画像たち
    private Vector2 scrollValueForDrawCrunchCompressedGUI;
    [Range(MinCompressionRate, MaxCompressionRate)]
    private int currentCompressionRate = 50;
    private bool isCompressed;
    private bool isOpenFoldoutForDrawCrunchCompressedGUI;

    [MenuItem("Tools/Texture Crunch Compress Tool")]
    public static void OpenEditorWindow()
    {
        GetWindow<TextureCrunchCompressTool>("テクスチャの圧縮ツール");
    }

    private void OnEnable()
    {
        Initialize();
    }

    /// <summary>
    /// 初期化処理
    /// </summary>
    private void Initialize()
    {
        compressedTextures.Clear();
        scrollValueForDrawCrunchCompressedGUI = Vector2.zero;
        isCompressed = false;
        isOpenFoldoutForDrawCrunchCompressedGUI = false;
    }

    private void OnGUI()
    {
        DrawCrunchCompressionSettingGUI();

        GUILayout.Space(5);

        if (isCompressed) // 圧縮を実行するまで表示しない
        {
            DrawCrunchCompressedGUI();
        }
    }

    /// <summary>
    /// 圧縮に関する設定画面の描画
    /// </summary>
    private void DrawCrunchCompressionSettingGUI()
    {
        GUILayout.Label("[概要]\n" +
                        "テクスチャを指定した圧縮率に圧縮します。");

        GUILayout.Space(5);

        currentCompressionRate = EditorGUILayout.IntSlider("圧縮率 : ", currentCompressionRate, MinCompressionRate, MaxCompressionRate);

        GUILayout.Space(5);

        if (GUILayout.Button("Crunch圧縮を実行"))
        {
            Initialize();
            ExecuteCrunchCompress();
            isCompressed = true;
        }
    }

    /// <summary>
    /// Crunch圧縮を実行
    /// </summary>
    private void ExecuteCrunchCompress()
    {
        var paths = GetAllTexturePath("Assets"); // "Assets"下にある画像をすべて取得
        foreach (var path in paths)
        {
            var impoter = (TextureImporter)AssetImporter.GetAtPath(path); // 画像設定を変更するためにAssetImporterを使用
            if (impoter.crunchedCompression && impoter.compressionQuality == currentCompressionRate) { continue; } // 未圧縮なら、圧縮する

            var texture = (Texture)AssetDatabase.LoadAssetAtPath(path, typeof(Texture)); // テクスチャの取得
            compressedTextures.Add(texture);

            impoter.crunchedCompression = true;                  // use crunch compressを有効化
            impoter.compressionQuality = currentCompressionRate; // 圧縮率の設定
            AssetDatabase.ImportAsset(path);                     // 変更を適用
        }
    }

    /// <summary>
    /// 特定のフォルダ下にあるTextureのパスをすべて取得 (検索 -> guid取得 -> パス取得)
    /// </summary>
    private static List<string> GetAllTexturePath(string rootFolder)
    {
        var guids = AssetDatabase.FindAssets("t:Texture", new string[] { rootFolder }); // "rootFolder"フォルダ下の全てのTextureのGUID
        var assetPaths = new List<string>();
        foreach (var guid in guids)
        {
            var path = AssetDatabase.GUIDToAssetPath(guid); // guid -> path
            var isSubject = false;
            foreach (var extension in TextureExtensions)
            {
                if (!path.Contains(extension)) { continue; } // 画像の拡張子が、対象となるものであるか?

                isSubject = true;
            }
            if (!isSubject) { continue; }

            assetPaths.Add(path);
        }
        return assetPaths;
    }

    /// <summary>
    /// 圧縮した画像の一覧表示画面の描画
    /// </summary>
    private void DrawCrunchCompressedGUI()
    {
        if (compressedTextures.Count < 1)
        {
            GUILayout.Label("圧縮対象は存在しませんでした。");
            return;
        }
        GUILayout.Label($"以下の対象に圧縮処理({currentCompressionRate}%)が適用されました : ");

        isOpenFoldoutForDrawCrunchCompressedGUI = EditorGUILayout.Foldout(isOpenFoldoutForDrawCrunchCompressedGUI, $"対象一覧 [{compressedTextures.Count}]");
        if (isOpenFoldoutForDrawCrunchCompressedGUI) // 折り畳みが開かれているなら、
        {
            scrollValueForDrawCrunchCompressedGUI = EditorGUILayout.BeginScrollView(scrollValueForDrawCrunchCompressedGUI);
            foreach (var compressedTexture in compressedTextures) // すべての圧縮した画像に対し、
            {
                EditorGUILayout.ObjectField(compressedTexture, typeof(Texture), false); // 表示
            }
            EditorGUILayout.EndScrollView();
        }
    }
}
#endif

Editorフォルダについて
Unityにはフォルダ名によって識別される特殊なフォルダが存在する。その内の一つにEditorフォルダがある。そして、エディタースクリプトを"Editor"と名付けたフォルダ内に配置しなければ、それは動作しないのである。
(エディタースクリプト : UnityEditor名前空間に存在するクラス(EditorやEditorWindow、PropertyDrawerなど)を継承したスクリプト)

2. Unityエディタ上で"Tools/Texture Crunch Compress Tool"を選択する :

image.png

3. すると、下記EditorWindowが出現するので、圧縮率を指定し、"Crunch圧縮を実行"ボタンを押すと、Assets下にあるすべてのテクスチャに対して圧縮が実行される :

image.png

作業手順

1. Unityエディタ上からEditorWindowを開けるように実装

image.png

2. 圧縮実行に必要な最低限の描画処理の実装

image.png
(この時点では、まだ圧縮処理は実装されていない)

3. Crunch圧縮処理の実装

4. Crunch圧縮したテクスチャ表示の描画処理の実装

image.png

実装方法

1. Unityエディタ上からEditorWindowを開けるように実装

image.png

以下のように、TextureCrunchCompressTool.csを作成し、Editorフォルダ内に配置する :

TextureCrunchCompressTool.cs (tap)
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class TextureCrunchCompressTool : EditorWindow
{
    [MenuItem("Tools/Texture Crunch Compress Tool")]
    public static void OpenEditorWindow()
    {
        GetWindow<TextureCrunchCompressTool>("テクスチャの圧縮ツール");
    }
    
    private void OnGUI() // 描画処理
    {
        
    }
}
#endif

ポイント:

  • #if UNITY_EDITOR : Unityエディタ上でしか動作しないようにしている。
  • MenuItem属性 : Unityエディタ上のToolsに項目を追加している。
  • GetWindow() : EditorWindowを開けるようにしている。

2. 圧縮実行に必要な最低限の描画処理の実装

image.png

以下のように、TextureCrunchCompressTool.csに追記する :

TextureCrunchCompressTool.cs (tap)
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class TextureCrunchCompressTool : EditorWindow
{
+   private const int MinCompressionRate = 0;
+   private const int MaxCompressionRate = 100;
    
+   [Range(MinCompressionRate, MaxCompressionRate)]
+   private int currentCompressionRate = 50; // 設定中の圧縮率

    [MenuItem("Tools/Texture Crunch Compress Tool")]
    public static void OpenEditorWindow()
    {
        GetWindow<TextureCrunchCompressTool>("テクスチャの圧縮ツール");
    }
    
    private void OnGUI() // 描画処理
    {
+       DrawCrunchCompressionSettingGUI();
    }

+   /// <summary>
+   /// 圧縮に関する設定画面の描画
+   /// </summary>
+   private void DrawCrunchCompressionSettingGUI()
+   {
+       GUILayout.Label("[概要]\n" +
+                       "テクスチャを指定した圧縮率に圧縮します。");
+
+       GUILayout.Space(5);
+
+       currentCompressionRate = EditorGUILayout.IntSlider("圧縮率 : ", currentCompressionRate, MinCompressionRate, MaxCompressionRate);
+       
+       GUILayout.Space(5);
+
+       if (GUILayout.Button("Crunch圧縮を実行"))
+       {
+           // Crunch圧縮を実行
+       }
+   }
}
#endif

ポイント:

  • Range属性 : currentCompressionRateの範囲を制限
  • EditorGUILayout.IntSlider() : 圧縮率を設定するスライダーを実装
  • GUILayout.Button() : 圧縮処理を実行できるようにしている。

3. Crunch圧縮処理の実装

以下のように、TextureCrunchCompressTool.csに追記する :

TextureCrunchCompressTool.cs (tap)
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class TextureCrunchCompressTool : EditorWindow
{
    private const int MinCompressionRate = 0;
    private const int MaxCompressionRate = 100;
+   private static readonly string[] TextureExtensions = new string[] { ".png", ".jpg" }; // 対象としたい画像の拡張子
    
    [Range(MinCompressionRate, MaxCompressionRate)]
    private int currentCompressionRate = 50; // 設定中の圧縮率

    [MenuItem("Tools/Texture Crunch Compress Tool")]
    public static void OpenEditorWindow()
    {
        GetWindow<TextureCrunchCompressTool>("テクスチャの圧縮ツール");
    }
    
    private void OnGUI() // 描画処理
    {
        DrawCrunchCompressionSettingGUI();
    }

    /// <summary>
    /// 圧縮に関する設定画面の描画
    /// </summary>
    private void DrawCrunchCompressionSettingGUI()
    {
        GUILayout.Label("[概要]\n" +
                        "テクスチャを指定した圧縮率に圧縮します。");
 
        GUILayout.Space(5);
 
        currentCompressionRate = EditorGUILayout.IntSlider("圧縮率 : ", currentCompressionRate, MinCompressionRate, MaxCompressionRate);
      
        GUILayout.Space(5);
 
        if (GUILayout.Button("Crunch圧縮を実行"))
        {
+           ExecuteCrunchCompress();
        }
    }

+   /// <summary>
+   /// Crunch圧縮を実行
+   /// </summary>
+   private void ExecuteCrunchCompress()
+   {
+       var paths = GetAllTexturePath("Assets"); // "Assets"下にある画像をすべて取得
+       foreach (var path in paths)
+       {
+           var impoter = (TextureImporter)AssetImporter.GetAtPath(path); // 画像設定を変更するためにAssetImporterを使用
+           if (impoter.crunchedCompression && impoter.compressionQuality == currentCompressionRate) { continue; } // 未圧縮なら、圧縮する
+
+           impoter.crunchedCompression = true;                  // use crunch compressを有効化
+           impoter.compressionQuality = currentCompressionRate; // 圧縮率の設定
+           AssetDatabase.ImportAsset(path);                     // 変更を適用
+       }
+   }
+
+   /// <summary>
+   /// 特定のフォルダ下にあるTextureのパスをすべて取得 (検索 -> guid取得 -> パス取得)
+   /// </summary>
+   private static List<string> GetAllTexturePath(string rootFolder)
+   {
+       var guids = AssetDatabase.FindAssets("t:Texture", new string[] { rootFolder }); // "rootFolder"フォルダ下の全てのTextureのGUID
+       var assetPaths = new List<string>();
+       foreach (var guid in guids)
+       {
+           var path = AssetDatabase.GUIDToAssetPath(guid); // guid -> path
+           var isSubject = false;
+           foreach (var extension in TextureExtensions)
+           {
+               if (!path.Contains(extension)) { continue; } // 画像の拡張子が、対象となるものであるか?
+
+               isSubject = true;
+           }
+           if (!isSubject) { continue; }
+
+           assetPaths.Add(path);
+       }
+       return assetPaths;
+   }
}
#endif

ポイント:

  • GetAllTexturePath("Assets") : Assets下に存在するすべてのテクスチャのパスを取得している。(例えば、引数を"Assets/Resources/Texture"にすると、そのフォルダより下にあるものにしか適用されなくなる)
  • AssetDatabase.FindAssets() : filterを"t:Texture"とすることで、すべてのテスクチャを検索している。(Unityエディタ上のProjectウィンドウの検索欄に"t:Texture"と入力すると、すべてのテクスチャが表示される)
  • TextureExtensions変数 : 拡張子を用いて適用範囲を制限している。
  • アセットインポーターを用いることで、圧縮の設定を変更できるようにしている。

"Crunch圧縮を実行"ボタンを押し、適当なテクスチャを選択して、インスペクターを確認し、以下のようになっていれば成功 :
image.png

この時点で、テクスチャの圧縮の実装は完了しているが、利便性向上のため、圧縮が適用されたテクスチャを表示する描画処理を、以下で実装する。

4. Crunch圧縮したテクスチャ表示の描画処理の実装

image.png
以下のように、TextureCrunchCompressTool.csに追記する :

TextureCrunchCompressTool.cs (tap)
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class TextureCrunchCompressTool : EditorWindow
{
    private const int MinCompressionRate = 0;
    private const int MaxCompressionRate = 100;
    private static readonly string[] TextureExtensions = new string[] { ".png", ".jpg" }; // 対象としたい画像の拡張子

+   private readonly List<Texture> compressedTextures = new List<Texture>(); // 圧縮した画像たち
+   private Vector2 scrollValueForDrawCrunchCompressedGUI;
    [Range(MinCompressionRate, MaxCompressionRate)]
    private int currentCompressionRate = 50; // 設定中の圧縮率
+   private bool isCompressed;
+   private bool isOpenFoldoutForDrawCrunchCompressedGUI;

    [MenuItem("Tools/Texture Crunch Compress Tool")]
    public static void OpenEditorWindow()
    {
        GetWindow<TextureCrunchCompressTool>("テクスチャの圧縮ツール");
    }

+   private void OnEnable()
+   {
+       Initialize();
+   }
+
+   /// <summary>
+   /// 初期化処理
+   /// </summary>
+   private void Initialize()
+   {
+       compressedTextures.Clear();
+       scrollValueForDrawCrunchCompressedGUI = Vector2.zero;
+       isCompressed = false;
+       isOpenFoldoutForDrawCrunchCompressedGUI = false;
+   }
    
    private void OnGUI() // 描画処理
    {
        DrawCrunchCompressionSettingGUI();
+
+       GUILayout.Space(5);
+
+       if (isCompressed) // 圧縮を実行するまで表示しない
+       {
+           DrawCrunchCompressedGUI();
+       }
    }

    /// <summary>
    /// 圧縮に関する設定画面の描画
    /// </summary>
    private void DrawCrunchCompressionSettingGUI()
    {
        GUILayout.Label("[概要]\n" +
                        "テクスチャを指定した圧縮率に圧縮します。");
 
        GUILayout.Space(5);
 
        currentCompressionRate = EditorGUILayout.IntSlider("圧縮率 : ", currentCompressionRate, MinCompressionRate, MaxCompressionRate);
      
        GUILayout.Space(5);
 
        if (GUILayout.Button("Crunch圧縮を実行"))
        {
+           Initialize();
            ExecuteCrunchCompress();
+           isCompressed = true;
        }
    }

    /// <summary>
    /// Crunch圧縮を実行
    /// </summary>
    private void ExecuteCrunchCompress()
    {
        var paths = GetAllTexturePath("Assets"); // "Assets"下にある画像をすべて取得
        foreach (var path in paths)
        {
            var impoter = (TextureImporter)AssetImporter.GetAtPath(path); // 画像設定を変更するためにAssetImporterを使用
            if (impoter.crunchedCompression && impoter.compressionQuality == currentCompressionRate) { continue; } // 未圧縮なら、圧縮する
 
            impoter.crunchedCompression = true;                  // use crunch compressを有効化
            impoter.compressionQuality = currentCompressionRate; // 圧縮率の設定
            AssetDatabase.ImportAsset(path);                     // 変更を適用
        }
    }
 
    /// <summary>
    /// 特定のフォルダ下にあるTextureのパスをすべて取得 (検索 -> guid取得 -> パス取得)
    /// </summary>
    private static List<string> GetAllTexturePath(string rootFolder)
    {
        var guids = AssetDatabase.FindAssets("t:Texture", new string[] { rootFolder }); // "rootFolder"フォルダ下の全てのTextureのGUID
        var assetPaths = new List<string>();
        foreach (var guid in guids)
        {
            var path = AssetDatabase.GUIDToAssetPath(guid); // guid -> path
            var isSubject = false;
            foreach (var extension in TextureExtensions)
            {
                if (!path.Contains(extension)) { continue; } // 画像の拡張子が、対象となるものであるか?
 
                isSubject = true;
            }
            if (!isSubject) { continue; }
 
            assetPaths.Add(path);
        }
        return assetPaths;
    }
+
+   /// <summary>
+   /// 圧縮した画像の一覧表示画面の描画
+   /// </summary>
+   private void DrawCrunchCompressedGUI()
+   {
+       if (compressedTextures.Count < 1)
+       {
+           GUILayout.Label("圧縮対象は存在しませんでした。");
+           return;
+       }
+       GUILayout.Label($"以下の対象に圧縮処理({currentCompressionRate}%)が適用されました : ");
+
+       isOpenFoldoutForDrawCrunchCompressedGUI = EditorGUILayout.Foldout(isOpenFoldoutForDrawCrunchCompressedGUI, $"対象一覧 [{compressedTextures.Count}]");
+       if (isOpenFoldoutForDrawCrunchCompressedGUI) // 折り畳みが開かれているなら、
+       {
+           scrollValueForDrawCrunchCompressedGUI = EditorGUILayout.BeginScrollView(scrollValueForDrawCrunchCompressedGUI);
+           foreach (var compressedTexture in compressedTextures) // すべての圧縮した画像に対し、
+           {
+               EditorGUILayout.ObjectField(compressedTexture, typeof(Texture), false); // 表示
+           }
+           EditorGUILayout.EndScrollView();
+       }
+   }
}
#endif

ポイント:

  • isCompressed変数 : 圧縮処理が実行されるまで、結果を表示しないようにしている。
  • EditorGUILayout.Foldout() : 対象一覧の表示を折りたためるようにしている。
  • EditorGUILayout.BeginScrollView()とEditorGUILayout.EndScrollView() : 対象一覧をスクロールできるようにしている。
  • EditorGUILayout.ObjectField() : 対象を表示している。

最後に

どうでしたか、わかりやすかったでしょうか。Crunch圧縮の設定自体は、個々のテクスチャのインスペクター上で設定できるが、個数が多いとかなり面倒なので、こういったエディタ拡張を作成できる能力は有用です。特に、ベイク時に生成されるライトマップは、基本的に個数が多いので、一つずつ適用していくのは骨が折れます。
また気が向いたら何か書きます。それでは。

宣伝 : CUPLEXという時間差アクションパズルゲームを作成しています。私は主にプログラムとプロジェクト管理を担当しています。よかったら、覗いてみてください↓
https://store.steampowered.com/app/2499100/CUPLEX/

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2