しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Unity で音源ファイルのパスを列挙値から取得できるようにする

※ これは 2022/01/14 時点の Unity 2021.2.8f1 の情報です

最新版では動作が異なる可能性がありますのでご注意ください

前回は列挙型(enum)の記述を partial によって既定部分とアプリごとに追記する部分みたいな分散記述をできるようにした

この仕組みを使って今度は Unity の音源ファイルの場所を調べて列挙値コードを自動生成することで、列挙値で音源ファイルの配置パスを取得できるようにしたい

音源ファイルを読み取って列挙値コード生成

といっても下準備は前回でできているので Editor ディレクトリに次のようなスクリプトを用意するだけ

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security;
using System.Text.RegularExpressions;
using Cysharp.Threading.Tasks;
using UnityEditor;
using UnityEngine;

namespace MatatabiUx.Common.Editor
{
    public class GenerateTool
    {
        [MenuItem("Tools/Generate/Auido Sources")]
        public static void GenerateAudios()
        {
            GenerateAudiosInternalAsync().Forget();
        }

        private static readonly Regex HyphenNameRegex = new Regex("(?<=-)[a-zA-Z]", RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase);

        private async static UniTask GenerateAudiosInternalAsync()
        {
            try
            {
                var root = new DirectoryInfo(Path.Combine(Application.dataPath, "Resources/Audios"));
                var audios = new List<FileInfo>();
                var defined = new HashSet<string>(Enumeration.GetItems<Audios>().Select(x => x.Name));
                var directories = new string[] { root.FullName }
                    .Concat(Directory.GetDirectories(root.FullName, "*", SearchOption.AllDirectories))
                    .ToArray();
                var extensions = new HashSet<string> { ".wav", ".mp3", ".ogg" };
                for (var i = 0; i < directories.Length; i++)
                {
                    var directory = directories[i];
                    var info = new DirectoryInfo(directory);
                    audios.AddRange(info.GetFiles().Where(x => extensions.Contains(x.Extension)));
                }

                var file = new FileInfo(Path.Combine(Application.dataPath, "Scripts/Common/Enums/Audios.generated.cs"));
                using (var fileStream = new FileStream(file.FullName, FileMode.Create, FileAccess.Write))
                using (var stream = new StreamWriter(fileStream))
                {
                    await stream.WriteLineAsync(@"using System;");
                    await stream.WriteLineAsync(@"using System.Collections.Generic;");
                    await stream.WriteLineAsync(@"using System.Linq;");
                    await stream.WriteLineAsync();
                    await stream.WriteLineAsync(@"namespace MatatabiUx.Common");
                    await stream.WriteLineAsync(@"{");
                    await stream.WriteLineAsync(@"    public partial class Audios : Enumeration");
                    await stream.WriteLineAsync(@"    {");

                    var id = 1;
                    foreach (var audio in audios)
                    {
                        var filename = Path.GetFileNameWithoutExtension(audio.Name);
                        var name = filename[0].ToString().ToUpper()
                            + HyphenNameRegex
                            .Replace(filename.Substring(1), new MatchEvaluator((m) => m.Value.ToUpper()))
                            .Replace("-", "");

                        if (defined.Contains(name))
                            continue;

                        if (id > 1)
                        {
                            await stream.WriteLineAsync();
                        }
                        var path = audio.FullName.Replace(root.FullName, "Audios").Replace("\\", "/");

                        await stream.WriteLineAsync(@$"        /// <summary>");
                        await stream.WriteLineAsync(@$"        /// 音源ファイル名: {audio.Name}");
                        await stream.WriteLineAsync(@$"        /// </summary>");
                        await stream.WriteLineAsync(@$"        public static Audios {name} = new({id}, nameof({name}), ""{path}"");");
                        id++;
                    }

                    await stream.WriteLineAsync(@"    }");
                    await stream.WriteLineAsync(@"}");
                }

                AssetDatabase.ImportAsset("Assets/Scripts/Common/Enums/Audios.generated.cs");
            }
            catch (IOException) { }
            catch (UnauthorizedAccessException) { }
            catch (SecurityException) { }
        }
    }
}
#endif

Resources/Audios 配下にある .wav, .mp3, .ogg" ファイルを調べて、Enumeration の列挙値コードを出力しているだけ

UnityEditor の [Tools] - [Generate] - [Audio Sorces] メニューで実行

UnityEditor のメニューから実行

すると Assets/Scripts/Common/Enums/Audios.generated.cs というスクリプトファイルが生成されて中身はこんな感じになった

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

namespace MatatabiUx.Common
{
    public partial class Audios : Enumeration
    {
        /// <summary>
        /// 音源ファイル名: balloon.mp3
        /// </summary>
        public static Audios Balloon = new(1, nameof(Balloon), "Audios/balloon.mp3");

        /// <summary>
        /// 音源ファイル名: Bgm.ogg
        /// </summary>
        public static Audios Bgm = new(2, nameof(Bgm), "Audios/Bgm.ogg");

        /// <summary>
        /// 音源ファイル名: coin.mp3
        /// </summary>
        public static Audios Coin = new(3, nameof(Coin), "Audios/coin.mp3");

        /// <summary>
        /// 音源ファイル名: dart.mp3
        /// </summary>
        public static Audios Dart = new(4, nameof(Dart), "Audios/dart.mp3");

        /// <summary>
        /// 音源ファイル名: drinks-fall.mp3
        /// </summary>
        public static Audios DrinksFall = new(5, nameof(DrinksFall), "Audios/drinks-fall.mp3");
    }
}

Resources/Audios に音源ファイルが追加されても、メニュー実行だけでパスが参照できる列挙値が生成されるので便利