com.wallstop-studios.unity-helpers
Version:
Treasure chest of Unity developer tools
696 lines (522 loc) • 21.1 kB
Markdown
**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.
---
- [WReadOnly](
- [WNotNull](
- [ValidateAssignment](
- [Best Practices](
---
Displays a field in the inspector as read-only, preventing accidental modifications while keeping the value visible.
```csharp
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public class GameManagerReadOnly : MonoBehaviour
{
[]
public string sessionId = "abc-123-xyz";
[]
public float elapsedTime = 0f;
[]
[]
private int internalScore = 100;
}
```
**Behavior:**
- Field appears grayed out in the inspector
- Value is visible but cannot be edited through the inspector
- Useful for displaying computed values, debug info, or auto-generated IDs
- Works with any serializable field type
> **Visual Reference**
>
> 
>
> _Fields marked with [WReadOnly] appear grayed out and cannot be edited_
```csharp
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public class EntityReadOnly : MonoBehaviour
{
// Auto-generated unique identifier
[]
public string entityId = System.Guid.NewGuid().ToString();
// Computed property exposed for debugging
[]
[]
private float _currentHealth;
// Reference that should only be set via code
[]
public Transform cachedTarget;
// Frame counter for debugging
[]
public int framesSinceLastUpdate;
}
```
**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_
---
## WNotNull
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.
### Basic Usage
```csharp
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public class PlayerControllerNotNull : MonoBehaviour
{
[]
public Rigidbody2D rb;
[]
public SpriteRenderer spriteRenderer;
[]
[]
private AudioSource audioSource;
private void Awake()
{
// Validates all [WNotNull] fields are assigned
// Throws ArgumentNullException if any are null in Editor ONLY
this.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_
### WNotNullMessageType Enum
The `WNotNullMessageType` enum controls how null fields are displayed in the inspector:
| Value | Description |
| --------- | ------------------------------------------- |
| `Warning` | Displays a yellow warning HelpBox (default) |
| `Error` | Displays a red error HelpBox |
### Constructor Overloads
The `[WNotNull]` attribute supports multiple constructor overloads for flexibility:
#### Default Warning
```csharp
// Default: warning message type with auto-generated message
[WNotNull]
public GameObject target;
// Inspector shows: "target must be assigned"
```
```csharp
// Error message type with auto-generated message
[]
public AudioSource criticalAudioSource;
// Inspector shows red error: "criticalAudioSource must be assigned"
```
```csharp
// Warning with custom message
[]
public Transform attackTarget;
// Inspector shows yellow warning: "Player needs a target to attack"
```
```csharp
// Error with custom message
[]
public AudioSource audioSource;
// Inspector shows red error: "Audio source is required for sound effects"
```
```csharp
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public class EnemyAI : MonoBehaviour
{
// Yellow warning - nice to have
[]
public ParticleSystem hitEffect;
// Red error - critical reference
[]
public Transform patrolPath;
// Warning with helpful context
[]
public string playerTag;
// Error with specific instructions
[]
public UnityEngine.AI.NavMeshAgent agent;
}
```
> **Visual Reference**
>
> 
>
> _Warning (yellow) and Error (red) HelpBoxes for null fields in the inspector_
### Runtime Validation with CheckForNulls()
The `CheckForNulls()` extension method validates all `[WNotNull]` fields at runtime:
```csharp
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public class EnemySpawner : MonoBehaviour
{
[]
public GameObject enemyPrefab;
[]
public Transform spawnPoint;
[]
public EnemyManager enemyManager;
private void Start()
{
// If any [WNotNull] field is null, this throws with the field name
// Example: ArgumentNullException("enemyPrefab")
this.CheckForNulls();
// Safe to use - we know these are assigned
SpawnEnemy();
}
private void SpawnEnemy()
{
GameObject enemy = Instantiate(enemyPrefab, spawnPoint.position, Quaternion.identity);
enemyManager.RegisterEnemy(enemy);
}
}
```
Both the inspector HelpBox display and the `CheckForNulls()` extension method are only active in the Unity Editor:
```csharp
// The validation code only runs in UNITY_EDITOR
// In builds, CheckForNulls() does nothing (stripped for performance)
this.CheckForNulls();
```
This means:
- **Development**: Visual feedback in inspector + runtime null checking with detailed exception messages
- **Production**: Zero runtime cost (all validation code is stripped)
### Combining with Other Attributes
```csharp
public class UIManager : MonoBehaviour
{
// Required reference with error severity - validated at runtime
[]
[]
private Canvas mainCanvas;
// Optional reference - not validated
[]
private AudioSource clickSound;
// Read-only and required
[]
[]
public RectTransform cachedRect;
// Group with validation and custom message
[]
[]
public Button startButton; // In group
[]
[] // quitButton IS included, then closes
public Button quitButton; // 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.
| Field Type | Validation Rule |
| ------------------------- | ------------------------ |
| Unity `Object` references | Not null |
| Strings | Not null or whitespace |
| `IList` (arrays, List<T>) | Has at least one element |
| `ICollection` | Has at least one element |
| `IEnumerable` | Has at least one element |
| Other types | Not null |
```csharp
using System.Collections.Generic;
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public class EnemySpawner : MonoBehaviour
{
[]
public GameObject enemyPrefab;
[]
public string enemyName;
[]
public List<Transform> spawnPoints;
private void Start()
{
// Logs warnings for any invalid [ValidateAssignment] fields
this.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_
### ValidateAssignmentMessageType Enum
The `ValidateAssignmentMessageType` enum controls how invalid fields are displayed in the inspector:
| Value | Description |
| --------- | ------------------------------------------- |
| `Warning` | Displays a yellow warning HelpBox (default) |
| `Error` | Displays a red error HelpBox |
### Constructor Overloads
The `[ValidateAssignment]` attribute supports multiple constructor overloads for flexibility:
#### Default Warning
```csharp
// Default: warning message type with auto-generated message
[ValidateAssignment]
public GameObject target;
// Inspector shows: "target must be assigned"
```
```csharp
// Error message type with auto-generated message
[]
public AudioSource criticalAudioSource;
// Inspector shows red error: "criticalAudioSource must be assigned"
```
```csharp
// Warning with custom message
[]
public string enemyName;
// Inspector shows yellow warning: "Enemy name is required for UI display"
```
```csharp
// Error with custom message
[]
public List<Transform> spawnPoints;
// Inspector shows red error: "Spawn points list cannot be empty"
```
```csharp
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
using System.Collections.Generic;
public class GameConfig : MonoBehaviour
{
// Yellow warning - validates not null
[]
public GameObject playerPrefab;
// Red error - validates string not empty/whitespace
[]
public string gameTitle;
// Warning with helpful context - validates list not empty
[]
public List<string> difficultyLevels;
// Error with specific instructions - validates array not empty
[]
public Transform[] spawnPoints;
}
```
> **Visual Reference**
>
> 
>
> _Warning (yellow) and Error (red) HelpBoxes for various invalid field types_
### Runtime Validation Methods
Two extension methods are available for runtime validation of `[ValidateAssignment]` fields:
#### ValidateAssignments()
Logs warnings to the console for all invalid fields:
```csharp
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public class LevelManager : MonoBehaviour
{
[]
public GameObject[] enemyPrefabs;
[]
public string levelName;
private void Start()
{
// Logs a warning for each invalid [ValidateAssignment] field
this.ValidateAssignments();
}
}
```
Returns `true` if any `[ValidateAssignment]` field is invalid:
```csharp
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public class SpawnManager : MonoBehaviour
{
[]
public GameObject spawnPrefab;
[]
public List<Transform> spawnLocations;
private void Start()
{
if (this.AreAnyAssignmentsInvalid())
{
Debug.LogError("SpawnManager has invalid assignments - spawning disabled");
enabled = false;
return;
}
// Safe to proceed - all assignments are valid
SpawnEnemies();
}
}
```
Both attributes validate fields and provide inspector feedback, but they serve different purposes:
| Feature | ValidateAssignment | WNotNull |
| -------------------------- | ------------------------------ | ------------------------------ |
| **Null object check** | ✓ | ✓ |
| **Empty string check** | ✓ | ✗ |
| **Empty collection check** | ✓ | ✗ |
| **Runtime behavior** | Logs warnings | Throws `ArgumentNullException` |
| **Best for** | Comprehensive field validation | Strict null reference checking |
**When to use which:**
- Use `[ValidateAssignment]` when you need to validate that strings are non-empty or collections have elements
- Use `[WNotNull]` when you want strict null checking with an exception thrown at runtime
- Use `[ValidateAssignment]` when you want softer runtime validation (warnings instead of exceptions)
- Use `[WNotNull]` for critical references where the game should fail fast if not assigned
```csharp
public class PlayerSetup : MonoBehaviour
{
// Use WNotNull for critical references - throws if null
[]
public Rigidbody2D rb;
// Use ValidateAssignment for string validation
[]
public string playerName;
// Use ValidateAssignment for collection validation
[]
public List<GameObject> startingWeapons;
private void Awake()
{
// Throws ArgumentNullException if rb is null
this.CheckForNulls();
// Logs warnings if playerName is empty or startingWeapons is empty
this.ValidateAssignments();
}
}
```
```csharp
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
using System.Collections.Generic;
public class UIManager : MonoBehaviour
{
// Group with validation
[]
[]
public Canvas mainCanvas; // In group
[]
public string startButtonText; // In group (auto-included)
[]
[] // menuItems IS included, then closes
public List<GameObject> menuItems; // In group (last field)
// Conditional validation
[]
[]
public string customThemeName;
public bool useCustomTheme;
}
```
**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
---
Call `CheckForNulls()` and `ValidateAssignments()` in `Awake()` or `Start()` to catch missing references immediately:
```csharp
private void Awake()
{
// Throws for critical null references
this.CheckForNulls();
// Logs warnings for empty strings, collections, etc.
this.ValidateAssignments();
}
```
```csharp
// Use WNotNull for critical object references (throws on null)
[]
public Rigidbody2D rb;
// Use ValidateAssignment for strings (validates not empty/whitespace)
[]
public string playerName;
// Use ValidateAssignment for collections (validates not empty)
[]
public List<Transform> waypoints;
```
```csharp
[]
public float Speed => rb.velocity.magnitude;
```
```csharp
// Auto-wired but protected from manual changes
[]
[]
public Collider2D col;
```
```csharp
// Required: Must be assigned in inspector
[]
public AudioClip attackSound;
// Required: Must not be empty
[]
public string characterName;
// Optional: May be null
public AudioClip hitSound;
```
```csharp
[]
public class GameConfig : ScriptableObject
{
[]
public GameObject playerPrefab;
[]
public Material defaultMaterial;
[]
public string gameTitle;
[]
public List<string> supportedLanguages;
private void OnEnable()
{
this.CheckForNulls();
this.ValidateAssignments();
}
}
```
---
- **[Inspector Grouping Attributes](./inspector-grouping-attributes.md)** - Organize related fields
- **[Inspector Conditional Display](./inspector-conditional-display.md)** - Show/hide fields conditionally
- **[Relational Components](../relational-components/relational-components.md)** - Auto-wire component references