ボタンを押すと特定のパッケージをまとめてAddするためのエディタ拡張

皆大好き俺々ライブラリ。
新しいプロジェクトを作成した時に、当然俺々ライブラリを組み込む手順を踏んでいるかと思いますが、
特定のパッケージに依存していた場合にいちいち該当のパッケージをインポートするのもまぁ面倒臭い。

そんな時に便利なパッケージインポート用のエディタ拡張を作ってみました。

# パッケージをスクリプトからAddするには？
実は公式マニュアルに記載は有ります

https://docs.unity3d.com/ja/2023.2/Manual/upm-api.html

ただ、個人的に

* EditorApplication.update += Progress;　が好きじゃない
* Awaitableの勉強をしたい
* インポート済みのパッケージがある上でAddすると警告メッセージが出るのが嫌

という事で、試しにAwaitableで自分なりに良い感じになるように組み立ててみました

# コーディング

という事でコードです

```cs:PackageInstaller.cs
using System.Linq;
using UnityEditor.PackageManager;
using UnityEngine;

namespace ScreenPocket.Editor
{
    /// <summary>
    /// 指定されたPackageが既にImport済みかどうかをチェックして、未インポートならAddする
    /// </summary>
    public class CheckAndAddPackage
    {
        #region Fields
        
        /// <summary>
        /// パッケージ名
        /// </summary>
        private readonly string _packageName;
        
        #endregion
        
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="packageName"></param>
        public CheckAndAddPackage(string packageName)
        {
            _packageName = packageName;
        }

        /// <summary>
        /// 非同期で既存チェックして、未インポートなら追加する
        /// </summary>
        public async Awaitable AsyncProcess()
        {
            //リストアップ要求
            var listRequest = Client.List();
            while (!listRequest.IsCompleted)
            {
                await Awaitable.MainThreadAsync();
            }

            if (listRequest.Result.Any(s => s.name == _packageName))
            {
                Debug.LogWarning($"Already Imported Package! name:{_packageName}");
                //既にインポートされているなら何もせずに抜ける
                return;
            }
            
            //まだ含まれていないならAddする

            var addRequest = Client.Add(_packageName);
            while (!addRequest.IsCompleted)
            {
                await Awaitable.MainThreadAsync();
            }

            switch (addRequest.Status)
            {
                case StatusCode.Success:
                    Debug.Log("Installed: " + addRequest.Result.packageId);
                    break;
                case >= StatusCode.Failure:
                    Debug.LogWarning(addRequest.Error.message);
                    break;
            }
        }
    }
    
    /// <summary>
    /// パッケージをインストールする
    /// </summary>
    public class PackageInstaller
    {
        protected static async Awaitable ProcessAsync(string[] identifers )
        {
            // パッケージをプロジェクトに加える
            foreach (var identifier in identifers)
            {
                await new CheckAndAddPackage(identifier).AsyncProcess();
            }
        }
    }
}
```
やっている事としては
* Client.List()でインポート済み一覧を取得（待ち）
* 同名パッケージが無いかチェックして、あれば何もせず抜ける
* なければClient.Add()で追加して追加完了まで待つ

とまぁシンプルな内容です。
Awaitableで待つようにしたので、Unity側(EditorApplication.update)にイベント登録しなくても良くなったのがちょっと嬉しい。

使う際は
```cs:SettingsWindow.cs
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine;

namespace ScreenPocket.Editor
{
    public class DefaultPackageInstaller : PackageInstaller
    {
        private static readonly string[] PackageIdentifiers = 
        {
            "com.unity.addressables",
            "com.unity.burst",
            "com.unity.collections",
            "com.unity.inputsystem",
            "com.unity.nuget.newtonsoft-json",
            "com.unity.ads.ios-support",
            "com.unity.ide.rider",
            "com.unity.jobs"
        };

        public static async void ProcessAsync()
        {
            await ProcessAsync(PackageIdentifiers);
        }
    }

    /// <summary>
    /// ScreenPocketに関する設定を行うためのWindow
    /// </summary>
    public sealed class SettingsWindow : EditorWindow
    {
        [MenuItem("Tools/ScreenPocket/Open SettingWindow")]
        private static void Open()
        {
            var window = GetWindow<SettingsWindow>();
            window.titleContent = new GUIContent("Setting");
        }

        private void OnGUI()
        {
            var buttonRect = new Rect(25, 40, 256, 32);            
            if (GUI.Button(buttonRect,"Add Default Packages"))
            {
                DefaultPackageInstaller.ProcessAsync();
            }
        }
    }
```
と、こんな風にボタン押下パッケージ追加できるようにしておけば、
俺俺ライブラリの前準備としてパッケージを入れることが出来るようになるかと思います。

2段階でImportすれば、スムーズに新プロジェクトに自前環境を導入できるようになるのではないかな？と。

# 終わりに
本当は「ちゃんとPackageManagerに対応した上でdependencyの設定で依存関係を持たせた方がカッコ良いんだろうなぁ…」と思いつつ、そこまで対応するまでもない場合に役立つかと思います。
あと、連休最終日に突貫で作ったので、もし微妙な部分が有ったらいい感じにアレンジしてやってください。

使用する場合は自己責任で、ScreenPocketと書かれた部分を書き換えてご利用ください。