Amazonで商品を見るセール会場へ

Unity│PSDデータを階層保持でPrefab+PNGに一括出力する方法

PSDデータで作成したイラストなどの階層構造を維持したままPrefab化し、

レイヤーごとに個別PNG画像として一括出力する方法を解説します。

検証環境:Unity 6.1

目次

Unity 2DでPSDデータを階層保持のままPrefabとPNGへ一括出力する手順

Unity標準のインポート機能とエディタ拡張を組み合わせることで、手動での書き出しや配置の手間を省けます。

PSDファイルの準備

テスト用としてsample.psdを用意します。

レイヤーグループの入れ子や日本語のレイヤー名を含めた構成でも問題なく処理できます。

シェイプやスマートオブジェクトなどは、インポート時の崩れを防ぐため事前にPhotoshop側でラスタライズしておきます。

レイヤーグループの入れ子やラスタライズ済みの要素を含むテスト用のPSDデータの構成を示す画面。

Unityプロジェクトの準備とフォルダ構成

Unityプロジェクトを2Dテンプレートで作成します。

検証環境:Unity 6.1

Assets配下に以下の作業フォルダを作成します。

親フォルダ子フォルダ用途
AssetsEditorエディタ拡張スクリプト(C#)を格納
Prefabs生成されたPrefabおよびPNG画像の出力先
PSDインポート対象のPSDファイルを配置

作成したPSDフォルダへsample.psdを配置します。

PSDインポート設定の変更

配置したsample.psdを選択し、Inspectorウィンドウでインポーターを

UnityEditor.TextureImporter (Default)からUnityEditor.U2D.PSD.PSDImporterへ切り替えます。

インスペクターでインポーターをデフォルトからPSDImporterへ切り替える操作の画面。

Inspector内の各オプションで、以下の項目を有効にします。

非表示レイヤーの取り込みや階層構造の保持など、PSDImporterで有効にするオプション設定画面。

Layer Import設定

  • Include Hidden Layers:非表示レイヤーも含めて取り込む
  • Use Layer Group:レイヤーグループの階層構造を保持する

設定変更を自動化するスクリプトは後述します。

インポート設定をPreset(PSDImporter.preset)として保存し、Preset Managerのデフォルトに登録しておくと、PSD配置時の自動適用が可能です。

インポート設定をプリセットとして保存し、自動適用の設定を行う画面。

PrefabおよびPNG画像の一括出力手順

使用するスクリプトのコード全文は記事末尾に掲載しています。

Projectウィンドウで対象のsample.psdを選択します。

右クリックメニューから出力スクリプトを実行します。

プロジェクトウィンドウでPSDファイルを選択し、右クリックメニューから出力スクリプトを実行する様子。

Prefabsフォルダ内にPSD名と同名のフォルダが作成され、階層を再現したPrefabと各レイヤーのPNG画像が書き出されます。

生成されたPrefabは、PSDでの配置座標やレイヤーサイズを保持した状態で組み立てられます。

出力処理によってフォルダ内に生成された、階層を再現したPrefabと各レイヤーのPNG画像。

以上で一括出力の基本フローは完了です。

今回使用した2つのエディタ拡張スクリプトを紹介します。

  • PsdOverridePostprocessor.cs(PSDインポート時にImporterをPSDImporterへ自動切り替え)
  • PsdToPrefabExporter.cs(PSDの階層構造を保持したままPrefabとPNG画像を一括生成)

プロジェクトの仕様に合わせて処理内容を調整することで、様々なアセット生成ワークフローに応用できます。

Unity関連の関連記事は一覧まとめから確認できます。

uGUIへ変換する方法については以下の記事を参照してください。

PsdOverridePostprocessor.cs

Assets/Editorフォルダに以下のスクリプトを配置します。

プロジェクトにPSDファイルを追加した際、自動でインポーターがUnityEditor.U2D.PSD.PSDImporterへ適用されます。

// Assets/Editor/PsdOverridePostprocessor.cs
using UnityEditor;
using UnityEditor.U2D.PSD;
using System;

public class PsdOverridePostprocessor : AssetPostprocessor
{
    // PSD を Texture として読み込む前に呼ばれる
    void OnPreprocessTexture()
    {
        // assetPath は「Assets/.../サンプル.psd」のような文字列
        if (assetPath.EndsWith(".psd", StringComparison.OrdinalIgnoreCase))
        {
            // 既に PSDImporter なら何もしない
            if (AssetImporter.GetAtPath(assetPath) is PSDImporter) return;

            // .psd を PSDImporter に差し替え
            AssetDatabase.SetImporterOverride<PSDImporter>(assetPath);
        }
    }
}

PsdToPrefabExporter.cs

こちらも同様にAssets/Editorフォルダへ配置します。

ProjectウィンドウでPSDファイルを右クリックし、コンテキストメニューから実行コマンドを選択します。

配置したスクリプトを実行するため、PSDファイルの右クリックメニューからコマンドを選択する操作画面。
// Assets/Editor/PsdToPrefabExporter.cs
using UnityEditor;
using UnityEngine;
using System.IO;
using System.Linq;

public static class PsdToPrefabExporter
{
    /// <summary>
    /// 選択 PSD → Prefab+PNG 一括変換
    /// </summary>
    [MenuItem("Assets/BG/PSD → Prefab+PNG (階層保持)", false, 30)]
    private static void ConvertSelectedPsd()
    {
        foreach (var psd in Selection.objects)
        {
            string psdPath = AssetDatabase.GetAssetPath(psd);
            if (Path.GetExtension(psdPath).ToLower() != ".psd") continue;

            // ---------- ① 出力ディレクトリ ----------
            string prefabRoot = "Assets/Prefabs";
            string dir        = Path.Combine(prefabRoot, Path.GetFileNameWithoutExtension(psdPath));
            Directory.CreateDirectory(dir);

            // ---------- ② PSDImporter が生成した階層付き GO ----------
            var rootGo = AssetDatabase.LoadAllAssetsAtPath(psdPath)
                                       .OfType<GameObject>()
                                       .FirstOrDefault();

            if (rootGo == null)
            {
                Debug.LogWarning($"Sprite のルートが見つかりません : {psdPath}");
                continue;
            }

            // ---------- ③ 一時インスタンス化 ----------
            var instance = (GameObject)PrefabUtility.InstantiatePrefab(rootGo);

            // ---------- ④ PNG 書き出し&置換 ----------
            foreach (var sr in instance.GetComponentsInChildren<SpriteRenderer>(true))
            {
                if (sr.sprite == null) continue;
                var sprite = sr.sprite;

                // ④‑A: PNG 出力
                string pngPath = Path.Combine(dir, $"{sprite.name}.png");
                SaveSpriteAsPng(sprite, pngPath);

                // ④‑B: インポート設定を Sprite に
                SetPngAsSprite(pngPath);

                // ④‑C: SpriteRenderer が参照先を差し替え
                sr.sprite = AssetDatabase.LoadAssetAtPath<Sprite>(pngPath);
            }

            // ---------- ⑤ Prefab 保存 ----------
            string prefabPath = Path.Combine(dir, $"{rootGo.name}.prefab");
            PrefabUtility.SaveAsPrefabAsset(instance, prefabPath);

            // ---------- ⑥ 後片付け ----------
            Object.DestroyImmediate(instance);
            AssetDatabase.Refresh();

            Debug.Log($"{psd.name} 変換完了 → {prefabPath}");
        }
    }

    // .psd が選択されていないとメニューを非表示
    [MenuItem("Assets/BG/PSD → Prefab+PNG (階層保持)", true)]
    private static bool Validate() =>
        Selection.objects.Any(o => Path.GetExtension(AssetDatabase.GetAssetPath(o)).ToLower() == ".psd");

    // ---------- Utility ----------

    /// <summary>Sprite 部分だけ切り取って PNG 書き出し</summary>
    private static void SaveSpriteAsPng(Sprite sprite, string pngPath)
    {
        int w = (int)sprite.rect.width;
        int h = (int)sprite.rect.height;
        int x = (int)sprite.rect.x;
        int y = (int)sprite.rect.y;

        // 圧縮フォーマットでも確実に取れる RenderTexture → ReadPixels 方式
        var rt = RenderTexture.GetTemporary(sprite.texture.width,
                                            sprite.texture.height,
                                            0, RenderTextureFormat.ARGB32,
                                            RenderTextureReadWrite.Linear);

        Graphics.Blit(sprite.texture, rt);

        var prev = RenderTexture.active;
        RenderTexture.active = rt;

        var tex = new Texture2D(w, h, TextureFormat.RGBA32, false);
        tex.ReadPixels(new Rect(x, y, w, h), 0, 0);
        tex.Apply();

        RenderTexture.active = prev;
        RenderTexture.ReleaseTemporary(rt);

        File.WriteAllBytes(pngPath, ImageConversion.EncodeToPNG(tex));
    }

    /// <summary>PNG を Sprite として再インポート</summary>
    private static void SetPngAsSprite(string path)
    {
        AssetDatabase.ImportAsset(path);
        if (AssetImporter.GetAtPath(path) is TextureImporter ti)
        {
            ti.textureType          = TextureImporterType.Sprite;
            ti.spritePixelsPerUnit  = 100;
            ti.alphaIsTransparency  = true;
            ti.textureCompression   = TextureImporterCompression.Uncompressed;
            ti.isReadable           = false;
            ti.SaveAndReimport();
        }
    }
}

処理が完了すると、Prefabsフォルダ配下にPSD名のフォルダが生成され、Prefabとレイヤー別PNG画像が出力されます。

スクリプト完了後にPrefabsフォルダ内へPrefabとレイヤー別PNG画像が出力された状態を示す画面。

手順は以上となります。

この記事で紹介しているコードは、GitHubのMOTOKI-LLC/cg-method-codeにもまとめています。

2Dとスプライトの記事は、ほかにもあります。

次に学ぶ・作業環境を選ぶ

学習を続けたい方や、作業環境を整えたい方は、目的に合うガイドをご覧ください。

Unityのおすすめ書籍

開発PCの選び方

周辺機器の優先度

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次