SdkSettingsSystem
Namespace: Vortex.Sdk.SdkSettingsSystem
Assembly: sdk.settings.system
Назначение
Единая точка управления Scripting Define Symbols для подключаемых SDK-пакетов Vortex. Собирает bool-тогглы из всех Sdk-пакетов в один ScriptableObject-ассет и синхронизирует их с PlayerSettings проекта.
Возможности:
- Один инспектор-ассет с тогглами включения/выключения каждого SDK-пакета
- Авто-синхронизация дефайнов при загрузке домена, смене целевой платформы и импорте самого ассета (подмена файла AssetSwap,
git checkout/pull) - Немедленная синхронизация из кода для скриптов сборки и CI —
SdkSettings.SyncDefinesNow()(см. Синхронизация из скрипта сборки) - Двунаправленный режим: «применить состояние тогглов в PlayerSettings» и «вычитать текущие defines обратно в тогглы»
- Расширение через
partial+asmref— новый пакет добавляет один файл, без правок в центре
Вне ответственности:
- Сами
defineConstraintsотдельных пакетов (живут в их asmdef) - Условная компиляция (это работа компилятора по результату sync)
- Очистка устаревших defines при удалении пакета
Зависимости
Vortex.Unity.CoreAssetsSystem—ICoreAssetVortex.Unity.EditorTools— атрибут[ToggleButton]- Sirenix Odin Inspector —
[Button],[HorizontalGroup] UnityEditor(Editor-only) —PlayerSettings,EditorUserBuildSettings,AssetDatabase
Архитектура
sdk.settings.system (asmdef)
├── SdkSettings.cs ← базовый partial, reflection-движок
├── Attribute/
│ └── DefineSymbolAttribute.cs
└── Editor/
├── MenuController.cs ← пункт меню Tools/Vortex/Configs/SDK Settings
└── SdkSettingsImportWatcher.cs ← AssetPostprocessor: ассет импортирован → синхронизация
<package>/DefineSettings/
├── SdkSettings.<Package>.cs ← partial-кусок с bool-полем
└── sdk.settings.system.ext.asmref → sdk.settings.system
asmref гарантирует, что partial-куски физически собираются в одну сборку с базовым SdkSettings. Без этого partial не работает (требование C#).
Компоненты
| Класс | Тип | Назначение |
|---|---|---|
SdkSettings |
partial ScriptableObject, ICoreAsset |
Главный ассет, partial-кусками собирает поля из всех Sdk-пакетов |
DefineSymbolAttribute |
Attribute |
Метка bool-поля: с каким define в PlayerSettings оно связано |
MenuController |
static, Editor-only | Пункт меню Tools/Vortex/Configs/SDK Settings для перехода к ассету |
SdkSettingsImportWatcher |
AssetPostprocessor, Editor-only |
Импорт действующего ассета → отложенная синхронизация |
Публичное Editor-API: SdkSettings.SyncDefinesNow() — немедленная синхронизация для скриптов сборки.
Как добавить новый Sdk-пакет
-
В корне нового пакета создать папку
DefineSettings/. -
Положить туда два файла:
SdkSettings.<PackageName>.cs:using UnityEngine; using Vortex.Sdk.SdkSettingsSystem.Attribute; using Vortex.Unity.EditorTools.Attributes; namespace Vortex.Sdk.SdkSettingsSystem { public partial class SdkSettings { [SerializeField, ToggleButton(isSingleButton: true)] [DefineSymbol("USING_VORTEX_MYPACKAGE")] private bool myPackageSdk = true; } }sdk.settings.system.ext.asmref:{ "reference": "GUID:56735331757685c47ba846af366ea373" }(GUID соответствует
sdk.settings.system.asmdef.) -
В asmdef нового пакета указать
defineConstraints: ["USING_VORTEX_MYPACKAGE"]— пакет начнёт компилироваться только при включённом тоггле.
После пересборки проекта в инспекторе SdkSettings появится новый тогглер.
Контракт
Вход
- bool-поля в
partial SdkSettings, помеченные[DefineSymbol("KEY")]
Выход
PlayerSettings.SetScriptingDefineSymbols— синхронизированный набор defines для текущейBuildTargetGroupSyncDefinesNow()→bool— выполнена ли синхронизация (совпадение без записи — тожеtrue)
Гарантии
- Каждое включённое поле → соответствующий define присутствует в PlayerSettings
- Каждое выключенное поле → соответствующий define отсутствует
- Запись в PlayerSettings выполняется только если итоговый набор отличается от текущего (идемпотентность)
- На дубликат
[DefineSymbol("X")]—LogErrorи пропуск второго
Ограничения
- Один ассет
SdkSettingsна проект внеEditor-папок (при множественных —LogErrorсо списком путей + abort). Ассеты вEditor-папках не учитываются: это заготовки (напр. варианты AssetSwap), в сборку не попадают и дефайнами не управляют - Все partial-куски обязаны лежать в asmref'ах на
sdk.settings.system - Editor-only: в рантайм-сборку движок не попадает
Жизненный цикл
| Триггер | Что происходит |
|---|---|
Первый запуск после создания ассета (через CoreAssetsController) |
OnPlatformChanged → ассет с _initialized = false → ReloadStates (тогглы выставляются по уже существующим defines), затем _initialized = true |
[InitializeOnLoadMethod] (загрузка домена) на «прогретом» ассете |
OnPlatformChanged → RefreshDefines |
EditorUserBuildSettings.activeBuildTargetChanged |
То же — RefreshDefines |
Импорт ассета SdkSettings вне Editor-папок (SdkSettingsImportWatcher) |
delayCall → OnPlatformChanged → RefreshDefines. Покрывает смену ассета без перезагрузки домена: применение варианта AssetSwap, git checkout/pull, внешняя правка. Перекомпиляция скриптов не срабатывает (импортируются только .cs); повтор безвреден — запись в PlayerSettings только при изменении набора дефайнов |
Кнопка ApplyChanges в инспекторе |
RefreshDefines — bool-поля → PlayerSettings |
Вызов SdkSettings.SyncDefinesNow() (скрипт сборки, CI) |
Сразу, без delayCall: RefreshDefines действующего ассета. Отказ (с ошибкой в лог) — во время сборки/импорта/компиляции, без ассета или при неинициализированном ассете |
Кнопка ReloadStates в инспекторе |
Чтение PlayerSettings → bool-поля + SetDirty + SaveAssets |
Зачем _initialized-флаг
При свеже-созданном ассете все bool-поля имеют дефолтные значения (обычно true). Без флага первый же RefreshDefines затёр бы существующий набор defines в PlayerSettings — например, если в проекте до подключения Vortex уже были собственные defines с пересекающимися именами или если пакет создан в проекте с уже проставленными defines из git. Поэтому первый прогон — ReloadStates (читаем то, что уже есть в PlayerSettings, в тогглы), и только потом начинается обычная синхронизация.
Флаг сериализуется в ассете (скрыт в инспекторе через [HideInInspector]).
Синхронизация из скрипта сборки
Автоматические точки синхронизации отложены (EditorApplication.delayCall): они ждут конца импорта/компиляции. Внутри одного -executeMethod отложенный вызов не успевает до BuildPipeline.BuildPlayer, а с -quit может не выполниться вовсе — сборка уйдёт со старыми дефайнами. Поэтому скрипт сборки, который меняет ассет SdkSettings (напр. применяет вариант AssetSwap) или просто хочет гарантии, синхронизирует явно:
public static void BuildNutaku()
{
if (!AssetSwapController.ApplyByComment("nutaku")) // подменили SdkSettings.asset (и не только)
EditorApplication.Exit(1);
if (!SdkSettings.SyncDefinesNow()) // дефайны → PlayerSettings прямо сейчас
EditorApplication.Exit(1);
BuildPipeline.BuildPlayer(...);
}
SyncDefinesNow():
| Результат | Когда |
|---|---|
true |
Дефайны записаны или уже совпадали с галочками |
false + ошибка в лог |
Идёт сборка (BuildPipeline.isBuildingPlayer), импорт или компиляция |
Действующего ассета нет или их несколько (вне Editor-папок) |
|
Ассет не инициализирован (_initialized = false): первый прогон прочитал бы текущие дефайны в ассет, а не наоборот — для сборки это неверное направление |
|
| Неизвестная платформа или ошибка записи PlayerSettings |
Особенности:
- Платформа. Пишется набор для
EditorUserBuildSettings.selectedBuildTargetGroup. В batchmode запускайте Unity с-buildTarget, совпадающим с целью сборки, иначе дефайны уйдут другой платформе. - Перекомпиляция не нужна. Плеерная сборка берёт дефайны из PlayerSettings в момент сборки. Редакторские сборки в этом процессе остаются скомпилированными со старыми дефайнами — это касается только редакторского кода под условием (build-хуки и т.п.).
- Повтор безвреден. Позже сработает и отложенная синхронизация от импорта ассета — она увидит совпадение и ничего не запишет.
Граничные случаи
| Ситуация | Поведение |
|---|---|
Ассет SdkSettings отсутствует в проекте |
Тихий no-op (OnPlatformChanged ничего не делает) |
Найдено более одного ассета вне Editor-папок |
LogError со списком путей и abort |
Копии ассета в Editor-папках (варианты AssetSwap) |
Поиском и наблюдателем импорта игнорируются. Кнопки инспектора у них работают: ApplyChanges на копии применит её тогглы к дефайнам проекта, ReloadStates перезапишет копию текущими дефайнами — копии правьте только тогглами |
Два partial-куска объявляют один и тот же [DefineSymbol("X")] |
LogError про дубликат, в карту попадает первый |
Поле помечено [DefineSymbol], но не bool |
Игнорируется |
BuildTargetGroup.Unknown |
Тихий выход |
Ошибка SaveAssets в ReloadStates |
Заглушено (try/catch{}) — известное упрощение |
| Удалён весь пакет (исчез partial-кусок) | Поле пропадает из инспектора, define в PlayerSettings остаётся (zombie-define) |
Меню
Tools → Vortex → Configs → SDK Settings — поиск ассета SdkSettings в проекте и переход к нему через MenuConfigSearchController.