-
Notifications
You must be signed in to change notification settings - Fork 7
Guides Odin Migration Guide
A practical guide for migrating from Odin Inspector to Unity Helpers. Examples are verified against the actual source code.
| 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> |
Odin:
using Sirenix.OdinInspector;
using Sirenix.Serialization;
public class Example : SerializedMonoBehaviour
{
public Dictionary<string, int> scores;
}Unity Helpers:
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]orpublic - Initialize with
newto avoid null references (good practice, Unity will initialize this like it does List and arrays)
Odin:
using Sirenix.OdinInspector;
using Sirenix.Serialization;
public class Example : SerializedMonoBehaviour
{
public HashSet<string> unlockedItems;
}Unity Helpers:
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.DataStructure.Adapters;
public class Example : MonoBehaviour
{
[SerializeField]
private SerializableHashSet<string> unlockedItems = new SerializableHashSet<string>();
}Odin:
[Button("Regenerate")]
private void RegenerateLevel() { }
[Button, ButtonGroup("Actions")]
private void Save() { }Unity Helpers:
[WButton("Regenerate")]
private void RegenerateLevel() { }
[WButton(groupName: "Actions")]
private void Save() { }Additional options:
// 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
OdinEditorfor a specific type - See Inspector Buttons - Custom Editors for details
Odin:
public bool showAdvanced;
[ShowIf("showAdvanced")]
public float advancedSetting;Unity Helpers:
public bool showAdvanced;
[WShowIf(nameof(showAdvanced))]
public float advancedSetting;Odin:
[HideIf("isDisabled")]
public float value;Unity Helpers:
[WShowIf(nameof(isDisabled), inverse: true)]
public float value;Odin:
public AttackType attackType;
[ShowIf("attackType", AttackType.Ranged)]
public float range;Unity Helpers:
public AttackType attackType;
[WShowIf(nameof(attackType), AttackType.Ranged)]
public float range;Odin:
[ShowIf("@level >= 5")]
public Ability ultimateAbility;Unity Helpers:
[WShowIf(nameof(level), WShowIfComparison.GreaterThanOrEqual, 5)]
public Ability ultimateAbility;Available comparisons: Equal, NotEqual, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, IsNull, IsNotNull, IsNullOrEmpty, IsNotNullOrEmpty
Odin:
[EnumToggleButtons]
public Direction direction;
[EnumToggleButtons]
public MovementFlags flags; // [Flags] enumUnity Helpers:
[WEnumToggleButtons]
public Direction direction;
[WEnumToggleButtons(showSelectAll: true, showSelectNone: true)]
public MovementFlags flags; // [Flags] enumControl buttons per row:
[WEnumToggleButtons(buttonsPerRow: 4)]
public DamageType damageTypes;Odin:
[ValueDropdown("GetFrameRates")]
public int targetFrameRate;
private int[] GetFrameRates() => new[] { 30, 60, 120 };Unity Helpers:
// 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 };Odin:
[ValueDropdown("GetDifficulties")]
public string difficulty;Unity Helpers:
// Inline values
[StringInList("Easy", "Normal", "Hard")]
public string difficulty;
// Or with provider
[StringInList(nameof(GetDifficulties))]
public string difficulty;Unity Helpers:
// 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;Odin:
[BoxGroup("Movement")]
public float speed;
[BoxGroup("Movement")]
public float jumpHeight;
[FoldoutGroup("Advanced")]
public float acceleration;Unity Helpers:
// 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;Unity Helpers:
[WGroup("Character", displayName: "Character Settings")]
public string characterName;
[WGroup("Stats", parentGroup: "Character")]
public int health;
public int mana;Odin:
[InlineEditor]
public EnemyConfig config;
[InlineEditor(InlineEditorModes.GUIOnly)]
public ItemData item;Unity Helpers:
[WInLineEditor]
public EnemyConfig config;
[WInLineEditor(WInLineEditorMode.FoldoutExpanded, inspectorHeight: 200f)]
public ItemData item;Available modes: AlwaysExpanded, FoldoutExpanded, FoldoutCollapsed
Odin:
[Required]
public GameObject prefab;
[Required("Player reference is required!")]
public Transform player;Unity Helpers:
[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
}For validating that collections aren't empty:
[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();
}Odin:
[ReadOnly]
public string generatedId;Unity Helpers:
[WReadOnly]
public string generatedId;Before (Odin):
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):
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() { }
}// Attributes
using WallstopStudios.UnityHelpers.Core.Attributes;
// Serializable collections
using WallstopStudios.UnityHelpers.Core.DataStructure.Adapters;-
No special base class - Use standard
MonoBehaviour/ScriptableObject -
Use
nameof()- Unity Helpers usesnameof()for condition fields (type-safe) -
Initialize collections - Initialize
new SerializableDictionary<K,V>()etc. to avoid null references -
[HideIf] becomes inverse - Use
[WShowIf(..., inverse: true)]instead of[HideIf] -
Numeric conditions - Use
WShowIfComparisonenum instead of expression strings -
Groups auto-include -
[WGroup]can auto-include subsequent fields withautoIncludeCount
- Inspector Overview - Complete inspector features guide
- Serialization Types - All serializable types
- Inspector Buttons - WButton detailed guide
- Inspector Conditional Display - WShowIf detailed guide
- Inspector Selection Attributes - Dropdowns and toggles
📦 Unity Helpers | 📖 Documentation | 🐛 Issues | 📜 MIT License
- Inspector Button
- Inspector Conditional Display
- Inspector Grouping Attributes
- Inspector Inline Editor
- Inspector Overview
- Inspector Selection Attributes
- Inspector Settings
- Inspector Validation Attributes
- Utility Components
- Visual Components
- Data Structures
- Helper Utilities
- Math And Extensions
- Pooling Guide
- Random Generators
- Reflection Helpers
- Singletons