После многих поисков я наконец смог собрать удивительно простое решение, которым я хотел бы поделиться с вами:
InitializeOnLoad
У Unity есть атрибут [InitializeOnLoad]
.Он говорит Unity инициализировать соответствующий класс, как только
- Unity запускается
- После любой повторной компиляции сценариев => также после импорта нового пакета Unity со сценариями
статический конструктор
В их Запуск кода редактора при запуске они показывают, как объединить это с конструктором static
.
С статические конструкторы :
Статический конструктор вызывается автоматически для инициализации класса перед созданием первого экземпляра или ссылками на любые статические члены.
Хотя обычно вам все равно придется создавать экземпляр класса, статический конструктор мгновенно «запускается / выполняется», когда класс инициализируется, что мы принудительно используем с помощью атрибута [InitializeOnLoad]
.
Определение сценариев символов
Дальнейшее Unity фактически имеет широких определений проекта в PlayerSettings
.
И хорошая часть: у нас также есть доступ к ним через API сценариев:
Итак, что я сделал сейчас, это следующее
Модуль A
Этот модуль не имеет зависимостей, а просто определяет "глобальный"определить "в PlayerSettings.Я поместил этот скрипт где-нибудь, например, в Assets/ModuleA/Editor
(важно имя последней папки).
using System.Linq;
using UnityEditor;
namespace ModuleA
{
// Will be initialized on load or recompiling
[InitializeOnLoad]
public static class Startup
{
// static constructor is called as soon as class is initialized
static Startup()
{
#region Add Compiler Define
// Get the current defines
// returns a string like "DEFINE_1;DEFINE_2;DEFINE_3"
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
// split into list just to check if my define is already there
var define = defines.Split(';').ToList();
if (!define.Contains("MODULE_A")
{
// if not there already add my define
defines += ";MODULE_A";
}
// and write back the new defines
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);
#endregion
}
}
}
Модуль B
Этот модуль зависит от Module A
.Таким образом, он сам определяет «глобальное определение» (поэтому более поздние модули могут проверять свои зависимости на Module B
), но дополнительно он сначала проверяет, импортирован ли модуль A.Если Module A
отсутствует, он выводит сообщение об ошибке на консоль отладки.
(Вы также можете выдать ошибку компилятора, используя #error SOME TEXT
, но по какой-то причине это не может правильно распечатать URL, поэтому я решил для Debug.LogError
)
Iпоместил этот скрипт где-нибудь, например, в Assets/ModuleB/Editor
#if MODULE_A
using System.Linq;
#endif
using UnityEditor;
#if !MODULE_A
using UnityEngine;
#endif
namespace ModuleB
{
// Will be initialized on load or recompiling
[InitializeOnLoad]
public static class Startup
{
// static constructor is called as soon as class is initialized
static Startup()
{
#if !MODULE_A
Debug.LogErrorFormat("! Missing Module Dependency !" +
"\nThe module {0} depends on the module {1}." +
"\n\nDownload it from {2} \n",
"MODULE_B",
"MODULE_A",
"https://Some.page.where./to.find.it/MyModules/ModuleA.unitypackage"
);
#else
// Add Compiler Define
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
var define = defines.Split(';').ToList();
if (!define.Contains("MODULE_B"))
{
defines += ";MODULE_B";
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);
#endif
}
}
}
Так что позже в других скриптах Модуль B У меня есть два варианта (оба делают в основном то же самое)
Я могу либо проверить везде #if MODULE_A
, чтобы точно проверить модуль, на который опирается этот скрипт
, либо вместо этого можно проверить #if MODULE_B
, чтобы скорее проверить с однимстрока, если все зависимости выполнены, так как в противном случае я не определяю MODULE_B
.
Таким образом, я могу полностью проверить все зависимости между определенными модулямиэто потрясающеЕдинственные два недостатка, которые я видел до сих пор:
- Мы должны знать как выглядит определение (например,
MODULE_A
) для каждого модуля и изменяется ли оно вв будущем его также необходимо изменить во всех зависимых модулях - «Глобальное определение» не удаляется в случае удаления модуля из проекта
Но хорошо - чторешение идеально?