SpriteAtlas作成時に圧縮設定を自動で適用する

SpriteAtlas作成時の圧縮設定を自動化します。
特にAndroidiOSなどのモバイル向け設定を変更します。

背景

Unityではアセット作成時やインポート時にAssetPostprocessorを利用することでアセットの設定を自動で変えることができます。
TextureにはTextureImporter、3DモデルにはModelImporterといったAssetImporterが存在しており、簡単にインポート設定を変更することができます。

しかしSpriteAtlasにはImporterが存在しないため、一旦SerializeObjectとして読み込み、直接値を書き換えることで設定を反映させます。
今回は各プラットフォームの圧縮設定を変更します。

導入

下準備で圧縮設定やPacking設定などを適用済みのPreset用SpriteAtlasを Assets/Editor/DefaultSpriteAtlas.spriteatlas に作成しておきます。
次に以下のコードをEditorフォルダ以下に作成すればSpriteAtlas作成時に自動で設定が適用されます。

using System.Linq;
using UnityEditor;
using UnityEngine.U2D;

/// <summary>
/// SpriteAtlasのプラットフォーム圧縮設定を自動適用する
/// </summary>
public class SpriteAtlasPostprocessor : AssetPostprocessor
{
    private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromPath)
    {
        // SpriteAtlasの変更のみ検知
        var list = importedAssets.Where(c => c.EndsWith(".spriteatlas")).ToArray();
        if (list.Length <= 0) return;

        // 設定をインポートするPreset用のSpriteAtlas
        var originAtlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>("Assets/Editor/DefaultSpriteAtlas.spriteatlas");
        var originSetting = new SerializedObject(originAtlas).FindProperty("m_EditorData").FindPropertyRelative("platformSettings");
        var originAndroid = originSetting.GetArrayElementAtIndex(0);
        var originiPhone = originSetting.GetArrayElementAtIndex(1);

        // 設定を適用
        foreach (var path in list)
        {
            var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(path);
            var serializedObject = new SerializedObject(atlas);
            var editorData = serializedObject.FindProperty("m_EditorData");

            // platform設定
            var platFormSettings = editorData.FindPropertyRelative("platformSettings");
            // Android、iOSのみ設定を変えるのでarraySizeは2
            platFormSettings.arraySize = 2;
            var targetAndroid = platFormSettings.GetArrayElementAtIndex(0);
            var targetiPhone = platFormSettings.GetArrayElementAtIndex(1);
            CopyProperty(targetAndroid, originAndroid);
            CopyProperty(targetiPhone, originiPhone);

            // ついでにpacking設定も適用
            var packingSetting = editorData.FindPropertyRelative("packingSettings");
            packingSetting.FindPropertyRelative("enableRotation").boolValue = false;
            packingSetting.FindPropertyRelative("enableTightPacking").boolValue = false;

            // 上記の変更を適用
            serializedObject.ApplyModifiedProperties();
        }
    }

    /// <summary>
    /// プロパティの書き換え
    /// フォーマットは以下のようになっている
    /// - serializedVersion: 2
    /// m_BuildTarget: Android
    /// m_MaxTextureSize: 2048
    /// m_ResizeAlgorithm: 0
    /// m_TextureFormat: 65
    /// m_TextureCompression: 1
    /// m_CompressionQuality: 50
    /// m_CrunchedCompression: 0
    /// m_AllowsAlphaSplitting: 0
    /// m_Overridden: 1
    /// m_AndroidETC2FallbackOverride: 0
    /// </summary>
    /// <param name="_target">適用先</param>
    /// <param name="_origin">適用元</param>
    private static void CopyProperty(SerializedProperty _target, SerializedProperty _origin)
    {
        _target.FindPropertyRelative("m_BuildTarget").stringValue = _origin.FindPropertyRelative("m_BuildTarget").stringValue;

        _target.FindPropertyRelative("m_MaxTextureSize").intValue = _origin.FindPropertyRelative("m_MaxTextureSize").intValue;

        _target.FindPropertyRelative("m_TextureFormat").intValue = _origin.FindPropertyRelative("m_TextureFormat").intValue;

        _target.FindPropertyRelative("m_TextureCompression").intValue = _origin.FindPropertyRelative("m_TextureCompression").intValue;

        _target.FindPropertyRelative("m_CompressionQuality").intValue = _origin.FindPropertyRelative("m_CompressionQuality").intValue;

        _target.FindPropertyRelative("m_CrunchedCompression").boolValue = _origin.FindPropertyRelative("m_CrunchedCompression").boolValue;

        _target.FindPropertyRelative("m_AllowsAlphaSplitting").boolValue = _origin.FindPropertyRelative("m_AllowsAlphaSplitting").boolValue;

        _target.FindPropertyRelative("m_Overridden").boolValue = _origin.FindPropertyRelative("m_Overridden").boolValue;

        _target.FindPropertyRelative("m_AndroidETC2FallbackOverride").intValue = _origin.FindPropertyRelative("m_AndroidETC2FallbackOverride").intValue;
    }
}

解説

AssetPostprocessorとOnPostprocessAllAssets

アセットの変更を検知します。
OnPostprocessAllAssetsには特定のファイル形式のみを検知する方法はないため、下記でspriteatlasのみをフィルタリングします。

var list = importedAssets.Where(c => c.EndsWith(".spriteatlas")).ToArray();

importedAssetsのpathには拡張子も含まれます。

SpriteAtlasのシリアライズ形式

デフォルトの.spriteAtlasファイルは以下のようになっています。

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!687078895 &4343727234628468602
SpriteAtlas:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_Name: New Sprite Atlas
  m_EditorData:
    serializedVersion: 2
    textureSettings:
      serializedVersion: 2
      anisoLevel: 1
      compressionQuality: 50
      maxTextureSize: 2048
      textureCompression: 0
      filterMode: 1
      generateMipMaps: 0
      readable: 0
      crunchedCompression: 0
      sRGB: 1
    platformSettings: []
    packingSettings:
      serializedVersion: 2
      padding: 4
      blockOffset: 1
      allowAlphaSplitting: 0
      enableRotation: 1
      enableTightPacking: 1
    variantMultiplier: 1
    packables: []
    totalSpriteSurfaceArea: 0
    bindAsDefault: 1
  m_MasterAtlas: {fileID: 0}
  m_PackedSprites: []
  m_PackedSpriteNamesToIndex: []
  m_Tag: New Sprite Atlas
  m_IsVariant: 0

プラットフォームの圧縮設定をoverrideし圧縮設定を変更してみるとplatformSettingsが以下になります。
圧縮設定はAndroidで RGBA Crunched ETC2 、iOSで RGBA Compressed ASTC 6×6 Block です。

platformSettings:
    - serializedVersion: 2
      m_BuildTarget: Android
      m_MaxTextureSize: 2048
      m_ResizeAlgorithm: 0
      m_TextureFormat: 65
      m_TextureCompression: 1
      m_CompressionQuality: 50
      m_CrunchedCompression: 0
      m_AllowsAlphaSplitting: 0
      m_Overridden: 1
      m_AndroidETC2FallbackOverride: 0
    - serializedVersion: 2
      m_BuildTarget: iPhone
      m_MaxTextureSize: 2048
      m_ResizeAlgorithm: 0
      m_TextureFormat: 50
      m_TextureCompression: 1
      m_CompressionQuality: 50
      m_CrunchedCompression: 0
      m_AllowsAlphaSplitting: 0
      m_Overridden: 1
      m_AndroidETC2FallbackOverride: 0

上記からm_EditorData->platformSettings->各値でたどって変更すれば良いことがわかります。

SerializeObjectからの値取得

元のSpriteAtlasの設定値を取得する流れです。
①SpriteAtlas形式のファイルをSerializedObjectに変換
new SerializedObject(originAtlas)
②FindPropertyで目的のプロパティを取得
.FindProperty("m_EditorData")
③FindPropertyRelativeでプロパティ内のプロパティを取得
.FindPropertyRelative("platformSettings")
④配列の場合はGetArrayElementAtIndex(番号)で取得
var originAndroid = originSetting.GetArrayElementAtIndex(0);

反映先も同様にして取得しますが、SpriteAtlasのPlatformSettingsは空だったのでサイズを拡張する必要があります。
platFormSettings.arraySize = 2

反映には各プロパティの実値を取得する必要があるため、stringValue/intValue/boolValueなどと型を指定します。
型はインスペクタでどのように見えているかを参考に。
(数値入力ならintValue、チェックボックスならboolValue)

最後に適用を保存すれば完了です。
serializedObject.ApplyModifiedProperties();

検証環境

  • Unity2018.3
  • Unity2019.1

参考

baba-s.hatenablog.com