勉強不足で至らんブログ

勉強不足ですが、何か発信できればと思っています。

UnityのlayerをenumにしてくれるEditor拡張を作った

たまにしてる、自分のQiita記事転載です(ブログ更新と言いたい)

GameObjectのTag管理を楽にしたくて調べてたら、これをみつけました。

UnityEditorInternal.InternalEditorUtilityを使う - けいごのなんとか

Tagを扱いやすくするクラスの自動生成…なんと甘美な響き…!! 結構前の記事ではありますがUnity5.6.3とかでも動作してくれるので、すごく助かります。

…ここでTagだけでなくLayerも扱いやすくできれば…どうせならEnumに…と思いまして、上記の記事のクラス自動生成を見よう見まねでLayerからEnumスクリプトの自動生成を作ってみました。

using UnityEngine;
using UnityEditor;
using System.Linq;
using UnityEditorInternal;
using System.Collections.Generic;
using System;

[InitializeOnLoad]
public class LayerEnumCreator
{
  private const string LAYER_ENUM_HASH_KEY = "Layer_Enum_Hash";

    [MenuItem("Assets/LayerEnumCreator")]
    public static void _layerNameCreator ()
    {
        if (EditorApplication.isPlaying || Application.isPlaying)
            return;

        BuildlayerName ();
    }
    
    static LayerEnumCreator ()
    {
        if (EditorApplication.isPlaying || Application.isPlaying)
            return;

        BuildlayerName ();
    }

    static void BuildlayerName ()
    {
        System.Text.StringBuilder builder = new System.Text.StringBuilder ();

        builder = WriteTagManagerClass (builder);


        string text = builder.ToString ().Replace (",}", "}");
        string assetPath = currentFolderPath + "../LayerEnum.cs";
        
        if (AssetDatabase.LoadAssetAtPath (assetPath.Replace ("/Editor/..", ""), typeof(UnityEngine.Object)) != null && EditorPrefs.GetInt (LAYER_ENUM_HASH_KEY, 0) == text.GetHashCode ())
            return;

        System.IO.File.WriteAllText (assetPath, text);
        EditorPrefs.SetInt (LAYER_ENUM_HASH_KEY, text.GetHashCode ());
        AssetDatabase.Refresh (ImportAssetOptions.ImportRecursive);
    }

    static System.Text.StringBuilder WriteTagManagerClass (System.Text.StringBuilder builder)
    {
        List<string> layerNames = InternalEditorUtility.layers.ToList();

        builder.AppendLine ("public class LayerEnum");
        builder.AppendLine ("{");

        {
            WriteLayerEnum (builder, layerNames);
        }

        builder.AppendLine ("}");
        return builder;
    }
    
    static void WriteLayerEnum (System.Text.StringBuilder builder, List<string> layerNames)
    {
        builder.Append ("\t").AppendLine ("/// <summary>");
        builder.Append ("\t").AppendFormat ("/// Access Layer Number Enum").AppendLine ();
        builder.Append ("\t").AppendLine ("/// </summary>");
        builder.Append("\t").Append ("public enum Layer {").AppendLine ();
        layerNames.ForEach ((layerName, i) => {
            var comma = (i == layerNames.Count() - 1)? "" : ",";
            if(i >= 3) {
                i += 1;
                if (i >= 6) {
                    i += 2;
                }
            }
            builder.Append("\t").Append("\t").AppendFormat (@"{0} = {1}", layerName.SymbolReplace(), i + comma).AppendLine();
        });
        builder.Append("\t").AppendLine ("};");
    }


    static string currentFolderPath {
        get {
            string currentFilePath = new System.Diagnostics.StackTrace (true).GetFrame (0).GetFileName ();
            return "Assets" + currentFilePath.Substring (0, currentFilePath.LastIndexOf ("/") + 1).Replace (Application.dataPath, string.Empty);
        }
    }
}

UnityのEditorの方でLayerの3, 6, 7は入力ができず空白なのでenumに含めず、スキップする処理にしてますが…あまり気に入ってないです… あと、実はこれだけでは動かないです。クラス拡張も行なっているので…

using UnityEngine;
using System.Linq;
using System.Collections.Generic;
using System;

public static class ClassExtension
{
    public static string SymbolReplace(this string name)
    {
        string[] invalidChar = new string[] {" ", " ", "!", "\"", "#", "$", "%", "&", "\'", "(", ")", "-", "=", "^", "~", "¥", "|", "[", "{", "@", "`", "]", "}", ":", "*", ";", "+", "/", "?", ".", ">", ",", "<"};
        invalidChar.ToList ().ForEach (s => name = name.Replace (s, string.Empty));
        return name;
    }
    
    public static void ForEach<T> (this IEnumerable<T> collection, Action<T, int> action)
    {
        int i = 0;
        foreach (T t in collection) {
            action (t, i++);
        }
    }
}

この拡張を加えれば動くと思います。 ただ、注意点なのはLayerは8から入力できますが、8を入力せず9を入力すると番号がズレるため、その辺は ご自分でよしなにお願いします。 初めての自動生成スクリプト(見よう見まね)なのでツッコミ所があれば突っ込んでいただければと思います。

デフォルトの結果だとこうなります。

public class LayerEnum
{
    /// <summary>
    /// Access Layer Number Enum
    /// </summary>
    public enum Layer {
        Default = 0,
        TransparentFX = 1,
        IgnoreRaycast = 2,
        Water = 4,
        UI = 5
    };
}