ScriptingDefineSymbolsをエディタのメニューから切り替える

## `Scripting Define Symbols`

プリプロセッサはビルドに問題を抱えがちな`Unity`には欠かせないものですが、
`Scripting Define Symbols`はなんというか……もうちょっと、こう、やりようというものが……。

[【Unity】Scripting Define Symbolsを簡単に設定できるようにするエディタ拡張](https://baba-s.hatenablog.com/entry/2015/02/10/102941)
[Scripting Define Symbolsを劇的に使いやすくするエディタ拡張【Unity】【エディタ拡張】](https://kan-kikuchi.hatenablog.com/entry/ScriptingDefineSymbolsEditor)

みたいな話をしたいのですが、既に先人が`Scripting Define Symbols`とはなんぞや？　という説明からなにからエディタ拡張で操作するとことまで書いてくれてるからそっち読んどいてくれよな！

おしまい。

## `DefineSwitcher.cs`

なんか何も考えないで書いてたら記事が終わったんですが、そういうことではなくてわたしも同じようなものを作ってみましたという話です。

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146245/d81e7db1-2884-5fbc-7ac2-7996b624463d.gif)

1シンボルしかON/OFFできませんが、その分簡単にエディタメニューからポチポチ切り替えられるのがポイントです。
エディタの初回起動時と`Switch Platform`のタイミングで`Scripting Define Symbols`をチェックして、エディタメニューのON/OFFを切り替えています。
`gist`に載せたものは特定のプラットフォーム[^platform]に向けた記述になっているので、コメントに書いたとおり`Define`と`DefineMenuPath`を書き換えて使ってください。


[gist : DefineSwitcher.cs](https://gist.github.com/nekomimi-daimao/f3c9c769c6875183987682c308f8212d)

```c#:DefineSwitcher.cs
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Build;
using UnityEngine;

namespace Nekomimi.Daimao
{
    /// <summary>
    /// Switch #define.
    /// rewrite <see cref="Define"/> and <seealso cref="DefineMenuPath"/>
    /// </summary>
    public class DefineSwitcher : IActiveBuildTargetChanged
    {
        #region Define

        private const string Define = "UNITY_OVR";
        private const char Separator = ';';

        private static void SetDefine(bool on)
        {
            if (on == IsDefined())
            {
                return;
            }

            var current = new HashSet<string>(CurrentDefines());

            if (on)
            {
                current.Add(Define);
            }
            else
            {
                current.Remove(Define);
            }

            PlayerSettings.SetScriptingDefineSymbolsForGroup(
                EditorUserBuildSettings.selectedBuildTargetGroup, string.Join(Separator.ToString(), current)
            );
        }

        private static IEnumerable<string> CurrentDefines()
        {
            return PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup).Split(Separator);
        }

        private static bool IsDefined()
        {
            return CurrentDefines().Contains(Define);
        }

        #endregion


        #region EditorMenu

        private const string DefineMenuPath = "Oculus/" + Define;

        [MenuItem(DefineMenuPath, priority = -1)]
        private static void SwitchDefine()
        {
            var current = IsDefined();
            SetDefine(!current);
            SetCheckState(!current);
        }

        private static void SetCheckState(bool state)
        {
            var check = Menu.GetChecked(DefineMenuPath);
            if (state == check)
            {
                return;
            }

            Menu.SetChecked(DefineMenuPath, !check);
        }

        #endregion


        #region IActiveBuildTargetChanged

        public int callbackOrder { get; } = 0;

        public void OnActiveBuildTargetChanged(BuildTarget previousTarget, BuildTarget newTarget)
        {
            SetCheckState(IsDefined());
        }

        #endregion

        #region InitializeOnLoadMethod

        private class FirstLoad : ScriptableSingleton<FirstLoad>
        {
            [SerializeField]
            public bool AlreadyLoaded = false;
        }

        [InitializeOnLoadMethod]
        private static void CheckOnLaunch()
        {
            if (FirstLoad.instance.AlreadyLoaded)
            {
                return;
            }

            FirstLoad.instance.AlreadyLoaded = true;
            SetCheckState(IsDefined());
        }

        #endregion
    }
}

```


## まとめ
こういうプリプロセッサとかマクロとかってあんまり使わない派なのですが、今回はそこそこつらいことがあったので作りました。
`Unity`のエディタ拡張ってなんだかんだ`API`が用意されているので、作ろうと思えばなんでも作れますね。
公式のマニュアルが*……ふわっ……*としているので先人の記事を探し出さなければならないのが苦しいところですが、これからはエディタ拡張で解決できる問題がないか、気に留めてみようと思います。

おしまい。

## 参考
[【Unity】Scripting Define Symbolsを簡単に設定できるようにするエディタ拡張](https://baba-s.hatenablog.com/entry/2015/02/10/102941)
[Scripting Define Symbolsを劇的に使いやすくするエディタ拡張【Unity】【エディタ拡張】](https://kan-kikuchi.hatenablog.com/entry/ScriptingDefineSymbolsEditor)
[【Unity】UnityEditor の起動時にのみ処理を行う](https://hacchi-man.hatenablog.com/entry/2020/04/01/220000)[^wakeup]

[^platform]: 問答無用で大量に`Scripting Define Symbols`に叩き込んでくるような`SDK`も大概ですが、`Oculus`の`SDK`は！　インポートしたときに！　シンボル！　定義してくれても！　いいのでは！
[^wakeup]: いろいろ探しましたがこの方法がベストと思います