Решили: самоинъекция дев-панелей идёт через DevSelfInject.WhenScopeReady<TScope> — ожидание
построенного контейнера покадрово, а не однократный scope?.Container.Inject(this).
Почему: прежняя строка проверяла на null объект скоупа, а падала на Container. Объект
LifetimeScope лежит в сцене с самого начала, контейнер он строит позже, и кто из них успеет к Start
дев-панели — решает порядок объектов в сцене. То есть NRE появлялся и исчезал от любой правки
сцены, никак не связанной с этими панелями: у нас он вылез после удаления пяти чужих объектов из
CoreScene.
Грабли:
scope?.Container.Inject(this)— ложное чувство защищённости.?.относится кscope, а не кContainer; выражение остаётся небезопасным ровно там, где ломается.- Симптом плавающий, и поэтому дорого ловится. Два одинаковых запуска дают разный результат; воспроизводить надо запуском play и чтением консоли, а не чтением кода — по коду это выглядит рабочим.
- Ждать событие построения нечем: публичного «контейнер готов» у
LifetimeScopeнет, поэтому ожидание кадрами (UniTask.NextFrame) с потолком в 120 кадров и предупреждением, если не дождались. - Панель, строящая UI из инъекции, обязана отложить и постройку.
DevEncounterPanelсобирал список боёв вStart— до инъекции его не из чего собирать; теперьBuildUi/Applyуходят в колбэк.
Владелец правды: Assets/_Project/Scripts/DevTools/DevSelfInject.cs; потребители —
DevEncounterPanel, CombatUnitDebugView.