유니티 ScriptedImporter 써보기 2부

2023. 7. 14. 01:09Unity, C# 프로그래밍

1부에서는 ScriptedImporter의 간단한 사용법을 알아봤는데, 

2부에서는 이미 Built-in 임포터로 정의되어있는 확장자들을 오버라이드 하는 법을 알아볼거다.

 

회사에서 현재 진행중인 프로젝트에서는 맵 파일을 bytes 확장자로 저장하고 있는데 해당 bytes 파일은 로드할 때 시간이 좀 걸리는 상황이였다. (로드할 때 TextAsset 으로 로드해서 여러 처리 과정이 있었다. GC도 좀 발생하고..)

 

이때 확장자를 바꿔서 등록하기에는 몇백개의 맵 파일이 있고, 우리 팀에서 관리하지 않는 툴에서 파일을 수정/저장하고 있었다.

물론 확장자를 바꾸려면 바꿀 수 있는데 그래도 오버라이드가 가능하기 때문에 파일을 많이 바꾸기 싫어서 시도해봤다.


ScriptedImporter 오버라이드 해보기

자 먼저 이미 정의된 확장자에 대해서 ScriptedImporter를 등록하려면 저번과 같은 방식을 사용하면 안된다.

1번 생성자
2번 생성자

해당 Attribute에는 생성자가 여러개 있는데 아래 2번 생성자의 overrideExts를 사용한다.

 

어제 예제를 살짝 수정해서

이렇게 overrideExts를 받는 생성자로 아래와 같이 임포터를 바꿔준다.

 

using System.IO;
using UnityEditor.AssetImporters;
using UnityEngine;

[ScriptedImporter(version: 1, exts: new string[0], overrideExts: new string[] { "bytes" })]
public sealed class MyAssetImporter : ScriptedImporter
{
    public override void OnImportAsset(AssetImportContext ctx)
    {
    	...
    }
}

 

그리고 잘 적용되는지 테스트하기 위해 저번에 사용했던

MyAsset.myAsset 파일도 MyAsset.bytes로 변경했다. (귀찮으니 내용은 그대로)

 

이렇게 임포터 코드를 수정하면 바로 적용되나..? 싶었지만

유니티는 이렇게 overrideExts를 사용해도 빌트인 임포터가 우선적으로 적용된다.

 

이제 에셋 임포터 에디터 UI 에서 선택할 수 있나? 싶었지만

회사에서 사용 중인 유니티 버전이 낮아서 그런지 임포터 변경을 에디터 UI 에서도 확인할 수 없었다

 

하지만 현재 집에 깔려있는 유니티 버전 기준으로는 에디터 UI 에서 선택 가능한 모습.

나중에 추가된 모양... 라이브 프로젝트는 버전업이 쉽지 않은데..

 

기본 적용도 되지 않고 임포터를 선택할 수 있는 UI도 제공하지 않았지만, overrideExts라는 기능을 추가하면서 유니티 측에서 아예 방법을 막아두었을 것 같지는 않아서 더 찾아보았다.

 

찾아보니 아래와 같은 API를 제공하고 있었다.

AssetDatabase.SetImporterOverride<T>(string path)

 

바로 간단한 에디터 메뉴를 추가해서 확인해 보자.

using UnityEditor;

public static class Menu
{
    [MenuItem("MyMenu/SetImporterOverride")]
    public static void SetImporterOverride()
    {
        AssetDatabase.SetImporterOverride<MyAssetImporter>("Assets/Resources/MyAsset.bytes");
    }
}

 

이렇게 작성하고 한번 컴파일 하면 유니티 상단에 MyMenu가 생길텐데 하위에 있는 SetImporterOverride를 누르면?

 

이렇게 MyAsset.bytes 파일이 이제 TextAsset 이 아닌 MyAsset 형식으로 불러와지게 된다.

 

적용 자체는 모두 마무리되었는데 회사에서 사용중인 맵 파일은 몇 백개 수준이고,

메뉴를 만들어서 관리한다면 사람이 직접 관리하는 것이기 때문에 메뉴를 누르는 것을 깜빡하거나 한다면 문제가 생길 수 있다.

 

그래서 자동으로 특정 경로에 있는 파일들은 해당 임포터로 오버라이드 할 수 있게 AssetPostprocessor 클래스를 사용해서 에셋을 가져올 때 설정하도록 수정했다.

 

using UnityEditor;

public sealed class MyAssetPostprocessor : AssetPostprocessor
{
    private void OnPreprocessAsset()
    {
        if (assetPath == "Assets/Resources/MyAsset.bytes")
        {
            if (assetImporter is not MyAssetImporter)
            {
                AssetDatabase.SetImporterOverride<MyAssetImporter>(assetPath);
            }
        }
    }
}

 

이렇게 만들게 되면 자동으로 MyAsset.bytes 파일은 MyAssetImporter 형식으로 오버라이드된다.

 

잘 작동하는 것을 확인하고

추후에 관리가 용이하도록 glob 패턴을 이용해서 살짝 수정해봤다.

 

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.AssetImporters;

public sealed class MyAssetPostprocessor : AssetPostprocessor
{
    private readonly static List<(string regexPattern, Action<string> handler)> _handlers = new();

    static MyAssetPostprocessor()
    {
        Register<MyAssetImporter>("Assets/Resources/MyAssets/*.bytes");
    }

    private void OnPreprocessAsset()
    {
        RefreshImporter(assetPath);
    }


    // glob 패턴으로 임포터 등록.
    private static void Register<T>(string glob) where T : ScriptedImporter
    {
        string regex = GlobSearchUtilities.GlobToRegex(glob);
        Action<string> handler = (assetPath) =>
        {
            if (AssetImporter.GetAtPath(assetPath) is T)
            {
                return;
            }

            AssetDatabase.SetImporterOverride<T>(assetPath);
        };

        _handlers.Add((regex, handler));
    }

    // 해당 path의 임포터 갱신.
    private static void RefreshImporter(string assetPath)
    {
        foreach (var (regexPattern, handler) in _handlers)
        {
            if (!Regex.IsMatch(assetPath, regexPattern))
            {
                continue;
            }

            handler.Invoke(assetPath);
            return;
        }
    }
}

위 코드는 Assets/Resources/MyAssets 폴더 하위의 모든 bytes 확장자 파일을 MyAssetImporter 로 오버라이드 해준다.

 

위에서 사용한 GlobSearchUtilities 클래스는 유니티 CsReference에서 가져와서 살짝 수정했다.

https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ProjectWindow/GlobSearchUtilities.cs

 

Regex를 바로 쓰기보다는 glob 패턴으로 만드는게

유니티 Search 창으로 미리보기 하면서 사용하기에 용이할 것 같아서 그렇게 했다.

 

glob 패턴은 유니티 AssetDatabase 검색창에서 결과를 미리 볼 수 있는 장점이 있다.

 

2부를 마지막으로 여기서 끝!

궁금하거나 이상하거나 빠진 내용이 있다면 댓글로 알려주세요 확인 가능하면 답변해드리겠습니다.

 


2024.01.03 추가.
위 예제랑은 살짝 다르지만 github에 샘플 프로젝트를 추가했습니다.

https://github.com/wisemin01/ScriptedImporterSampleProject

 

GitHub - wisemin01/ScriptedImporterSampleProject: This is a sample project for Unity’s Scripted Importer feature.

This is a sample project for Unity’s Scripted Importer feature. - GitHub - wisemin01/ScriptedImporterSampleProject: This is a sample project for Unity’s Scripted Importer feature.

github.com