4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

UnityAdvent Calendar 2023

Day 21

PNGなど既存AssetのImporterを上書きする SetImporterOverride

Last updated at Posted at 2023-12-20

前書き

この記事は、2023のUnityアドカレの12/21の記事です。
今年は、完走賞に挑戦してみたいと思います。Qiita君ぬい欲しい!

はじめに

Unityには、任意の拡張子のファイルに対し、ScriptedImporterクラスを実装することで、そのファイルをUnityのアセットとして対応させることができます。以前には、これを使って、Shaderアセットを非標準のShader言語から作り出す方法を紹介しました。

このScriptedImporterですが、Unity組み込みで対応している拡張子(PNGやmp3など)は定義することができません。

[ScriptedImporter(1, "png")]
class MyPngImporter : ScriptedImporter
{
    ...
}

image.png

しかし、これは本当に不可能なのでしょうか?(疑心暗鬼)

PSDのインポータ

Unityは昔からPSDのインポートをサポートしています。それは、単純に全レイヤーを結合して一枚のTexture2Dとして取り込むという機能です。

しかし、追加パッケージにもcom.unity.2d.psdimporterというものがあります。これは、レイヤー構造を認識してもっと賢く構造的にインポートをしてくれる代物です。

上記の理屈では、PSDはもう組み込みでサポートされてしまっているはずなので、独自のScriptedImporterで対応することができないはずです。

マニュアルを読んでみると…

デフォルトでは、.psd ファイルのインポートにはテクスチャインポーターを使用します。PSD Importer を使用して .psd ファイルをインポートする場合は、.psd ファイルを選択し、Importer ドロップダウンをクリックして、UnityEditor.U2D.PSD.PSDImporter を選択します。
image.png

どうやらやっぱり対応しているようです。そしてその下に、AssetDatabase.SetImporterOverride<TImporter>を使うことで、スクリプトからもインポータを切り替えることができると書いてあります。

このプロセスを自動化するスクリプトを作成することもできます。以下は、AssetDatabase.SetImporterOverride メソッドを使用して自動化スクリプトを作成する方法の例です。

[MenuItem("Assets/2D Importer/Change PSD File Importer", false, 30)]
static void ChangeImporter()
{
    ...// すべての.psdファイルをサーチ
    AssetDatabase.SetImporterOverride<PSDImporter>(path);
}

ということで、PSDImporterはなんだかpsd対応できています。PSDImporter.csを見るとその理由がわかります。

Editor/PSDImporter.cs#L25
[ScriptedImporter(23100002, new string[] {"psb"}, new[] {"psd"}, AllowCaching = true)]
public partial class PSDImporter : ScriptedImporter, ISpriteEditorDataProvider
{
    ...
}

パラメータ名が書いていないので少しわかりづらいですが、第2引数が対象拡張子(exts)、第3引数が「オーバライド対象の拡張子(overrideExts)」となっています。これはUnity2021で追加されたオーバーロードのようです。

ということで、PSDを直接対象とはせずoverrideExtsの方に入れることで、デフォルト適用はされないものの、インポータのインスペクタから切り替えられるようになるようです。

ちなみに、2019まではAutoSelectがこの代わりだったみたいです。

無理やりデフォルトにする

AssetPostProcessorを使うことで、アセットがインポートされる直前に差し替えてしまうことができます。そして、フィルターも自由にかけることができるので、拡張拡張子(??なんて呼ぶのでしょうか?)のようなものだけに対応することもできます。

class Processor : AssetPostprocessor
{
    void OnPreprocessAsset()
    {
        if(assetPath.EndsWith(".neg.png"))
        {
            var overrideImporter = AssetDatabase.GetImporterOverride(assetPath);
            if (overrideImporter == null)
            {
                AssetDatabase.SetImporterOverride<NegaPosiPngImporter>(assetPath);
            }
        }
    }
}

これで、*.neg.pngだけNegaPosiPngImporterでインポートすることができます。

[ScriptedImporter(1, new[] { "__png__" }, new [] { "png" })]
class NegaPosiPngImporter : ScriptedImporter
{
    public override void OnImportAsset(AssetImportContext ctx)
    {
        var png = new Texture2D(2, 2);
        png.LoadImage(System.IO.File.ReadAllBytes(assetPath));
        png.Apply();
        ctx.AddObjectToAsset("main obj", png);
        ctx.SetMainObject(png);

        var nega = new Texture2D(png.width, png.height){ name = $"nega {png.name}"};
        nega.SetPixels(png.GetPixels()
            .Select(c => new Color(1 - c.r, 1 - c.g, 1 - c.b, c.a))
            .ToArray());
        nega.Apply();
        ctx.AddObjectToAsset("nega obj", nega);
    }
}

image.png

ちなみに、このインポータを変更したという情報はMETAファイルにシリアライズされます。

image.png

C#ファイル(MonoScript)は無理

残念ながら、C#ファイルではこのインポータを切り替える機能は封印されていました。

[ScriptedImporter(1, new[] { "__cs__" }, new [] { "cs" })]
public class MonoScriptImporter2 : ScriptedImporter
{
    public override void OnImportAsset(AssetImportContext ctx)
    {
        // ↓ このメソッド(`CreateMonoScript`)も使えるのかよくわからない謎API
        // (どの道、ここまでたどり着かない)
        var cs = MonoScripts.CreateMonoScript(File.ReadAllText(assetPath), "", "", "", false);
        ctx.AddObjectToAsset("main obj", cs);
        ctx.SetMainObject(cs);

        ctx.AddObjectToAsset("txt obj", new TextAsset(File.ReadAllText(assetPath)));
    }
}

image.png

また、AssetPostProcessorによる方法についてですが、MonoScriptはOnPreprocessAssetで呼び出されません。代わりにOnPostprocessAllAssetsSetImporterOverrideしても、無視されてしまいます。

AssetDatabase.SetImporterOverride<NegaPosiPngImporter>(assetPath);
Debug.Log(AssetDatabase.GetImporterOverride(assetPath)); // null

まとめ

従来、Unityの組み込みで対応している拡張子にはScriptedImporterを作ることができませんでした。しかし、ScriptedImporterAttributeで、拡張子を直指定するのではなく、overrideExtsに指定することでインスペクタから選択可能になります。また、AssetPostProcessor.OnPreprocessAssetAssetDatabase.SetImporterOverrideすることで、自動選択させることができ、実質的に組み込み対応の拡張子のインポータを上書きすることができました。しかし、C#ファイルのMonoScriptAssetに関しては特別に不可能でした。

組み込みのインポータが気に入らない方は、ぜひ試してみてください!

プロジェクト

4
0
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
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?