|
| 1 | +# Scriptable Object Collection — Project Instructions |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +This is a **Unity Package** (`com.brunomikoski.scriptableobjectcollection`) that improves the usability of ScriptableObjects by grouping them into collections with code generation, custom inspectors, and GUID-based referencing. It targets **Unity 6000.0+** and is distributed via UPM (Unity Package Manager). |
| 6 | + |
| 7 | +## Language and Runtime |
| 8 | + |
| 9 | +- **C# 9** (Unity's default for Unity 6). Do not use C# 10+ features (e.g., `global using`, file-scoped namespaces, raw string literals). |
| 10 | +- Target **.NET Standard 2.1** APIs only. Do not use APIs exclusive to .NET 5/6/7+. |
| 11 | +- `unsafe` code is **not allowed** (`allowUnsafeCode: false` in assembly definitions). |
| 12 | +- Use `is` and `is not` pattern matching, target-typed `new()`, and null-coalescing where appropriate — these are available in C# 9. |
| 13 | + |
| 14 | +## Project Structure |
| 15 | + |
| 16 | +``` |
| 17 | +Scripts/ |
| 18 | + Runtime/ → Runtime assembly (BrunoMikoski.ScriptableObjectCollection) |
| 19 | + Editor/ → Editor assembly (BrunoMikoski.ScriptableObjectCollection.Editor) |
| 20 | + Browser/ → Editor Browser sub-assembly |
| 21 | + Core/ → Settings, code generation, dropdowns |
| 22 | + CustomEditors/ → Custom Inspector editors |
| 23 | + Processors/ → Asset post-processors |
| 24 | + PropertyDrawers/ → Custom property drawers |
| 25 | + Generators/ → Static code generators |
| 26 | +Samples~/ → UPM samples (ignored by Unity unless imported) |
| 27 | +Documentation~/ → UPM documentation (ignored by Unity at runtime) |
| 28 | +``` |
| 29 | + |
| 30 | +- **Runtime code must never reference `UnityEditor`** unless wrapped in `#if UNITY_EDITOR` / `#endif`. |
| 31 | +- Editor-only logic in runtime files must always be inside `#if UNITY_EDITOR` blocks. |
| 32 | +- Assembly definitions (`.asmdef`) define compilation boundaries — do not add cross-assembly references without updating them. |
| 33 | + |
| 34 | +## Code Style and Conventions |
| 35 | + |
| 36 | +### Naming |
| 37 | +- **Namespace**: `BrunoMikoski.ScriptableObjectCollections` (with `s`) for all types. Sub-namespaces: `.Picker`. |
| 38 | +- **Private fields**: `camelCase`, no underscore prefix (e.g., `private LongGuid guid;`). |
| 39 | +- **Properties**: `PascalCase` (e.g., `public LongGuid GUID => guid;`). Acronyms stay uppercase (`GUID`, `SOC`). |
| 40 | +- **Constants**: `UPPER_SNAKE_CASE` for private string constants in editors (e.g., `ITEMS_PROPERTY_NAME`), `PascalCase` for public constants. |
| 41 | +- **Classes**: One class per file. File name matches class name. |
| 42 | +- **Interfaces**: Prefixed with `I` (e.g., `ISOCItem`). |
| 43 | + |
| 44 | +### Formatting |
| 45 | +- 4-space indentation (no tabs). |
| 46 | +- Allman-style braces (opening brace on new line). |
| 47 | +- UTF-8 encoding **without BOM** for all `.cs` files. |
| 48 | +- Normalize line endings (handle both `\r\n` and `\n`). |
| 49 | + |
| 50 | +### Patterns |
| 51 | +- Prefer `for` loops over `foreach` in hot paths and runtime code to avoid enumerator allocations. |
| 52 | +- Use `[SerializeField]` with `private` fields; avoid public serialized fields. |
| 53 | +- Use `[HideInInspector]` for serialized fields that shouldn't appear in the default inspector. |
| 54 | +- Use `[FormerlySerializedAs("oldName")]` when renaming serialized fields to preserve data. |
| 55 | +- Avoid LINQ in runtime hot paths (allocations). LINQ is acceptable in editor code. |
| 56 | +- Cache lookups in dictionaries when collections are iterated frequently (see `itemGuidToScriptableObject`, `itemNameToScriptableObject` patterns). |
| 57 | +- Always validate caches: when returning a cached value, verify it's still valid (not destroyed, GUID still matches) before trusting it. Evict stale entries. |
| 58 | + |
| 59 | +### Unity-Specific |
| 60 | +- Use `ObjectUtility.SetDirty(obj)` instead of calling `EditorUtility.SetDirty` directly (it wraps the editor check). |
| 61 | +- Use `ScriptableObject.CreateInstance<T>()` to instantiate ScriptableObjects, never `new`. |
| 62 | +- Null-check Unity objects carefully: use `.IsNull()` extension or explicit `== null` (Unity overrides `==` for destroyed objects). Standard `is null` does **not** detect destroyed Unity objects. |
| 63 | +- Use `AssetDatabase` APIs only inside `#if UNITY_EDITOR` blocks or in Editor assemblies. |
| 64 | +- Use `TypeCache` in editor code for efficient type lookups instead of reflection-heavy alternatives. |
| 65 | + |
| 66 | +## GUID System |
| 67 | + |
| 68 | +- Items and collections use `LongGuid` (a 128-bit struct, two `long` values) — not Unity's `string` GUIDs. |
| 69 | +- GUID validation and uniqueness is enforced by `SOCItemGuidProcessor` (an asset postprocessor), not at item creation time. |
| 70 | +- When working with GUIDs: always check `guid.IsValid()` before using. Generate new GUIDs with `GenerateNewGUID()`. |
| 71 | + |
| 72 | +## Code Generation |
| 73 | + |
| 74 | +- Generated scripts use UTF-8 without BOM. |
| 75 | +- Line endings are normalized before writing. |
| 76 | +- Generated files use `partial class` and the `new` modifier where appropriate to avoid compiler warnings. |
| 77 | + |
| 78 | +## PR and Review Guidelines |
| 79 | + |
| 80 | +- Keep runtime and editor changes clearly separated. |
| 81 | +- Any new serialized field rename must include `[FormerlySerializedAs]` for backward compatibility. |
| 82 | +- New editor UI should use UXML/UIToolkit where the existing editors do, but IMGUI is acceptable for property drawers. |
| 83 | +- Avoid introducing GC allocations in runtime `Matches()` or lookup methods — reuse collections (see `HashSet` reuse pattern in `CollectionItemQuery`). |
| 84 | +- Conditional compilation (`#if UNITY_EDITOR`, `#if ADDRESSABLES_ENABLED`) must be used correctly; do not assume optional packages are present. |
0 commit comments