【Unity】選択したテクスチャのインポート設定を自動で変更するスクリプト

投稿者: | 2023-06-08

ChatGPTを活用して、テクスチャのインポート設定を自動で変更するスクリプトを作りました。

インポート時に通知するメソッドもありますが、今回は選択されたテクスチャのみを対象にしました。

今回は、接尾辞によってテクスチャタイプとsRGBの値を変更し、テクスチャサイズに合わせてMax Sizeを変更してみます。

スクリプト

using System.Linq;
using UnityEditor;
using UnityEngine;

public class TextureSettingsEditor : ScriptableObject
{
    [MenuItem("Tools/Adjust Texture Settings")]
    public static void AdjustTextureSettings()
    {
        var textures = Selection.GetFiltered(typeof(Texture2D), SelectionMode.DeepAssets).Cast<Texture2D>().ToList();

        if (textures.Count == 0)
        {
            Debug.LogError("No texture selected. Please select texture(s) before running the script.");
            return;
        }

        foreach (var texture in textures)
        {
            string path = AssetDatabase.GetAssetPath(texture);
            TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;

            if (importer == null)
            {
                Debug.LogError("Failed to get TextureImporter for texture: " + texture.name);
                continue;
            }

            // Get the original size
            var originalMaxSize = importer.maxTextureSize;
            importer.maxTextureSize = 16384;
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
            int width = texture.width;
            int height = texture.height;
            int maxSize = Mathf.Max(width, height);

            // Process texture name
            string lowerCaseName = texture.name.ToLowerInvariant();
            if (lowerCaseName.Contains("_normal"))
            {
                importer.textureType = TextureImporterType.NormalMap;
            }
            else if (lowerCaseName.Contains("maskmap") || lowerCaseName.Contains("detailmap"))
            {
                importer.textureType = TextureImporterType.Default;
                importer.sRGBTexture = false;
            }
            else if (lowerCaseName.Contains("albedo"))
            {
                importer.textureType = TextureImporterType.Default;
                importer.sRGBTexture = true;
            }
            else
            {
                Debug.LogWarning("Texture name does not contain recognized keywords (albedo, maskmap, detailmap, _normal): " + texture.name);
            }

            // Process texture size
            maxSize = NextPowerOfTwo(maxSize);
            importer.maxTextureSize = maxSize;

            // Apply changes
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
        }
    }

    private static int NextPowerOfTwo(int v)
    {
        v--;
        v |= v >> 1;
        v |= v >> 2;
        v |= v >> 4;
        v |= v >> 8;
        v |= v >> 16;
        v++;
        return v;
    }
}

テクスチャの設定を調整するための静的メソッドにMenuItem属性を付けて、メインメニューから実行できるようにしています。

using System.Linq;
using UnityEditor;
using UnityEngine;

public class TextureSettingsEditor : ScriptableObject
{
    [MenuItem("Tools/Adjust Texture Settings")]
    public static void AdjustTextureSettings()
    {

選択されたオブジェクトはSelection.GetFilteredメソッドで、タイプやモードでフィルターして取得できます。テクスチャのリストを取得して、要素がなければ終了します。

        var textures = Selection.GetFiltered(typeof(Texture2D), SelectionMode.DeepAssets).Cast<Texture2D>().ToList();

        if (textures.Count == 0)
        {
            Debug.LogError("No texture selected. Please select texture(s) before running the script.");
            return;
        }

テクスチャのインポート設定を変更するには、TextureImporterを使います。AssetImporter.GetAtPathメソッドに各テクスチャのパスを入れてAssetImporterを取得し、TextureImporterにキャストします。

            string path = AssetDatabase.GetAssetPath(texture);
            TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;

            if (importer == null)
            {
                Debug.LogError("Failed to get TextureImporter for texture: " + texture.name);
                continue;
            }

TextureImporterがなければ次のテクスチャへスキップします。

テクスチャサイズの取得

テクスチャサイズは Texture.width と Texture.height ですが、これはインポート設定のMax Sizeで制限されます。テクスチャサイズが2048pxでも、インポート設定のMax Sizeが64pxなら、Texture.width/height の値は64になります。

テクスチャサイズ
インポート設定
Texture.width/height
Debug.Log(" Size: " + texture.width + "x" + texture.height);

なので、まずインポート設定のMax Sizeを最大にして、その後にテクスチャサイズを取得しています。Max Sizeを変更するには、TextureImporter.maxTextureSizeに値を代入します。その後、一度AssetDatabase.ImportAssetでアセットをインポートしています。

            var originalMaxSize = importer.maxTextureSize;
            importer.maxTextureSize = 16384;
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
            int width = texture.width;
            int height = texture.height;
            int maxSize = Mathf.Max(width, height);

テクスチャサイズは縦横の大きい方を使います。

その後、テクスチャの名前でアルベド、ノーマルなどの種類を判別して処理を変えています。テクスチャ名は小文字にして使います。

            string lowerCaseName = texture.name.ToLowerInvariant();
            if (lowerCaseName.Contains("_normal"))
            {
                importer.textureType = TextureImporterType.NormalMap;
            }
            else if (lowerCaseName.Contains("maskmap") || lowerCaseName.Contains("detailmap"))
            {
                importer.textureType = TextureImporterType.Default;
                importer.sRGBTexture = false;
            }
            else if (lowerCaseName.Contains("albedo"))
            {
                importer.textureType = TextureImporterType.Default;
                importer.sRGBTexture = true;
            }
            else
            {
                Debug.LogWarning("Texture name does not contain recognized keywords (albedo, maskmap, detailmap, _normal): " + texture.name);
            }

ノーマルマップはテクスチャタイプを「Normal map」にします。マスクマップやディテールマップは、タイプを「Default」にして、sRGBのチェックを外します。アルベドは「Default」にして、sRGBのチェックを入れます。

最後に、テクスチャサイズ以上で2のべき乗となる最小の値を計算してMax Sizeに設定し、再度アセットをインポートします。

            maxSize = NextPowerOfTwo(maxSize);
            importer.maxTextureSize = maxSize;

            // Apply changes
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
        }
    }

中途半端な値を設定すると、表示がおかしくなります。

importer.maxTextureSize = 2000; //maxSize;

Max Sizeに設定する2のべき乗は、ビット演算を使って計算します。


    private static int NextPowerOfTwo(int v)
    {
        v--;
        v |= v >> 1;
        v |= v >> 2;
        v |= v >> 4;
        v |= v >> 8;
        v |= v >> 16;
        v++;
        return v;
    }
}

この計算では、2進数における入力値の次の桁が1で他は0である数を求めます。そのために、まず入力と同じ桁ですべてのビットが1である数を作り、最後に1を足しています。(1001→1111→10000)

もちろんです。それでは具体的な数字を使ってビット操作の詳細を説明しましょう。ここでは、入力数値として 10 を使用します。10 を2進数で表すと 1010 です。

  1. まず、v から1を引きます。10 – 1 = 9。9 を2進数で表すと 1001 です。

  2. 次に、vを右に1ビットシフトします。これは v / 2 の整数部分と同等です。したがって、9(1001)を1ビット右にシフトすると 100(これは4を表します)になります。

  3. これを v 自身とビットOR演算します。1001 OR 100 の結果は 1101 となります。これは10進数で 13 を表します。

  4. 次に、この操作を2ビット、4ビット、8ビット、16ビットと繰り返します。以下はその結果です:

    • 13(1101)を2ビット右にシフト: 11(3)。1101 OR 11 = 1111(15)。
    • 15(1111)を4ビット右にシフト: 0。1111 OR 0 = 1111(15)。
    • 15(1111)を8ビット右にシフト: 0。1111 OR 0 = 1111(15)。
    • 15(1111)を16ビット右にシフト: 0。1111 OR 0 = 1111(15)。

  5. 最後に、vに1を足します。15 + 1 = 16。16 を2進数で表すと 10000 となります。
したがって、NextPowerOfTwo(10) の結果は 16 になります。これは 10 より大きく、かつ2のべき乗となる最小の数値です。

はじめに1を引くのは、入力値が2のべき乗の場合、そのまま返すためだそうです。

例えば、vが16である場合、1を引かないと以下のようになります:

  • 16(10000)を1ビット右にシフト: 1000(8)。10000 OR 1000 = 11000(24)。
  • 24(11000)を2ビット右にシフト: 110(6)。11000 OR 110 = 11110(30)。
  • 30(11110)を4ビット右にシフト: 1。11110 OR 1 = 11111(31)。
  • 31(11111)を8ビット右にシフト: 0。11111 OR 0 = 11111(31)。
  • 31(11111)を16ビット右にシフト: 0。11111 OR 0 = 11111(31)。
  • 最後に 31(11111)に1を足すと 100000(32)になります。

この場合、16(すでに2のべき乗)が入力されたときに32が返されます。

一方、vが16である場合に1を引くと以下のようになります:

  • 15(1111)を1ビット右にシフト: 111(7)。1111 OR 111 = 1111(15)。
  • 15(1111)を2ビット右にシフト: 11(3)。1111 OR 11 = 1111(15)。
  • 15(1111)を4ビット右にシフト: 0。1111 OR 0 = 1111(15)。
  • 15(1111)を8ビット右にシフト: 0。1111 OR 0 = 1111(15)。
  • 15(1111)を16ビット右にシフト: 0。1111 OR 0 = 1111(15)。
  • 最後に 15(1111)に1を足すと 10000(16)になります。

スクリプトを実行

3つのテクスチャ画像を用意しました。テクスチャタイプ等はすべて同じです。

テクスチャサイズは4096pxですが、インポート設定のMax Sizeは2048にしています。

これらのテクスチャを選択します。

メインメニューから上のメソッドを実行します。

すると、スクリプトの通りにインポート設定が変更されました。

ChatGPT

指示は以下の形式を試しました。

あなたは経験豊富なゲームプログラマーで、ゲーム制作作業の自動化ツールを作っています。

以下を実装したUnityのエディタ拡張スクリプトを作ってください。

# 箇条書きで具体的な処理を書く。

必ず答えの前に質問をしてください。
質問者が何を求めているのか、より明確にするためです。

ChatGPTがまず質問します。

あなたの要望を理解しましたが、私はいくつかの確認事項があります。

  1. すべてのテクスチャに対する設定変更は、Unity Editor上で直接、リアルタイムに行われることを期待していますか?それとも、これらの設定はスクリプトが実行される際に一時的に適用され、ゲームが終了するかリロードされると元に戻ることを期待していますか?

  2. Texture typeとTexsRGBの設定は、Unityの「Texture Import Settings」に関連していると理解していますが、これは正しいですか?

  3. テクスチャ名に複数のキーワードが含まれている場合、たとえば「albedo_maskmap」など、どの設定を優先すべきですか?

  4. 「Max Sizeを調べたサイズ以上で最小の値にします。」との指示ですが、これはつまりテクスチャの最大サイズを、そのテクスチャの縦または横の大きい方のピクセル数に設定する、という理解で良いですか?
以上、回答をお待ちしております。

テクスチャの名前に複数の接尾辞が含まれている場合を想定していませんでした。また、サイズに関する指示がわかりにくく、2のべき乗にするというのも抜けていました。

テクスチャのサイズを取得するところでは、インポート設定のMax Sizeの制限を受けない値を取るのは難しいと言われました。

あなたの質問に対する答えは、残念ながらUnityの通常のAPIでは直接取得することはできません。Unityではテクスチャの元の解像度にアクセスするための直接的な方法は提供されていません。これは、Unityがテクスチャをインポートするときに元の画像から直接生成されたアセットの解像度を使用するためです。

ただし、一部のワークアラウンドがあります。これらには、元のファイルからテクスチャを再読み込みして解像度を取得する方法や、テクスチャメタデータを解析する方法があります。しかし、これらの方法は比較的複雑であり、一部のパフォーマンスと互換性の問題があります。

以下に、元のファイルからテクスチャを再読み込みして解像度を取得する一例を示します。

Max Sizeを一度大きな値にすることで解決できました。

コメントを残す

メールアドレスが公開されることはありません。