UNPKG

com.wallstop-studios.unity-helpers

Version:

Treasure chest of Unity developer tools

558 lines (389 loc) 11.9 kB
# Odin Inspector to Unity Helpers Migration Guide A practical guide for migrating from Odin Inspector to Unity Helpers. Examples are verified against the actual source code. --- ## Quick Reference Table | Odin Feature | Unity Helpers Equivalent | | ------------------------- | ----------------------------------------------------- | | `[Button]` | `[WButton]` | | `[ReadOnly]` | `[WReadOnly]` | | `[ShowIf]` / `[HideIf]` | `[WShowIf]` | | `[EnumToggleButtons]` | `[WEnumToggleButtons]` | | `[ValueDropdown]` | `[WValueDropDown]`, `[IntDropDown]`, `[StringInList]` | | `[BoxGroup]` | `[WGroup]` | | `[FoldoutGroup]` | `[WGroup(collapsible: true)]` | | `[InlineEditor]` | `[WInLineEditor]` | | `[Required]` | `[WNotNull]`, `[ValidateAssignment]` | | `SerializedMonoBehaviour` | Standard `MonoBehaviour` | | `SerializedDictionary` | `SerializableDictionary<K,V>` | | N/A (paid feature) | `SerializableHashSet<T>` | --- ## 1. Serializable Collections ### Dictionary **Odin:** ```csharp using Sirenix.OdinInspector; using Sirenix.Serialization; public class Example : SerializedMonoBehaviour { public Dictionary<string, int> scores; } ``` **Unity Helpers:** ```csharp using UnityEngine; using WallstopStudios.UnityHelpers.Core.DataStructure.Adapters; public class Example : MonoBehaviour { [SerializeField] private SerializableDictionary<string, int> scores = new SerializableDictionary<string, int>(); } ``` **Key differences:** - No special base class required (use standard `MonoBehaviour`) - Must use `[SerializeField]` or `public` - Initialize with `new` to avoid null references (good practice, Unity will initialize this like it does List<T> and arrays) ### HashSet **Odin:** ```csharp using Sirenix.OdinInspector; using Sirenix.Serialization; public class Example : SerializedMonoBehaviour { public HashSet<string> unlockedItems; } ``` **Unity Helpers:** ```csharp using UnityEngine; using WallstopStudios.UnityHelpers.Core.DataStructure.Adapters; public class Example : MonoBehaviour { [SerializeField] private SerializableHashSet<string> unlockedItems = new SerializableHashSet<string>(); } ``` --- ## 2. Inspector Buttons **Odin:** ```csharp [Button("Regenerate")] private void RegenerateLevel() { } [Button, ButtonGroup("Actions")] private void Save() { } ``` **Unity Helpers:** ```csharp [WButton("Regenerate")] private void RegenerateLevel() { } [WButton(groupName: "Actions")] private void Save() { } ``` **Additional options:** ```csharp // Control button order within a group [WButton(drawOrder: 1, groupName: "Debug")] private void PrintDebugInfo() { } // Control group placement (top or bottom of inspector) [WButton(groupName: "Authoring", groupPlacement: WButtonGroupPlacement.Top)] private void GenerateIds() { } ``` **Odin Inspector Support:** WButton works with Odin's `SerializedMonoBehaviour` and `SerializedScriptableObject` without additional setup. Use `[WButton]` on your methods. **Manual integration may be needed if:** - You create a custom `OdinEditor` for a specific type - See [Inspector Buttons - Custom Editors](../features/inspector/inspector-button.md#integration-with-custom-odin-editors) for details --- ## 3. Conditional Display ### Basic Boolean Condition **Odin:** ```csharp public bool showAdvanced; [ShowIf("showAdvanced")] public float advancedSetting; ``` **Unity Helpers:** ```csharp public bool showAdvanced; [WShowIf(nameof(showAdvanced))] public float advancedSetting; ``` ### Hide If (Inverse) **Odin:** ```csharp [HideIf("isDisabled")] public float value; ``` **Unity Helpers:** ```csharp [WShowIf(nameof(isDisabled), inverse: true)] public float value; ``` ### Enum Value Comparison **Odin:** ```csharp public AttackType attackType; [ShowIf("attackType", AttackType.Ranged)] public float range; ``` **Unity Helpers:** ```csharp public AttackType attackType; [WShowIf(nameof(attackType), AttackType.Ranged)] public float range; ``` ### Numeric Comparisons **Odin:** ```csharp [ShowIf("@level >= 5")] public Ability ultimateAbility; ``` **Unity Helpers:** ```csharp [WShowIf(nameof(level), WShowIfComparison.GreaterThanOrEqual, 5)] public Ability ultimateAbility; ``` **Available comparisons:** `Equal`, `NotEqual`, `GreaterThan`, `GreaterThanOrEqual`, `LessThan`, `LessThanOrEqual`, `IsNull`, `IsNotNull`, `IsNullOrEmpty`, `IsNotNullOrEmpty` --- ## 4. Enum Toggle Buttons **Odin:** ```csharp [EnumToggleButtons] public Direction direction; [EnumToggleButtons] public MovementFlags flags; // [Flags] enum ``` **Unity Helpers:** ```csharp [WEnumToggleButtons] public Direction direction; [WEnumToggleButtons(showSelectAll: true, showSelectNone: true)] public MovementFlags flags; // [Flags] enum ``` **Control buttons per row:** ```csharp [WEnumToggleButtons(buttonsPerRow: 4)] public DamageType damageTypes; ``` --- ## 5. Value Dropdowns ### Integer Dropdown **Odin:** ```csharp [ValueDropdown("GetFrameRates")] public int targetFrameRate; private int[] GetFrameRates() => new[] { 30, 60, 120 }; ``` **Unity Helpers:** ```csharp // Inline values (simplest) [IntDropDown(30, 60, 120)] public int targetFrameRate; // Or with provider method [IntDropDown(nameof(GetFrameRates))] public int targetFrameRate; private IEnumerable<int> GetFrameRates() => new[] { 30, 60, 120 }; ``` ### String Dropdown **Odin:** ```csharp [ValueDropdown("GetDifficulties")] public string difficulty; ``` **Unity Helpers:** ```csharp // Inline values [StringInList("Easy", "Normal", "Hard")] public string difficulty; // Or with provider [StringInList(nameof(GetDifficulties))] public string difficulty; ``` ### Generic Value Dropdown **Unity Helpers:** ```csharp // Static provider from another class [WValueDropDown(typeof(AudioManager), nameof(AudioManager.GetSoundNames))] public string soundEffect; // Instance provider (method on same class) [WValueDropDown(nameof(GetAvailableWeapons), typeof(WeaponData))] public WeaponData selectedWeapon; ``` --- ## 6. Grouping Fields **Odin:** ```csharp [BoxGroup("Movement")] public float speed; [BoxGroup("Movement")] public float jumpHeight; [FoldoutGroup("Advanced")] public float acceleration; ``` **Unity Helpers:** ```csharp // Auto-include next N fields [WGroup("Movement", autoIncludeCount: 2)] public float speed; // Field 1: in group public float jumpHeight; // Field 2: in group (auto-included, last by count) // Or explicit end marker [WGroup("Movement")] public float speed; // In group public float jumpHeight; // In group (auto-included) [WGroupEnd] // friction IS included, then group closes public float friction; // In group (last field) // Collapsible (foldout) [WGroup("Advanced", collapsible: true, startCollapsed: true)] public float acceleration; ``` ### Nested Groups **Unity Helpers:** ```csharp [WGroup("Character", displayName: "Character Settings")] public string characterName; [WGroup("Stats", parentGroup: "Character")] public int health; public int mana; ``` --- ## 7. Inline Editors **Odin:** ```csharp [InlineEditor] public EnemyConfig config; [InlineEditor(InlineEditorModes.GUIOnly)] public ItemData item; ``` **Unity Helpers:** ```csharp [WInLineEditor] public EnemyConfig config; [WInLineEditor(WInLineEditorMode.FoldoutExpanded, inspectorHeight: 200f)] public ItemData item; ``` **Available modes:** `AlwaysExpanded`, `FoldoutExpanded`, `FoldoutCollapsed` --- ## 8. Required/NotNull Validation **Odin:** ```csharp [Required] public GameObject prefab; [Required("Player reference is required!")] public Transform player; ``` **Unity Helpers:** ```csharp [WNotNull] public GameObject prefab; [WNotNull(WNotNullMessageType.Error, "Player reference is required!")] public Transform player; // Runtime validation in Awake/Start private void Awake() { this.CheckForNulls(); // Extension method } ``` ### Collection Validation For validating that collections aren't empty: ```csharp [ValidateAssignment] public List<Transform> spawnPoints; // Warns if null or empty [ValidateAssignment(ValidateAssignmentMessageType.Error, "Need at least one enemy type")] public List<EnemyData> enemyTypes; // Runtime check private void Start() { this.ValidateAssignments(); } ``` --- ## 9. Read-Only Fields **Odin:** ```csharp [ReadOnly] public string generatedId; ``` **Unity Helpers:** ```csharp [WReadOnly] public string generatedId; ``` --- ## 10. Complete Migration Example **Before (Odin):** ```csharp using Sirenix.OdinInspector; using Sirenix.Serialization; using UnityEngine; using System.Collections.Generic; public class EnemySpawner : SerializedMonoBehaviour { [BoxGroup("Settings")] [Required] public GameObject enemyPrefab; [BoxGroup("Settings")] [ShowIf("useWaves")] public int wavesCount = 3; public bool useWaves; [EnumToggleButtons] public SpawnPattern pattern; [ValueDropdown("GetSpawnRates")] public float spawnRate; public Dictionary<string, int> enemyWeights; [Button("Spawn Wave")] private void SpawnWave() { } private float[] GetSpawnRates() => new[] { 0.5f, 1f, 2f }; } ``` **After (Unity Helpers):** ```csharp using UnityEngine; using System.Collections.Generic; using WallstopStudios.UnityHelpers.Core.Attributes; using WallstopStudios.UnityHelpers.Core.DataStructure.Adapters; public class EnemySpawner : MonoBehaviour { [WGroup("Settings", autoIncludeCount: 2)] [WNotNull] [SerializeField] private GameObject enemyPrefab; [WShowIf(nameof(useWaves))] [SerializeField] private int wavesCount = 3; [SerializeField] private bool useWaves; [WEnumToggleButtons] [SerializeField] private SpawnPattern pattern; [WValueDropDown(0.5f, 1f, 2f)] [SerializeField] private float spawnRate; [SerializeField] private SerializableDictionary<string, int> enemyWeights = new SerializableDictionary<string, int>(); [WButton("Spawn Wave")] private void SpawnWave() { } } ``` --- ## Namespace Reference ```csharp // Attributes using WallstopStudios.UnityHelpers.Core.Attributes; // Serializable collections using WallstopStudios.UnityHelpers.Core.DataStructure.Adapters; ``` --- ## Key Differences Summary 1. **No special base class** - Use standard `MonoBehaviour` / `ScriptableObject` 2. **Use `nameof()`** - Unity Helpers uses `nameof()` for condition fields (type-safe) 3. **Initialize collections** - Initialize `new SerializableDictionary<K,V>()` etc. to avoid null references 4. **[HideIf] becomes inverse** - Use `[WShowIf(..., inverse: true)]` instead of `[HideIf]` 5. **Numeric conditions** - Use `WShowIfComparison` enum instead of expression strings 6. **Groups auto-include** - `[WGroup]` can auto-include subsequent fields with `autoIncludeCount` --- ## See Also - **[Inspector Overview](../features/inspector/inspector-overview.md)** - Complete inspector features guide - **[Serialization Types](../features/serialization/serialization-types.md)** - All serializable types - **[Inspector Buttons](../features/inspector/inspector-button.md)** - WButton detailed guide - **[Inspector Conditional Display](../features/inspector/inspector-conditional-display.md)** - WShowIf detailed guide - **[Inspector Selection Attributes](../features/inspector/inspector-selection-attributes.md)** - Dropdowns and toggles