Protect your data with declarative validation and read-only presentation.
Unity Helpers provides validation attributes that help maintain data integrity and prevent accidental modifications. These attributes work seamlessly with the Unity inspector and can validate fields at runtime.
usingUnityEngine;usingWallstopStudios.UnityHelpers.Core.Attributes;publicclassEntityReadOnly:MonoBehaviour{// Auto-generated unique identifier[WReadOnly]publicstringentityId=System.Guid.NewGuid().ToString();// Computed property exposed for debugging[WReadOnly][SerializeField]privatefloat_currentHealth;// Reference that should only be set via code[WReadOnly]publicTransformcachedTarget;// Frame counter for debugging[WReadOnly]publicintframesSinceLastUpdate;}
Why Use WReadOnly:
Prevent accidents: Stop designers from accidentally modifying auto-generated values
Debug visibility: Show internal state without allowing modification
Documentation: Make it clear which fields are managed by code vs. configured in the editor
Data integrity: Protect computed or cached values from manual overrides
Visual Reference
Multiple field types (string, float, Transform, int) displayed as read-only
Validates that a field is not null, providing both visual inspector feedback and runtime validation. When a field marked with [WNotNull] is null, the inspector displays a warning or error HelpBox. Additionally, calling CheckForNulls() on an object will throw an ArgumentNullException for any null [WNotNull] fields.
usingUnityEngine;usingWallstopStudios.UnityHelpers.Core.Attributes;publicclassPlayerControllerNotNull:MonoBehaviour{[WNotNull]publicRigidbody2Drb;[WNotNull]publicSpriteRendererspriteRenderer;[WNotNull][SerializeField]privateAudioSourceaudioSource;privatevoidAwake(){// Validates all [WNotNull] fields are assigned// Throws ArgumentNullException if any are null in Editor ONLYthis.CheckForNulls();}}
Behavior:
Inspector feedback: Displays a HelpBox warning (yellow) or error (red) when the field is null
Runtime validation: Call this.CheckForNulls() to validate all [WNotNull] fields
Throws ArgumentNullException with the field name if any marked field is null
Works with both Unity Object types and plain C# objects
All validation runs only in the Unity Editor (stripped in builds for performance)
Visual Reference
Fields marked with [WNotNull] display a HelpBox in the inspector when null
// Error message type with auto-generated message[WNotNull(WNotNullMessageType.Error)]publicAudioSourcecriticalAudioSource;// Inspector shows red error: "criticalAudioSource must be assigned"
// Warning with custom message[WNotNull("Player needs a target to attack")]publicTransformattackTarget;// Inspector shows yellow warning: "Player needs a target to attack"
// Error with custom message[WNotNull(WNotNullMessageType.Error, "Audio source is required for sound effects")]publicAudioSourceaudioSource;// Inspector shows red error: "Audio source is required for sound effects"
usingUnityEngine;usingWallstopStudios.UnityHelpers.Core.Attributes;publicclassEnemyAI:MonoBehaviour{// Yellow warning - nice to have[WNotNull]publicParticleSystemhitEffect;// Red error - critical reference[WNotNull(WNotNullMessageType.Error)]publicTransformpatrolPath;// Warning with helpful context[WNotNull("Assign the player tag or the enemy won't detect the player")]publicstringplayerTag;// Error with specific instructions[WNotNull(WNotNullMessageType.Error, "Drag the NavMeshAgent component here - required for movement")]publicUnityEngine.AI.NavMeshAgentagent;}
Visual Reference
Warning (yellow) and Error (red) HelpBoxes for null fields in the inspector
usingUnityEngine;usingWallstopStudios.UnityHelpers.Core.Attributes;publicclassEnemySpawner:MonoBehaviour{[WNotNull]publicGameObjectenemyPrefab;[WNotNull]publicTransformspawnPoint;[WNotNull(WNotNullMessageType.Error)]publicEnemyManagerenemyManager;privatevoidStart(){// If any [WNotNull] field is null, this throws with the field name// Example: ArgumentNullException("enemyPrefab")this.CheckForNulls();// Safe to use - we know these are assignedSpawnEnemy();}privatevoidSpawnEnemy(){GameObjectenemy=Instantiate(enemyPrefab,spawnPoint.position,Quaternion.identity);enemyManager.RegisterEnemy(enemy);}}
publicclassUIManager:MonoBehaviour{// Required reference with error severity - validated at runtime[WNotNull(WNotNullMessageType.Error)][SerializeField]privateCanvasmainCanvas;// Optional reference - not validated[SerializeField]privateAudioSourceclickSound;// Read-only and required[WReadOnly][WNotNull]publicRectTransformcachedRect;// Group with validation and custom message[WGroup("UI Elements")][WNotNull("Start button required for main menu")]publicButtonstartButton;// In group[WNotNull(WNotNullMessageType.Error, "Quit button required for main menu")][WGroupEnd("UI Elements")]// quitButton IS included, then closespublicButtonquitButton;// In group (last field)}
Why Use WNotNull:
Visual feedback: See missing references immediately in the inspector without running the game
Severity control: Use warnings for nice-to-have references, errors for critical ones
Custom messages: Provide helpful context about why a reference is needed
Early failure: Catch missing references at game start with CheckForNulls(), not when first used
Clear errors: Get the exact field name in the exception message
Documentation: Make required references explicit in code
Zero runtime cost: All validation stripped from builds
Validates that a field is properly assigned, providing visual inspector feedback when validation fails. Unlike [WNotNull] which only checks for null references, [ValidateAssignment] validates that fields are "properly assigned" based on their type—including checking for empty strings, empty collections, and null references.
usingSystem.Collections.Generic;usingUnityEngine;usingWallstopStudios.UnityHelpers.Core.Attributes;publicclassEnemySpawner:MonoBehaviour{[ValidateAssignment]publicGameObjectenemyPrefab;[ValidateAssignment]publicstringenemyName;[ValidateAssignment]publicList<Transform>spawnPoints;privatevoidStart(){// Logs warnings for any invalid [ValidateAssignment] fieldsthis.ValidateAssignments();}}
Behavior:
Inspector feedback: Displays a HelpBox warning (yellow) or error (red) when the field is invalid
Runtime validation: Call this.ValidateAssignments() to log warnings for invalid fields
Programmatic checking: Call this.AreAnyAssignmentsInvalid() to check if any fields are invalid
Works with Unity Object types, strings, collections, and any reference type
Inspector validation runs only in the Unity Editor (stripped in builds for performance)
Visual Reference
Fields marked with [ValidateAssignment] display a HelpBox in the inspector when invalid
// Error message type with auto-generated message[ValidateAssignment(ValidateAssignmentMessageType.Error)]publicAudioSourcecriticalAudioSource;// Inspector shows red error: "criticalAudioSource must be assigned"
// Warning with custom message[ValidateAssignment("Enemy name is required for UI display")]publicstringenemyName;// Inspector shows yellow warning: "Enemy name is required for UI display"
// Error with custom message[ValidateAssignment(ValidateAssignmentMessageType.Error, "Spawn points list cannot be empty")]publicList<Transform>spawnPoints;// Inspector shows red error: "Spawn points list cannot be empty"
usingUnityEngine;usingWallstopStudios.UnityHelpers.Core.Attributes;usingSystem.Collections.Generic;publicclassGameConfig:MonoBehaviour{// Yellow warning - validates not null[ValidateAssignment]publicGameObjectplayerPrefab;// Red error - validates string not empty/whitespace[ValidateAssignment(ValidateAssignmentMessageType.Error)]publicstringgameTitle;// Warning with helpful context - validates list not empty[ValidateAssignment("Add at least one difficulty level")]publicList<string>difficultyLevels;// Error with specific instructions - validates array not empty[ValidateAssignment(ValidateAssignmentMessageType.Error, "Spawn points are required - add Transform references")]publicTransform[]spawnPoints;}
Visual Reference
Warning (yellow) and Error (red) HelpBoxes for various invalid field types
usingUnityEngine;usingWallstopStudios.UnityHelpers.Core.Attributes;publicclassLevelManager:MonoBehaviour{[ValidateAssignment]publicGameObject[]enemyPrefabs;[ValidateAssignment]publicstringlevelName;privatevoidStart(){// Logs a warning for each invalid [ValidateAssignment] fieldthis.ValidateAssignments();}}
usingUnityEngine;usingWallstopStudios.UnityHelpers.Core.Attributes;publicclassSpawnManager:MonoBehaviour{[ValidateAssignment]publicGameObjectspawnPrefab;[ValidateAssignment]publicList<Transform>spawnLocations;privatevoidStart(){if(this.AreAnyAssignmentsInvalid()){Debug.LogError("SpawnManager has invalid assignments - spawning disabled");enabled=false;return;}// Safe to proceed - all assignments are validSpawnEnemies();}}
publicclassPlayerSetup:MonoBehaviour{// Use WNotNull for critical references - throws if null[WNotNull(WNotNullMessageType.Error)]publicRigidbody2Drb;// Use ValidateAssignment for string validation[ValidateAssignment]publicstringplayerName;// Use ValidateAssignment for collection validation[ValidateAssignment(ValidateAssignmentMessageType.Error, "Add at least one weapon")]publicList<GameObject>startingWeapons;privatevoidAwake(){// Throws ArgumentNullException if rb is nullthis.CheckForNulls();// Logs warnings if playerName is empty or startingWeapons is emptythis.ValidateAssignments();}}
usingUnityEngine;usingWallstopStudios.UnityHelpers.Core.Attributes;usingSystem.Collections.Generic;publicclassUIManager:MonoBehaviour{// Group with validation[WGroup("Required UI Elements")][ValidateAssignment(ValidateAssignmentMessageType.Error)]publicCanvasmainCanvas;// In group[ValidateAssignment("Button text cannot be empty")]publicstringstartButtonText;// In group (auto-included)[ValidateAssignment(ValidateAssignmentMessageType.Error, "Add menu items to display")][WGroupEnd("Required UI Elements")]// menuItems IS included, then closespublicList<GameObject>menuItems;// In group (last field)// Conditional validation[WShowIf(nameof(useCustomTheme))][ValidateAssignment("Custom theme name required when using custom theme")]publicstringcustomThemeName;publicbooluseCustomTheme;}
Why Use ValidateAssignment:
Comprehensive validation: Validates strings, collections, and references—not just null checks
Visual feedback: See invalid fields immediately in the inspector
Severity control: Use warnings for nice-to-have fields, errors for critical ones
Custom messages: Provide helpful context about validation requirements
Non-throwing validation: Use ValidateAssignments() for warnings instead of exceptions
Programmatic checking: Use AreAnyAssignmentsInvalid() for conditional logic
Zero runtime cost: All validation stripped from builds
// Use WNotNull for critical object references (throws on null)[WNotNull(WNotNullMessageType.Error)]publicRigidbody2Drb;// Use ValidateAssignment for strings (validates not empty/whitespace)[ValidateAssignment]publicstringplayerName;// Use ValidateAssignment for collections (validates not empty)[ValidateAssignment(ValidateAssignmentMessageType.Error)]publicList<Transform>waypoints;
// Required: Must be assigned in inspector[WNotNull]publicAudioClipattackSound;// Required: Must not be empty[ValidateAssignment]publicstringcharacterName;// Optional: May be nullpublicAudioCliphitSound;
[CreateAssetMenu]publicclassGameConfig:ScriptableObject{[WNotNull]publicGameObjectplayerPrefab;[WNotNull]publicMaterialdefaultMaterial;[ValidateAssignment("Game title cannot be empty")]publicstringgameTitle;[ValidateAssignment(ValidateAssignmentMessageType.Error)]publicList<string>supportedLanguages;privatevoidOnEnable(){this.CheckForNulls();this.ValidateAssignments();}}