Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8aca8ef
feat(serialize-references): add [SerializeReferenceSelector] dropdown…
VPDPersonal Jun 6, 2026
10581a0
fix(serialize-references): import UnityEngine for Rect in SerializeRe…
VPDPersonal Jun 6, 2026
0840d23
docs(serialize-references): add SerializeReferences sample
VPDPersonal Jun 6, 2026
05ce9e6
docs(serialize-references): add prefabs to SerializeReferences sample
VPDPersonal Jun 6, 2026
f463b7f
feat(serialize-references): support open generic type selection
VPDPersonal Jun 7, 2026
1237a56
docs(serialize-references): add generic Modifiers example to sample
VPDPersonal Jun 7, 2026
83baf4e
docs(serialize-references): document open generic type selection
VPDPersonal Jun 7, 2026
6653fc8
refactor(serialize-references): resolve generic arguments inline in t…
VPDPersonal Jun 7, 2026
5ffd30e
fix(serialize-references): hide compiler-generated types from the gen…
VPDPersonal Jun 7, 2026
a80db48
fix(serialize-references): align UIToolkit selector header layout
VPDPersonal Jun 8, 2026
2e1667f
feat(serialize-references): preserve data on type switch and add copy…
VPDPersonal Jun 8, 2026
675df91
feat(serialize-references): repair missing types and un-share aliased…
VPDPersonal Jun 8, 2026
c7a8b82
refactor(serialize-references): detect and repair missing types from …
VPDPersonal Jun 8, 2026
4b31066
feat(serialize-references): add Repair Missing References window
VPDPersonal Jun 8, 2026
5164103
feat(serialize-references): compact missing/shared notices and Prefab…
VPDPersonal Jun 8, 2026
6dd2f90
feat(serialize-references): resolve and repair missing types nested i…
VPDPersonal Jun 9, 2026
0ab5096
feat(serialize-references): restyle Repair window, embed type picker
VPDPersonal Jun 10, 2026
eeb48c2
docs(serialize-references): update Repair window menu path
VPDPersonal Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- **Features**
- [ProfilerMarker](#profilermarker)
- [Serializable Type System](#serializable-type-system)
- [SerializeReference Selector](#serializereference-selector)
- [Enum System](#enum-system)
- [ID System (Beta)](#id-system-beta)
- [SerializedProperty Extensions](#serializedproperty-extensions)
Expand Down Expand Up @@ -273,7 +274,8 @@ namespace Aspid.FastTools.Types.Editors
Type[] types = null,
string currentAqn = "",
TypeAllow allow = TypeAllow.None,
Action<string> onSelected = null);
Action<string> onSelected = null,
Func<Type, bool> filter = null);
}
}
```
Expand All @@ -285,6 +287,7 @@ namespace Aspid.FastTools.Types.Editors
| `currentAqn` | Assembly-qualified name of the currently selected type, used to pre-navigate to its location. Pass `null` or empty to start at the root. |
| `allow` | Which special type kinds (abstract classes, interfaces) are included in addition to concrete classes. Default: `TypeAllow.None`. |
| `onSelected` | Callback invoked with the assembly-qualified name of the selected type, or `null` if the user chose `<None>`. |
| `filter` | Optional predicate applied to each candidate type after the base-type and `allow` checks. Return `false` to hide a type. Pass `null` to keep every match. |

### ComponentTypeSelector

Expand Down Expand Up @@ -325,6 +328,63 @@ public sealed class TankEnemy : EnemyBase

---

## SerializeReference Selector

A drop-in dropdown for `[SerializeReference]` fields. Add `[SerializeReferenceSelector]` next to `[SerializeReference]` and the Inspector replaces the default managed-reference UI with the same searchable, hierarchical type picker used by `SerializableType` — letting you choose which concrete implementation of the field's declared type is instantiated.

- Lists every concrete, non-`UnityEngine.Object` class assignable to the field's declared interface / base type.
- Picking a type instantiates it; `<None>` clears the reference.
- The assigned instance's serialized fields are drawn inline under a foldout.
- A stored type that no longer resolves (renamed or deleted) is surfaced as a missing-type warning instead of silently clearing.
- Open generic implementations (e.g. `Modifier<T>`) are offered too: arguments are inferred from a closed-generic field, or picked in a follow-up window (validated against the field type) before instantiation.
- Switching the selected type preserves matching data — fields shared by the old and new implementation (by name and serialized shape) carry over instead of resetting to defaults.
- Right-click the header for a Copy / Paste context menu: it copies the managed-reference value and pastes it as an independent instance into any compatible field (paste is disabled when the clipboard type is not assignable to the target).
- A missing type can be repaired in place: the warning is a compact yellow notice whose underlined **Fix** word opens the type picker — choose the correct type and the reference is re-pointed while keeping its stored data; hover the notice for the full missing-type detail. Works for saved assets (ScriptableObjects and prefab assets) selected in the Project **and for objects open in Prefab Mode** — saved assets are rewritten in their YAML, while a Prefab Mode object is repaired on the live instance, recovering the data Unity still holds for the missing type. The repair also reaches nested references — through nested managed references and through plain `[Serializable]` containers (a struct/class field or a `List<T>` of them) — so a missing type buried in a slot or list element is fixed inline too.
- For missing references the Inspector cannot surface in the moment — components on child objects when the asset is not open in Prefab Mode, plus bulk repair and orphaned entries no field points at — the **Repair Missing References** window (`Tools → Aspid 🐍`) scans the whole asset file and lists every one with its own **Fix** picker, no Prefab Mode required.
- An aliased reference (two fields sharing one instance, e.g. after duplicating a list element) is flagged by the same compact notice, whose underlined **Make unique** word (also a right-click → **Make Unique Reference** action) splits it into an independent copy.
- Works on single fields, arrays, and `List<T>`, in both IMGUI and UIToolkit inspectors.

```csharp
using System;
using UnityEngine;
using System.Collections.Generic;
using Aspid.FastTools.SerializeReferences;

public interface IWeapon
{
void Fire();
}

[Serializable]
public sealed class Pistol : IWeapon
{
[SerializeField] [Min(0)] private int _damage = 10;

public void Fire() => Debug.Log($"Pistol: {_damage} dmg");
}

[Serializable]
public sealed class Railgun : IWeapon
{
[SerializeField] [Min(0)] private float _chargeTime = 1.5f;

public void Fire() => Debug.Log($"Railgun charged for {_chargeTime}s");
}

public sealed class Loadout : MonoBehaviour
{
[SerializeReference] [SerializeReferenceSelector]
private IWeapon _primary;

[SerializeReference] [SerializeReferenceSelector]
private List<IWeapon> _sidearms;
}
```

The attribute is editor-only (`[Conditional("UNITY_EDITOR")]`) and carries no runtime cost.

---

## Enum System

Provides serializable enum-to-value mappings configurable from the Inspector.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- **Features**
- [ProfilerMarker](#profilermarker)
- [Serializable Type System](#serializable-type-system)
- [SerializeReference Selector](#serializereference-selector)
- [Enum System](#enum-system)
- [ID System (Beta)](#id-system-beta)
- [SerializedProperty Extensions](#serializedproperty-extensions)
Expand Down Expand Up @@ -273,7 +274,8 @@ namespace Aspid.FastTools.Types.Editors
Type[] types = null,
string currentAqn = "",
TypeAllow allow = TypeAllow.None,
Action<string> onSelected = null);
Action<string> onSelected = null,
Func<Type, bool> filter = null);
}
}
```
Expand All @@ -285,6 +287,7 @@ namespace Aspid.FastTools.Types.Editors
| `currentAqn` | Assembly-qualified имя текущего выбранного типа: окно сразу откроется на его уровне иерархии. Передайте `null` или пустую строку, чтобы стартовать с корня. |
| `allow` | Какие специальные категории (абстрактные классы, интерфейсы) включаются в список в дополнение к конкретным классам. По умолчанию: `TypeAllow.None`. |
| `onSelected` | Callback с assembly-qualified именем выбранного типа или `null`, если пользователь выбрал `<None>`. |
| `filter` | Необязательный предикат, применяемый к каждому типу-кандидату после проверок базового типа и `allow`. Верните `false`, чтобы скрыть тип. Передайте `null`, чтобы оставить все совпадения. |

### ComponentTypeSelector

Expand Down Expand Up @@ -325,6 +328,63 @@ public sealed class TankEnemy : EnemyBase

---

## SerializeReference Selector

Готовый выпадающий список для полей с `[SerializeReference]`. Добавьте `[SerializeReferenceSelector]` рядом с `[SerializeReference]`, и Inspector заменит стандартный UI managed-ссылки тем же иерархическим выбором типа с поиском, что используется в `SerializableType` — позволяя выбрать, какая конкретная реализация объявленного типа поля будет создана.

- Показывает каждый конкретный, не наследующий `UnityEngine.Object` класс, совместимый с объявленным интерфейсом / базовым типом поля.
- Выбор типа создаёт его экземпляр; `<None>` очищает ссылку.
- Сериализуемые поля назначенного экземпляра рисуются вложенно под foldout.
- Сохранённый тип, который больше не разрешается (переименован или удалён), показывается как предупреждение о потерянном типе, а не очищается молча.
- Открытые generic-реализации (например, `Modifier<T>`) тоже предлагаются: аргументы выводятся из закрытого generic-поля либо выбираются в дополнительном окне (с проверкой на присваиваемость полю) перед созданием экземпляра.
- При смене типа совпадающие данные сохраняются — поля, общие у старой и новой реализации (по имени и сериализуемой форме), переносятся, а не сбрасываются в значения по умолчанию.
- По правому клику на заголовке доступно контекстное меню Copy / Paste: оно копирует значение managed-ссылки и вставляет его как независимый экземпляр в любое совместимое поле (вставка недоступна, если тип из буфера нельзя присвоить полю).
- Потерянный тип можно починить на месте: предупреждение показано компактным жёлтым лейблом, подчёркнутое слово **Fix** открывает селектор типов — выбранный тип переназначается с сохранением данных, а при наведении на лейбл показывается полная информация о потерянном типе. Работает для сохранённых ассетов (ScriptableObject и префаб-ассетов), выделенных в Project, **и для объектов, открытых в Prefab Mode**: сохранённые ассеты переписываются в YAML, а объект в Prefab Mode чинится прямо на живом экземпляре — с восстановлением данных, которые Unity всё ещё хранит для потерянного типа. Починка работает на любой глубине — через вложенные managed-ссылки и через обычные `[Serializable]`-контейнеры (поле-struct/class или `List<T>` из них), так что потерянный тип внутри слота или элемента списка чинится инлайном тоже.
- До потерянных ссылок, которые инспектор не показывает в моменте — компоненты дочерних объектов, когда ассет не открыт в Prefab Mode, плюс массовая починка и осиротевшие записи, на которые не указывает ни одно поле, — есть окно **Repair Missing References** (`Tools → Aspid 🐍`): оно сканирует весь файл ассета и выводит каждую со своим **Fix**, без Prefab Mode.
- Общая ссылка (два поля делят один экземпляр, например после дублирования элемента списка) помечается тем же компактным лейблом; подчёркнутое слово **Make unique** (а также действие правой кнопкой → **Make Unique Reference**) расщепляет её в независимую копию.
- Работает с одиночными полями, массивами и `List<T>`, в инспекторах IMGUI и UIToolkit.

```csharp
using System;
using UnityEngine;
using System.Collections.Generic;
using Aspid.FastTools.SerializeReferences;

public interface IWeapon
{
void Fire();
}

[Serializable]
public sealed class Pistol : IWeapon
{
[SerializeField] [Min(0)] private int _damage = 10;

public void Fire() => Debug.Log($"Pistol: {_damage} dmg");
}

[Serializable]
public sealed class Railgun : IWeapon
{
[SerializeField] [Min(0)] private float _chargeTime = 1.5f;

public void Fire() => Debug.Log($"Railgun charged for {_chargeTime}s");
}

public sealed class Loadout : MonoBehaviour
{
[SerializeReference] [SerializeReferenceSelector]
private IWeapon _primary;

[SerializeReference] [SerializeReferenceSelector]
private List<IWeapon> _sidearms;
}
```

Атрибут существует только в редакторе (`[Conditional("UNITY_EDITOR")]`) и не несёт стоимости в рантайме.

---

## Enum System

Предоставляет сериализуемые отображения enum → значение, настраиваемые через Inspector.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &6600000000000000001
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6600000000000000002}
- component: {fileID: 6600000000000000003}
m_Layer: 0
m_Name: IMGUILoadout
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6600000000000000002
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6600000000000000001}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6600000000000000003
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6600000000000000001}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e6cd8f6abf02422a92c5f183b53afa29, type: 3}
m_Name:
m_EditorClassIdentifier: Aspid.FastTools.Samples.SerializeReferences::Aspid.FastTools.Samples.SerializeReferences.IMGUILoadout
_primaryWeapon:
rid: 2001
_sidearms: []
_onHitEffect:
rid: 2002
references:
version: 2
RefIds:
- rid: 2001
type: {class: Pistol, ns: Aspid.FastTools.Samples.SerializeReferences, asm: Aspid.FastTools.Samples.SerializeReferences}
data:
_damage: 10
_magazineSize: 12
- rid: 2002
type: {class: BurnEffect, ns: Aspid.FastTools.Samples.SerializeReferences, asm: Aspid.FastTools.Samples.SerializeReferences}
data:
_duration: 3
_damagePerSecond: 5

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &6500000000000000001
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6500000000000000002}
- component: {fileID: 6500000000000000003}
m_Layer: 0
m_Name: Loadout
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6500000000000000002
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6500000000000000001}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6500000000000000003
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6500000000000000001}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 884d53b5154744d3af6948b1eef02505, type: 3}
m_Name:
m_EditorClassIdentifier: Aspid.FastTools.Samples.SerializeReferences::Aspid.FastTools.Samples.SerializeReferences.Loadout
_primaryWeapon:
rid: 1001
_sidearms:
- rid: 1002
- rid: 1003
_onHitEffect:
rid: 1004
references:
version: 2
RefIds:
- rid: 1001
type: {class: Railgun, ns: Aspid.FastTools.Samples.SerializeReferences, asm: Aspid.FastTools.Samples.SerializeReferences}
data:
_chargeTime: 2
_chargeEffect:
rid: 1005
- rid: 1002
type: {class: Pistol, ns: Aspid.FastTools.Samples.SerializeReferences, asm: Aspid.FastTools.Samples.SerializeReferences}
data:
_damage: 15
_magazineSize: 12
- rid: 1003
type: {class: Shotgun, ns: Aspid.FastTools.Samples.SerializeReferences, asm: Aspid.FastTools.Samples.SerializeReferences}
data:
_pellets: 8
_spreadAngle: 25
- rid: 1004
type: {class: FreezeEffect, ns: Aspid.FastTools.Samples.SerializeReferences, asm: Aspid.FastTools.Samples.SerializeReferences}
data:
_duration: 2.5
_slowPercent: 40
- rid: 1005
type: {class: BurnEffect, ns: Aspid.FastTools.Samples.SerializeReferences, asm: Aspid.FastTools.Samples.SerializeReferences}
data:
_duration: 3
_damagePerSecond: 5

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading