Scripting Define Symbols
プリプロセッサはビルドに問題を抱えがちなUnityには欠かせないものですが、
Scripting Define Symbolsはなんというか……もうちょっと、こう、やりようというものが……。
【Unity】Scripting Define Symbolsを簡単に設定できるようにするエディタ拡張
Scripting Define Symbolsを劇的に使いやすくするエディタ拡張【Unity】【エディタ拡張】
みたいな話をしたいのですが、既に先人がScripting Define Symbolsとはなんぞや? という説明からなにからエディタ拡張で操作するとことまで書いてくれてるからそっち読んどいてくれよな!
おしまい。
DefineSwitcher.cs
なんか何も考えないで書いてたら記事が終わったんですが、そういうことではなくてわたしも同じようなものを作ってみましたという話です。
1シンボルしかON/OFFできませんが、その分簡単にエディタメニューからポチポチ切り替えられるのがポイントです。
エディタの初回起動時とSwitch PlatformのタイミングでScripting Define Symbolsをチェックして、エディタメニューのON/OFFを切り替えています。
gistに載せたものは特定のプラットフォーム1に向けた記述になっているので、コメントに書いたとおりDefineとDefineMenuPathを書き換えて使ってください。
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を簡単に設定できるようにするエディタ拡張
Scripting Define Symbolsを劇的に使いやすくするエディタ拡張【Unity】【エディタ拡張】
【Unity】UnityEditor の起動時にのみ処理を行う2
